summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/action_map_editor.cpp8
-rw-r--r--editor/connections_dialog.cpp215
-rw-r--r--editor/connections_dialog.h16
-rw-r--r--editor/editor_data.h1
-rw-r--r--editor/editor_help.cpp2
-rw-r--r--editor/editor_inspector.cpp2
-rw-r--r--editor/editor_node.cpp500
-rw-r--r--editor/editor_node.h35
-rw-r--r--editor/editor_properties.cpp4
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/event_listener_line_edit.cpp36
-rw-r--r--editor/export/editor_export.cpp11
-rw-r--r--editor/export/editor_export_platform.cpp49
-rw-r--r--editor/export/editor_export_platform.h2
-rw-r--r--editor/export/editor_export_plugin.cpp10
-rw-r--r--editor/export/editor_export_plugin.h18
-rw-r--r--editor/export/editor_export_preset.cpp96
-rw-r--r--editor/export/editor_export_preset.h21
-rw-r--r--editor/export/project_export.cpp132
-rw-r--r--editor/export/project_export.h10
-rw-r--r--editor/filesystem_dock.cpp33
-rw-r--r--editor/filesystem_dock.h2
-rw-r--r--editor/icons/BoneMapHumanBody.svg27
-rw-r--r--editor/icons/BoneMapHumanFace.svg2
-rw-r--r--editor/icons/BoneMapHumanLeftHand.svg2
-rw-r--r--editor/icons/BoneMapHumanRightHand.svg2
-rw-r--r--editor/icons/Keyboard.svg2
-rw-r--r--editor/icons/KeyboardError.svg1
-rw-r--r--editor/icons/KeyboardLabel.svg1
-rw-r--r--editor/icons/KeyboardPhysical.svg2
-rw-r--r--editor/import/editor_import_collada.cpp2
-rw-r--r--editor/input_event_configuration_dialog.cpp154
-rw-r--r--editor/input_event_configuration_dialog.h17
-rw-r--r--editor/plugin_config_dialog.cpp4
-rw-r--r--editor/plugin_config_dialog.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp73
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h6
-rw-r--r--editor/plugins/dedicated_server_export_plugin.cpp139
-rw-r--r--editor/plugins/dedicated_server_export_plugin.h58
-rw-r--r--editor/plugins/gradient_editor.cpp183
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.cpp56
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp140
-rw-r--r--editor/plugins/theme_editor_plugin.cpp10
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp2
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp29
-rw-r--r--editor/progress_dialog.cpp10
-rw-r--r--editor/progress_dialog.h2
-rw-r--r--editor/project_converter_3_to_4.cpp2
-rw-r--r--editor/scene_tree_dock.cpp12
-rw-r--r--editor/scene_tree_editor.cpp4
51 files changed, 1750 insertions, 401 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index cb8d98932d..ae54c20fe2 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -449,10 +449,14 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
// First Column - Icon
Ref<InputEventKey> k = event;
if (k.is_valid()) {
- if (k->get_physical_keycode() == Key::NONE) {
+ if (k->get_physical_keycode() == Key::NONE && k->get_keycode() == Key::NONE && k->get_key_label() != Key::NONE) {
+ event_item->set_icon(0, action_tree->get_theme_icon(SNAME("KeyboardLabel"), SNAME("EditorIcons")));
+ } else if (k->get_keycode() != Key::NONE) {
event_item->set_icon(0, action_tree->get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons")));
- } else {
+ } else if (k->get_physical_keycode() != Key::NONE) {
event_item->set_icon(0, action_tree->get_theme_icon(SNAME("KeyboardPhysical"), SNAME("EditorIcons")));
+ } else {
+ event_item->set_icon(0, action_tree->get_theme_icon(SNAME("KeyboardError"), SNAME("EditorIcons")));
}
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 9ff480f130..db12dbc72b 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -30,6 +30,7 @@
#include "connections_dialog.h"
+#include "core/config/project_settings.h"
#include "editor/doc_tools.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
@@ -165,6 +166,7 @@ void ConnectDialog::_tree_node_selected() {
if (!edit_mode) {
set_dst_method(generate_method_callback_name(source, signal, current));
}
+ _update_method_tree();
_update_ok_enabled();
}
@@ -182,6 +184,11 @@ void ConnectDialog::_unbind_count_changed(double p_count) {
}
}
+void ConnectDialog::_method_selected() {
+ TreeItem *selected_item = method_tree->get_selected();
+ dst_method->set_text(selected_item->get_text(0));
+}
+
/*
* Adds a new parameter bind to connection.
*/
@@ -242,14 +249,150 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p
String dst_method;
if (p_source == p_target) {
- dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_to_self_name")).format(subst);
+ dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_to_self_name")).format(subst);
} else {
- dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_name")).format(subst);
+ dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_name")).format(subst);
}
return dst_method;
}
+void ConnectDialog::_create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item) {
+ for (const MethodInfo &mi : p_methods) {
+ TreeItem *method_item = method_tree->create_item(p_parent_item);
+ method_item->set_text(0, mi.name);
+ if (mi.return_val.type == Variant::NIL) {
+ method_item->set_icon(0, get_theme_icon(SNAME("Variant"), "EditorIcons"));
+ } else {
+ method_item->set_icon(0, get_theme_icon(Variant::get_type_name(mi.return_val.type), "EditorIcons"));
+ }
+ }
+}
+
+List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const {
+ bool check_signal = compatible_methods_only->is_pressed();
+ List<MethodInfo> ret;
+
+ for (const MethodInfo &mi : p_methods) {
+ if (!p_search_string.is_empty() && !mi.name.contains(p_search_string)) {
+ continue;
+ }
+
+ if (check_signal) {
+ if (mi.arguments.size() != p_signal.arguments.size()) {
+ continue;
+ }
+
+ bool type_mismatch = false;
+ const List<PropertyInfo>::Element *E = p_signal.arguments.front();
+ for (const List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next(), E = E->next()) {
+ Variant::Type stype = E->get().type;
+ Variant::Type mtype = F->get().type;
+
+ if (stype != Variant::NIL && mtype != Variant::NIL && stype != mtype) {
+ type_mismatch = true;
+ break;
+ }
+ }
+
+ if (type_mismatch) {
+ continue;
+ }
+ }
+ ret.push_back(mi);
+ }
+ return ret;
+}
+
+void ConnectDialog::_update_method_tree() {
+ method_tree->clear();
+
+ Color disabled_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")) * 0.7;
+ String search_string = method_search->get_text();
+ Node *target = tree->get_selected();
+ if (!target) {
+ return;
+ }
+
+ MethodInfo signal_info;
+ if (compatible_methods_only->is_pressed()) {
+ List<MethodInfo> signals;
+ source->get_signal_list(&signals);
+ for (const MethodInfo &mi : signals) {
+ if (mi.name == signal) {
+ signal_info = mi;
+ break;
+ }
+ }
+ }
+
+ TreeItem *root_item = method_tree->create_item();
+ root_item->set_text(0, TTR("Methods"));
+ root_item->set_selectable(0, false);
+
+ // If a script is attached, get methods from it.
+ ScriptInstance *si = target->get_script_instance();
+ if (si) {
+ TreeItem *si_item = method_tree->create_item(root_item);
+ si_item->set_text(0, TTR("Attached Script"));
+ si_item->set_icon(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")));
+ si_item->set_selectable(0, false);
+
+ List<MethodInfo> methods;
+ si->get_method_list(&methods);
+ methods = _filter_method_list(methods, signal_info, search_string);
+
+ if (methods.is_empty()) {
+ si_item->set_custom_color(0, disabled_color);
+ } else {
+ _create_method_tree_items(methods, si_item);
+ }
+ }
+
+ if (script_methods_only->is_pressed()) {
+ return;
+ }
+
+ // Get methods from each class in the heirarchy.
+ StringName current_class = target->get_class_name();
+ do {
+ TreeItem *class_item = method_tree->create_item(root_item);
+ class_item->set_text(0, current_class);
+ Ref<Texture2D> icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons"));
+ if (has_theme_icon(current_class, SNAME("EditorIcons"))) {
+ icon = get_theme_icon(current_class, SNAME("EditorIcons"));
+ }
+ class_item->set_icon(0, icon);
+ class_item->set_selectable(0, false);
+
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(current_class, &methods, true);
+ methods = _filter_method_list(methods, signal_info, search_string);
+
+ if (methods.is_empty()) {
+ class_item->set_custom_color(0, disabled_color);
+ } else {
+ _create_method_tree_items(methods, class_item);
+ }
+ current_class = ClassDB::get_parent_class_nocheck(current_class);
+ } while (current_class != StringName());
+}
+
+void ConnectDialog::_method_check_button_pressed(const CheckButton *p_button) {
+ if (p_button == script_methods_only) {
+ EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_script_methods_only", p_button->is_pressed());
+ } else if (p_button == compatible_methods_only) {
+ EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_compatible_methods_only", p_button->is_pressed());
+ }
+ _update_method_tree();
+}
+
+void ConnectDialog::_open_method_popup() {
+ method_popup->popup_centered();
+ method_search->clear();
+ method_search->grab_focus();
+}
+
/*
* Enables or disables the connect button. The connect button is enabled if a
* node is selected and valid in the selected mode.
@@ -262,7 +405,7 @@ void ConnectDialog::_update_ok_enabled() {
return;
}
- if (!advanced->is_pressed() && target->get_script().is_null()) {
+ if (dst_method->get_text().is_empty()) {
get_ok_button()->set_disabled(true);
return;
}
@@ -288,14 +431,12 @@ void ConnectDialog::_notification(int p_what) {
style->set_content_margin(SIDE_TOP, style->get_content_margin(SIDE_TOP) + 1.0);
from_signal->add_theme_style_override("normal", style);
}
+ method_search->set_right_icon(get_theme_icon("Search", "EditorIcons"));
} break;
}
}
void ConnectDialog::_bind_methods() {
- ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed);
- ClassDB::bind_method("_update_ok_enabled", &ConnectDialog::_update_ok_enabled);
-
ADD_SIGNAL(MethodInfo("connected"));
}
@@ -437,7 +578,6 @@ void ConnectDialog::_advanced_pressed() {
error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()));
}
- _update_ok_enabled();
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "use_advanced_connections", advanced->is_pressed());
popup_centered();
@@ -457,9 +597,18 @@ ConnectDialog::ConnectDialog() {
main_hb->add_child(vbc_left);
vbc_left->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ HBoxContainer *from_signal_hb = memnew(HBoxContainer);
+
from_signal = memnew(LineEdit);
from_signal->set_editable(false);
- vbc_left->add_margin_child(TTR("From Signal:"), from_signal);
+ from_signal->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ from_signal_hb->add_child(from_signal);
+
+ advanced = memnew(CheckButton(TTR("Advanced")));
+ from_signal_hb->add_child(advanced);
+ advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed));
+
+ vbc_left->add_margin_child(TTR("From Signal:"), from_signal_hb);
tree = memnew(SceneTreeEditor(false));
tree->set_connecting_signal(true);
@@ -476,6 +625,39 @@ ConnectDialog::ConnectDialog() {
vbc_left->add_child(error_label);
error_label->hide();
+ method_popup = memnew(AcceptDialog);
+ method_popup->set_title(TTR("Select Method"));
+ method_popup->set_min_size(Vector2(400, 600) * EDSCALE);
+ add_child(method_popup);
+
+ VBoxContainer *method_vbc = memnew(VBoxContainer);
+ method_popup->add_child(method_vbc);
+
+ method_search = memnew(LineEdit);
+ method_vbc->add_child(method_search);
+ method_search->set_placeholder(TTR("Filter Methods"));
+ method_search->set_clear_button_enabled(true);
+ method_search->connect("text_changed", callable_mp(this, &ConnectDialog::_update_method_tree).unbind(1));
+
+ method_tree = memnew(Tree);
+ method_vbc->add_child(method_tree);
+ method_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ method_tree->set_hide_root(true);
+ method_tree->connect("item_selected", callable_mp(this, &ConnectDialog::_method_selected));
+ method_tree->connect("item_activated", callable_mp((Window *)method_popup, &Window::hide));
+
+ script_methods_only = memnew(CheckButton(TTR("Script Methods Only")));
+ method_vbc->add_child(script_methods_only);
+ script_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END);
+ script_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_script_methods_only", true));
+ script_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(script_methods_only));
+
+ compatible_methods_only = memnew(CheckButton(TTR("Compatible Methods Only")));
+ method_vbc->add_child(compatible_methods_only);
+ compatible_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END);
+ compatible_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_compatible_methods_only", true));
+ compatible_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(compatible_methods_only));
+
vbc_right = memnew(VBoxContainer);
main_hb->add_child(vbc_right);
vbc_right->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -521,10 +703,20 @@ ConnectDialog::ConnectDialog() {
vbc_right->add_margin_child(TTR("Unbind Signal Arguments:"), unbind_count);
+ HBoxContainer *hbc_method = memnew(HBoxContainer);
+ vbc_left->add_margin_child(TTR("Receiver Method:"), hbc_method);
+
dst_method = memnew(LineEdit);
dst_method->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ dst_method->connect("text_changed", callable_mp(method_tree, &Tree::deselect_all).unbind(1));
dst_method->connect("text_submitted", callable_mp(this, &ConnectDialog::_text_submitted));
- vbc_left->add_margin_child(TTR("Receiver Method:"), dst_method);
+ hbc_method->add_child(dst_method);
+
+ Button *open_tree_button = memnew(Button);
+ open_tree_button->set_flat(false);
+ open_tree_button->set_text("...");
+ open_tree_button->connect("pressed", callable_mp(this, &ConnectDialog::_open_method_popup));
+ hbc_method->add_child(open_tree_button);
advanced = memnew(CheckButton);
vbc_left->add_child(advanced);
@@ -566,7 +758,7 @@ ConnectDialog::~ConnectDialog() {
// Originally copied and adapted from EditorProperty, try to keep style in sync.
Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const {
EditorHelpBit *help_bit = memnew(EditorHelpBit);
- help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
+ help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1));
// p_text is expected to be something like this:
// "gui_input::(event: InputEvent)::<Signal description>"
@@ -1238,9 +1430,6 @@ ConnectionsDock::ConnectionsDock() {
tree->connect("item_mouse_selected", callable_mp(this, &ConnectionsDock::_rmb_pressed));
add_theme_constant_override("separation", 3 * EDSCALE);
-
- EDITOR_DEF("interface/editors/default_signal_callback_name", "_on_{node_name}_{signal_name}");
- EDITOR_DEF("interface/editors/default_signal_callback_to_self_name", "_on_{signal_name}");
}
ConnectionsDock::~ConnectionsDock() {
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index 829a98caed..0bea897976 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -114,9 +114,15 @@ private:
bool first_popup = true;
NodePath dst_path;
VBoxContainer *vbc_right = nullptr;
-
SceneTreeEditor *tree = nullptr;
AcceptDialog *error = nullptr;
+
+ AcceptDialog *method_popup = nullptr;
+ Tree *method_tree = nullptr;
+ LineEdit *method_search = nullptr;
+ CheckButton *script_methods_only = nullptr;
+ CheckButton *compatible_methods_only = nullptr;
+
SpinBox *unbind_count = nullptr;
EditorInspector *bind_editor = nullptr;
OptionButton *type_list = nullptr;
@@ -132,6 +138,14 @@ private:
void _item_activated();
void _text_submitted(const String &p_text);
void _tree_node_selected();
+
+ void _method_selected();
+ void _create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item);
+ List<MethodInfo> _filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const;
+ void _update_method_tree();
+ void _method_check_button_pressed(const CheckButton *p_button);
+ void _open_method_popup();
+
void _unbind_count_changed(double p_count);
void _add_bind();
void _remove_bind();
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 6a89b3572c..d00501280d 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -201,7 +201,6 @@ public:
String get_scene_type(int p_idx) const;
void set_scene_path(int p_idx, const String &p_path);
Ref<Script> get_scene_root_script(int p_idx) const;
- void set_edited_scene_version(uint64_t version, int p_scene_idx = -1);
void set_scene_modified_time(int p_idx, uint64_t p_time);
uint64_t get_scene_modified_time(int p_idx) const;
void clear_edited_scenes();
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 4cf947b006..e11251596a 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -2370,7 +2370,7 @@ EditorHelpBit::EditorHelpBit() {
rich_text = memnew(RichTextLabel);
add_child(rich_text);
rich_text->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked));
- rich_text->set_fit_content_height(true);
+ rich_text->set_fit_content(true);
set_custom_minimum_size(Size2(0, 50 * EDSCALE));
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 4753761f05..0166d4c719 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -886,7 +886,7 @@ void EditorProperty::_update_pin_flags() {
static Control *make_help_bit(const String &p_text, bool p_property) {
EditorHelpBit *help_bit = memnew(EditorHelpBit);
- help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
+ help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1));
PackedStringArray slices = p_text.split("::", false);
if (slices.is_empty()) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 05e0c6750e..173cbc6893 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -58,6 +58,7 @@
#include "scene/gui/tab_bar.h"
#include "scene/gui/tab_container.h"
#include "scene/main/window.h"
+#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
#include "servers/display_server.h"
#include "servers/navigation_server_3d.h"
@@ -124,6 +125,7 @@
#include "editor/plugins/asset_library_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/debugger_editor_plugin.h"
+#include "editor/plugins/dedicated_server_export_plugin.h"
#include "editor/plugins/editor_preview_plugins.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/plugins/gdextension_export_plugin.h"
@@ -997,6 +999,7 @@ void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
for (const String &E : scenes) {
reload_scene(E);
+ reload_instances_with_path_in_edited_scenes(E);
}
scene_tabs->set_current_tab(current_tab);
@@ -2112,6 +2115,7 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
}
}
active_plugins[owner_id].insert(plugin);
+ editor_plugins_over->add_plugin(plugin);
plugin->edit(p_object);
plugin->make_visible(true);
}
@@ -3085,7 +3089,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
}
String EditorNode::adjust_scene_name_casing(const String &root_name) {
- switch (GLOBAL_GET("editor/scene/scene_naming").operator int()) {
+ switch (GLOBAL_GET("editor/naming/scene_name_casing").operator int()) {
case SCENE_NAME_CASING_AUTO:
// Use casing of the root node.
break;
@@ -3653,12 +3657,12 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
if (get_edited_scene()) {
if (current_tab < 2) {
- // Use heuristic instead.
- int n2d = 0, n3d = 0;
- _find_node_types(get_edited_scene(), n2d, n3d);
- if (n2d > n3d) {
+ Node *editor_node = SceneTreeDock::get_singleton()->get_tree_editor()->get_selected();
+ editor_node = editor_node == nullptr ? get_edited_scene() : editor_node;
+
+ if (Object::cast_to<Node2D>(editor_node) || Object::cast_to<Control>(editor_node)) {
editor_select(EDITOR_2D);
- } else if (n3d > n2d) {
+ } else if (Object::cast_to<Node3D>(editor_node)) {
editor_select(EDITOR_3D);
}
}
@@ -3932,6 +3936,134 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
return OK;
}
+HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node) {
+ HashMap<StringName, Variant> modified_property_map;
+
+ List<PropertyInfo> pinfo;
+ p_node->get_property_list(&pinfo);
+ for (const PropertyInfo &E : pinfo) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
+ bool is_valid_revert = false;
+ Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_node, E.name, &is_valid_revert);
+ Variant current_value = p_node->get(E.name);
+ if (is_valid_revert) {
+ if (PropertyUtils::is_property_value_different(current_value, revert_value)) {
+ modified_property_map[E.name] = current_value;
+ }
+ }
+ }
+ }
+
+ return modified_property_map;
+}
+
+void EditorNode::update_diff_data_for_node(
+ Node *p_edited_scene,
+ Node *p_root,
+ Node *p_node,
+ HashMap<NodePath, ModificationNodeEntry> &p_modification_table,
+ List<AdditiveNodeEntry> &p_addition_list) {
+ bool node_part_of_subscene = p_node != p_edited_scene &&
+ p_edited_scene->get_scene_inherited_state().is_valid() &&
+ p_edited_scene->get_scene_inherited_state()->find_node_by_path(p_edited_scene->get_path_to(p_node)) >= 0;
+
+ // Loop through the owners until either we reach the root node or nullptr
+ Node *valid_node_owner = p_node->get_owner();
+ while (valid_node_owner) {
+ if (valid_node_owner == p_root) {
+ break;
+ }
+ valid_node_owner = valid_node_owner->get_owner();
+ }
+
+ if ((valid_node_owner == p_root && (p_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_root) {
+ HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node);
+
+ // Find all valid connections to other nodes.
+ List<Connection> connections_to;
+ p_node->get_all_signal_connections(&connections_to);
+
+ List<ConnectionWithNodePath> valid_connections_to;
+ for (const Connection &c : connections_to) {
+ Node *connection_target_node = Object::cast_to<Node>(c.callable.get_object());
+ if (connection_target_node) {
+ // TODO: add support for reinstating custom callables
+ if (!c.callable.is_custom()) {
+ ConnectionWithNodePath connection_to;
+ connection_to.connection = c;
+ connection_to.node_path = p_node->get_path_to(connection_target_node);
+ valid_connections_to.push_back(connection_to);
+ }
+ }
+ }
+
+ // Find all valid connections from other nodes.
+ List<Connection> connections_from;
+ p_node->get_signals_connected_to_this(&connections_from);
+
+ List<Connection> valid_connections_from;
+ for (const Connection &c : connections_from) {
+ Node *source_node = Object::cast_to<Node>(c.signal.get_object());
+
+ Node *valid_source_owner = nullptr;
+ if (source_node) {
+ valid_source_owner = source_node->get_owner();
+ while (valid_source_owner) {
+ if (valid_source_owner == p_root) {
+ break;
+ }
+ valid_source_owner = valid_source_owner->get_owner();
+ }
+ }
+
+ if (!source_node || valid_source_owner == nullptr) {
+ // TODO: add support for reinstating custom callables
+ if (!c.callable.is_custom()) {
+ valid_connections_from.push_back(c);
+ }
+ }
+ }
+
+ // Find all node groups.
+ List<Node::GroupInfo> groups;
+ p_node->get_groups(&groups);
+
+ if (!modified_properties.is_empty() || !valid_connections_to.is_empty() || !valid_connections_from.is_empty() || !groups.is_empty()) {
+ ModificationNodeEntry modification_node_entry;
+ modification_node_entry.property_table = modified_properties;
+ modification_node_entry.connections_to = valid_connections_to;
+ modification_node_entry.connections_from = valid_connections_from;
+ modification_node_entry.groups = groups;
+
+ p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry;
+ }
+ } else {
+ AdditiveNodeEntry new_additive_node_entry;
+ new_additive_node_entry.node = p_node;
+ new_additive_node_entry.parent = p_root->get_path_to(p_node->get_parent());
+ new_additive_node_entry.owner = p_node->get_owner();
+ new_additive_node_entry.index = p_node->get_index();
+
+ Node2D *node_2d = Object::cast_to<Node2D>(p_node);
+ if (node_2d) {
+ new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent());
+ }
+ Node3D *node_3d = Object::cast_to<Node3D>(p_node);
+ if (node_3d) {
+ new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent());
+ }
+ p_addition_list.push_back(new_additive_node_entry);
+
+ return;
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ Node *child = p_node->get_child(i);
+ update_diff_data_for_node(p_edited_scene, p_root, child, p_modification_table, p_addition_list);
+ }
+}
+//
+
void EditorNode::open_request(const String &p_path) {
if (!opening_prev) {
List<String>::Element *prev_scene_item = previous_scenes.find(p_path);
@@ -5309,7 +5441,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
_scene_tab_closed(scene_tabs->get_hovered_tab());
}
} else {
- if ((mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) || (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed())) {
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
_menu_option_confirm(FILE_NEW_SCENE, true);
}
}
@@ -5788,6 +5920,353 @@ void EditorNode::reload_scene(const String &p_path) {
scene_tabs->set_current_tab(current_tab);
}
+void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list) {
+ String scene_file_path = p_node->get_scene_file_path();
+
+ // This is going to get messy...
+ if (p_node->get_scene_file_path() == p_instance_path) {
+ p_instance_list.push_back(p_node);
+ } else {
+ Node *current_node = p_node;
+
+ Ref<SceneState> inherited_state = current_node->get_scene_inherited_state();
+ while (inherited_state.is_valid()) {
+ String inherited_path = inherited_state->get_path();
+ if (inherited_path == p_instance_path) {
+ p_instance_list.push_back(p_node);
+ break;
+ }
+
+ inherited_state = inherited_state->get_base_scene_state();
+ }
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ Node *child = p_node->get_child(i);
+ find_all_instances_inheriting_path_in_node(p_root, child, p_instance_path, p_instance_list);
+ }
+}
+
+void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) {
+ int original_edited_scene_idx = editor_data.get_edited_scene();
+ HashMap<int, List<Node *>> edited_scene_map;
+
+ // Walk through each opened scene to get a global list of all instances which match
+ // the current reimported scenes.
+ for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
+ if (editor_data.get_scene_path(i) != p_instance_path) {
+ Node *edited_scene_root = editor_data.get_edited_scene_root(i);
+
+ if (edited_scene_root) {
+ List<Node *> valid_nodes;
+ find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, p_instance_path, valid_nodes);
+ if (valid_nodes.size() > 0) {
+ edited_scene_map[i] = valid_nodes;
+ }
+ }
+ }
+ }
+
+ if (edited_scene_map.size() > 0) {
+ // Reload the new instance.
+ Error err;
+ Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err);
+ instance_scene_packed_scene->set_path(p_instance_path, true);
+
+ ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND(instance_scene_packed_scene.is_null());
+
+ HashMap<String, Ref<PackedScene>> local_scene_cache;
+ local_scene_cache[p_instance_path] = instance_scene_packed_scene;
+
+ for (const KeyValue<int, List<Node *>> &edited_scene_map_elem : edited_scene_map) {
+ // Set the current scene.
+ int current_scene_idx = edited_scene_map_elem.key;
+ editor_data.set_edited_scene(current_scene_idx);
+ Node *current_edited_scene = editor_data.get_edited_scene_root(current_scene_idx);
+
+ // Clear the history for this tab (should we allow history to be retained?).
+ EditorUndoRedoManager::get_singleton()->clear_history();
+
+ // Update the version
+ editor_data.is_scene_changed(current_scene_idx);
+
+ for (Node *original_node : edited_scene_map_elem.value) {
+ // Walk the tree for the current node and extract relevant diff data, storing it in the modification table.
+ // For additional nodes which are part of the current scene, they get added to the addition table.
+ HashMap<NodePath, ModificationNodeEntry> modification_table;
+ List<AdditiveNodeEntry> addition_list;
+ update_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list);
+
+ // Disconnect all relevant connections, all connections from and persistent connections to.
+ for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : modification_table) {
+ for (Connection conn : modification_table_entry.value.connections_from) {
+ conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable);
+ }
+ for (ConnectionWithNodePath cwnp : modification_table_entry.value.connections_to) {
+ Connection conn = cwnp.connection;
+ if (conn.flags & CONNECT_PERSIST) {
+ conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable);
+ }
+ }
+ }
+
+ // Store all the paths for any selected nodes which are ancestors of the node we're replacing.
+ List<NodePath> selected_node_paths;
+ for (Node *selected_node : editor_selection->get_selected_node_list()) {
+ if (selected_node == original_node || original_node->is_ancestor_of(selected_node)) {
+ selected_node_paths.push_back(original_node->get_path_to(selected_node));
+ editor_selection->remove_node(selected_node);
+ }
+ }
+
+ // Remove all nodes which were added as additional elements (they will be restored later).
+ for (AdditiveNodeEntry additive_node_entry : addition_list) {
+ Node *addition_node = additive_node_entry.node;
+ addition_node->get_parent()->remove_child(addition_node);
+ }
+
+ // Clear ownership of the nodes (kind of hack to workaround an issue with
+ // replace_by when called on nodes in other tabs).
+ List<Node *> nodes_owned_by_original_node;
+ original_node->get_owned_by(original_node, &nodes_owned_by_original_node);
+ for (Node *owned_node : nodes_owned_by_original_node) {
+ owned_node->set_owner(nullptr);
+ }
+
+ // Delete all the remaining node children.
+ while (original_node->get_child_count()) {
+ Node *child = original_node->get_child(0);
+
+ original_node->remove_child(child);
+ child->queue_free();
+ }
+
+ // Reset the editable instance state.
+ bool is_editable = true;
+ Node *owner = original_node->get_owner();
+ if (owner) {
+ is_editable = owner->is_editable_instance(original_node);
+ }
+
+ // Load a replacement scene for the node.
+ Ref<PackedScene> current_packed_scene;
+ if (original_node->get_scene_file_path() == p_instance_path) {
+ // If the node file name directly matches the scene we're replacing,
+ // just load it since we already cached it.
+ current_packed_scene = instance_scene_packed_scene;
+ } else {
+ // Otherwise, check the inheritance chain, reloading and caching any scenes
+ // we require along the way.
+ List<String> required_load_paths;
+ String scene_path = original_node->get_scene_file_path();
+ // Do we need to check if the paths are empty?
+ if (!scene_path.is_empty()) {
+ required_load_paths.push_front(scene_path);
+ }
+ Ref<SceneState> inherited_state = original_node->get_scene_inherited_state();
+ while (inherited_state.is_valid()) {
+ String inherited_path = inherited_state->get_path();
+ // Do we need to check if the paths are empty?
+ if (!inherited_path.is_empty()) {
+ required_load_paths.push_front(inherited_path);
+ }
+ inherited_state = inherited_state->get_base_scene_state();
+ }
+
+ // Ensure the inheritance chain is loaded in the correct order so that cache can
+ // be properly updated.
+ for (String path : required_load_paths) {
+ if (!local_scene_cache.find(path)) {
+ current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err);
+ current_packed_scene->set_path(path, true);
+ local_scene_cache[path] = current_packed_scene;
+ } else {
+ current_packed_scene = local_scene_cache[path];
+ }
+ }
+ }
+
+ ERR_FAIL_COND(current_packed_scene.is_null());
+
+ // Instantiate the node.
+ Node *instantiated_node = nullptr;
+ if (current_packed_scene.is_valid()) {
+ instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
+ }
+
+ ERR_FAIL_COND(!instantiated_node);
+
+ bool original_node_is_displayed_folded = original_node->is_displayed_folded();
+ bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
+
+ // Update the name to match
+ instantiated_node->set_name(original_node->get_name());
+
+ // Is this replacing the edited root node?
+ String original_node_file_path = original_node->get_scene_file_path();
+
+ if (current_edited_scene == original_node) {
+ instantiated_node->set_scene_instance_state(original_node->get_scene_instance_state());
+ // Fix unsaved inherited scene
+ if (original_node_file_path.is_empty()) {
+ Ref<SceneState> state = current_packed_scene->get_state();
+ state->set_path(current_packed_scene->get_path());
+ instantiated_node->set_scene_inherited_state(state);
+ instantiated_node->set_scene_file_path(String());
+ }
+ editor_data.set_edited_scene_root(instantiated_node);
+ current_edited_scene = instantiated_node;
+
+ if (original_node->is_inside_tree()) {
+ SceneTreeDock::get_singleton()->set_edited_scene(current_edited_scene);
+ original_node->get_tree()->set_edited_scene_root(instantiated_node);
+ }
+ }
+
+ // Replace the original node with the instantiated version.
+ original_node->replace_by(instantiated_node, false);
+
+ // Mark the old node for deletion.
+ original_node->queue_free();
+
+ // Restore the folded and placeholder state from the original node.
+ instantiated_node->set_display_folded(original_node_is_displayed_folded);
+ instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder);
+
+ if (owner) {
+ Ref<SceneState> ss_inst = owner->get_scene_instance_state();
+ if (ss_inst.is_valid()) {
+ ss_inst->update_instance_resource(p_instance_path, current_packed_scene);
+ }
+
+ owner->set_editable_instance(instantiated_node, is_editable);
+ }
+
+ // Attempt to re-add all the additional nodes.
+ for (AdditiveNodeEntry additive_node_entry : addition_list) {
+ Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent);
+
+ if (!parent_node) {
+ parent_node = current_edited_scene;
+ }
+
+ parent_node->add_child(additive_node_entry.node);
+ parent_node->move_child(additive_node_entry.node, additive_node_entry.index);
+ // If the additive node's owner was the node which got replaced, update it.
+ if (additive_node_entry.owner == original_node) {
+ additive_node_entry.owner = instantiated_node;
+ }
+
+ additive_node_entry.node->set_owner(additive_node_entry.owner);
+
+ // If the parent node was lost, attempt to restore the original global transform.
+ {
+ Node2D *node_2d = Object::cast_to<Node2D>(additive_node_entry.node);
+ if (node_2d) {
+ node_2d->set_transform(additive_node_entry.transform_2d);
+ }
+
+ Node3D *node_3d = Object::cast_to<Node3D>(additive_node_entry.node);
+ if (node_3d) {
+ node_3d->set_transform(additive_node_entry.transform_3d);
+ }
+ }
+ }
+
+ // Restore the selection.
+ if (selected_node_paths.size()) {
+ for (NodePath selected_node_path : selected_node_paths) {
+ Node *selected_node = instantiated_node->get_node_or_null(selected_node_path);
+ if (selected_node) {
+ editor_selection->add_node(selected_node);
+ }
+ }
+ editor_selection->update();
+ }
+
+ // Attempt to restore the modified properties and signals for the instantitated node and all its owned children.
+ for (KeyValue<NodePath, ModificationNodeEntry> &E : modification_table) {
+ NodePath new_current_path = E.key;
+ Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path);
+
+ if (modifiable_node) {
+ // Get properties for this node.
+ List<PropertyInfo> pinfo;
+ modifiable_node->get_property_list(&pinfo);
+
+ // Get names of all valid property names (TODO: make this more efficent).
+ List<String> property_names;
+ for (const PropertyInfo &E2 : pinfo) {
+ if (E2.usage & PROPERTY_USAGE_STORAGE) {
+ property_names.push_back(E2.name);
+ }
+ }
+
+ // Restore the modified properties for this node.
+ for (const KeyValue<StringName, Variant> &E2 : E.value.property_table) {
+ if (property_names.find(E2.key)) {
+ modifiable_node->set(E2.key, E2.value);
+ }
+ }
+ // Restore the connections to other nodes.
+ for (const ConnectionWithNodePath &E2 : E.value.connections_to) {
+ Connection conn = E2.connection;
+
+ // Get the node the callable is targetting.
+ Node *target_node = cast_to<Node>(conn.callable.get_object());
+
+ // If the callable object no longer exists or is marked for deletion,
+ // attempt to reaccquire the closest match by using the node path
+ // we saved earlier.
+ if (!target_node || !target_node->is_queued_for_deletion()) {
+ target_node = modifiable_node->get_node_or_null(E2.node_path);
+ }
+
+ if (target_node) {
+ // Reconstruct the callable.
+ Callable new_callable = Callable(target_node, conn.callable.get_method());
+
+ if (!modifiable_node->is_connected(conn.signal.get_name(), new_callable)) {
+ ERR_FAIL_COND(modifiable_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK);
+ }
+ }
+ }
+
+ // Restore the connections from other nodes.
+ for (const Connection &E2 : E.value.connections_from) {
+ Connection conn = E2;
+
+ bool valid = modifiable_node->has_method(conn.callable.get_method()) || Ref<Script>(modifiable_node->get_script()).is_null() || Ref<Script>(modifiable_node->get_script())->has_method(conn.callable.get_method());
+ ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method()));
+
+ // Get the object which the signal is connected from.
+ Object *source_object = conn.signal.get_object();
+
+ if (source_object) {
+ ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(modifiable_node, conn.callable.get_method()), conn.flags) != OK);
+ }
+ }
+
+ // Re-add the groups.
+ for (const Node::GroupInfo &E2 : E.value.groups) {
+ modifiable_node->add_to_group(E2.name, E2.persistent);
+ }
+ }
+ }
+ }
+ // Cleanup the history of the changes.
+ editor_history.cleanup_history();
+
+ current_edited_scene->propagate_notification(NOTIFICATION_NODE_RECACHE_REQUESTED);
+ }
+ edited_scene_map.clear();
+ }
+ editor_data.set_edited_scene(original_edited_scene_idx);
+
+ _edit_current();
+}
+
int EditorNode::plugin_init_callback_count = 0;
void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callback) {
@@ -5951,7 +6430,7 @@ void EditorNode::_feature_profile_changed() {
}
void EditorNode::_bind_methods() {
- GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/scene/scene_naming", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE);
ClassDB::bind_method("edit_current", &EditorNode::edit_current);
ClassDB::bind_method("edit_node", &EditorNode::edit_node);
@@ -7401,6 +7880,11 @@ EditorNode::EditorNode() {
EditorExport::get_singleton()->add_export_plugin(gdextension_export_plugin);
+ Ref<DedicatedServerExportPlugin> dedicated_server_export_plugin;
+ dedicated_server_export_plugin.instantiate();
+
+ EditorExport::get_singleton()->add_export_plugin(dedicated_server_export_plugin);
+
Ref<PackedSceneEditorTranslationParserPlugin> packed_scene_translation_parser_plugin;
packed_scene_translation_parser_plugin.instantiate();
EditorTranslationParser::get_singleton()->add_parser(packed_scene_translation_parser_plugin, EditorTranslationParser::STANDARD);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index ef4ea34da2..bb10abb589 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -87,6 +87,7 @@ class ImportDock;
class LinkButton;
class MenuBar;
class MenuButton;
+class Node2D;
class NodeDock;
class OptionButton;
class OrphanResourcesDialog;
@@ -818,6 +819,37 @@ public:
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
+ HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node);
+
+ struct AdditiveNodeEntry {
+ Node *node = nullptr;
+ NodePath parent = NodePath();
+ Node *owner = nullptr;
+ int index = 0;
+ // Used if the original parent node is lost
+ Transform2D transform_2d;
+ Transform3D transform_3d;
+ };
+
+ struct ConnectionWithNodePath {
+ Connection connection;
+ NodePath node_path;
+ };
+
+ struct ModificationNodeEntry {
+ HashMap<StringName, Variant> property_table;
+ List<ConnectionWithNodePath> connections_to;
+ List<Connection> connections_from;
+ List<Node::GroupInfo> groups;
+ };
+
+ void update_diff_data_for_node(
+ Node *p_edited_scene,
+ Node *p_root,
+ Node *p_node,
+ HashMap<NodePath, ModificationNodeEntry> &p_modification_table,
+ List<AdditiveNodeEntry> &p_addition_list);
+
bool is_scene_open(const String &p_path);
void set_current_scene(int p_idx);
@@ -870,6 +902,9 @@ public:
void reload_scene(const String &p_path);
+ void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list);
+ void reload_instances_with_path_in_edited_scenes(const String &p_path);
+
bool is_exiting() const { return exiting; }
Button *get_pause_button() { return pause_button; }
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index a69214f903..152e77acb7 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -268,7 +268,7 @@ void EditorPropertyTextEnum::_custom_value_accepted() {
_custom_value_submitted(new_value);
}
-void EditorPropertyTextEnum::_custom_value_cancelled() {
+void EditorPropertyTextEnum::_custom_value_canceled() {
custom_value_edit->set_text(get_edited_object()->get(get_edited_property()));
edit_custom_layout->hide();
@@ -380,7 +380,7 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() {
cancel_button = memnew(Button);
cancel_button->set_flat(true);
edit_custom_layout->add_child(cancel_button);
- cancel_button->connect("pressed", callable_mp(this, &EditorPropertyTextEnum::_custom_value_cancelled));
+ cancel_button->connect("pressed", callable_mp(this, &EditorPropertyTextEnum::_custom_value_canceled));
add_focusable(option_button);
add_focusable(edit_button);
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index ea9203cbf4..d29ec12f97 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -121,7 +121,7 @@ class EditorPropertyTextEnum : public EditorProperty {
void _edit_custom_value();
void _custom_value_submitted(String p_value);
void _custom_value_accepted();
- void _custom_value_cancelled();
+ void _custom_value_canceled();
protected:
virtual void _set_read_only(bool p_read_only) override;
diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp
index 274fe34c52..ea4a7133bf 100644
--- a/editor/event_listener_line_edit.cpp
+++ b/editor/event_listener_line_edit.cpp
@@ -59,16 +59,42 @@ static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = {
String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) {
ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEvent");
- String text = p_event->as_text();
-
+ String text;
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_command_or_control_autoremap()) {
+ if (key.is_valid()) {
+ String mods_text = key->InputEventWithModifiers::as_text();
+ mods_text = mods_text.is_empty() ? mods_text : mods_text + "+";
+ if (key->is_command_or_control_autoremap()) {
#ifdef MACOS_ENABLED
- text = text.replace("Command", "Command/Ctrl");
+ mods_text = mods_text.replace("Command", "Command/Ctrl");
#else
- text = text.replace("Ctrl", "Command/Ctrl");
+ mods_text = mods_text.replace("Ctrl", "Command/Ctrl");
#endif
+ }
+
+ if (key->get_keycode() != Key::NONE) {
+ text += mods_text + keycode_get_string(key->get_keycode());
+ }
+ if (key->get_physical_keycode() != Key::NONE) {
+ if (!text.is_empty()) {
+ text += " or ";
+ }
+ text += mods_text + keycode_get_string(key->get_physical_keycode()) + " (" + RTR("Physical") + ")";
+ }
+ if (key->get_key_label() != Key::NONE) {
+ if (!text.is_empty()) {
+ text += " or ";
+ }
+ text += mods_text + keycode_get_string(key->get_key_label()) + " (Unicode)";
+ }
+
+ if (text.is_empty()) {
+ text = "(" + RTR("Unset") + ")";
+ }
+ } else {
+ text = p_event->as_text();
}
+
Ref<InputEventMouse> mouse = p_event;
Ref<InputEventJoypadMotion> jp_motion = p_event;
Ref<InputEventJoypadButton> jp_button = p_event;
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 1d147cc5b9..4900ced2e4 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -45,6 +45,7 @@ void EditorExport::_save() {
config->set_value(section, "name", preset->get_name());
config->set_value(section, "platform", preset->get_platform()->get_name());
config->set_value(section, "runnable", preset->is_runnable());
+ config->set_value(section, "dedicated_server", preset->is_dedicated_server());
config->set_value(section, "custom_features", preset->get_custom_features());
bool save_files = false;
@@ -64,6 +65,11 @@ void EditorExport::_save() {
config->set_value(section, "export_filter", "exclude");
save_files = true;
} break;
+ case EditorExportPreset::EXPORT_CUSTOMIZED: {
+ config->set_value(section, "export_filter", "customized");
+ config->set_value(section, "customized_files", preset->get_customized_files());
+ save_files = false;
+ };
}
if (save_files) {
@@ -213,6 +219,7 @@ void EditorExport::load_config() {
preset->set_name(config->get_value(section, "name"));
preset->set_runnable(config->get_value(section, "runnable"));
+ preset->set_dedicated_server(config->get_value(section, "dedicated_server", false));
if (config->has_section_key(section, "custom_features")) {
preset->set_custom_features(config->get_value(section, "custom_features"));
@@ -233,6 +240,10 @@ void EditorExport::load_config() {
} else if (export_filter == "exclude") {
preset->set_export_filter(EditorExportPreset::EXCLUDE_SELECTED_RESOURCES);
get_files = true;
+ } else if (export_filter == "customized") {
+ preset->set_export_filter(EditorExportPreset::EXPORT_CUSTOMIZED);
+ preset->set_customized_files(config->get_value(section, "customized_files", Dictionary()));
+ get_files = false;
}
if (get_files) {
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 8da52e20f7..9f79eecfb7 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -343,6 +343,24 @@ void EditorExportPlatform::_export_find_resources(EditorFileSystemDirectory *p_d
}
}
+void EditorExportPlatform::_export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths) {
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ EditorFileSystemDirectory *subdir = p_dir->get_subdir(i);
+ _export_find_customized_resources(p_preset, subdir, p_preset->get_file_export_mode(subdir->get_path(), p_mode), p_paths);
+ }
+
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ if (p_dir->get_file_type(i) == "TextFile") {
+ continue;
+ }
+ String path = p_dir->get_file_path(i);
+ EditorExportPreset::FileExportMode file_mode = p_preset->get_file_export_mode(path, p_mode);
+ if (file_mode != EditorExportPreset::MODE_FILE_REMOVE) {
+ p_paths.insert(path);
+ }
+ }
+}
+
void EditorExportPlatform::_export_find_dependencies(const String &p_path, HashSet<String> &p_paths) {
if (p_paths.has(p_path)) {
return;
@@ -639,10 +657,20 @@ bool EditorExportPlatform::_export_customize_object(Object *p_object, LocalVecto
return changed;
}
+bool EditorExportPlatform::_is_editable_ancestor(Node *p_root, Node *p_node) {
+ while (p_node != nullptr && p_node != p_root) {
+ if (p_root->is_editable_instance(p_node)) {
+ return true;
+ }
+ p_node = p_node->get_owner();
+ }
+ return false;
+}
+
bool EditorExportPlatform::_export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins) {
bool changed = false;
- if (p_node == p_root || p_node->get_owner() == p_root) {
+ if (p_root == p_node || p_node->get_owner() == p_root || _is_editable_ancestor(p_root, p_node)) {
if (_export_customize_object(p_node, customize_resources_plugins)) {
changed = true;
}
@@ -757,10 +785,10 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector
break;
}
}
+ }
- if (_export_customize_object(res.ptr(), customize_resources_plugins)) {
- modified = true;
- }
+ if (_export_customize_object(res.ptr(), customize_resources_plugins)) {
+ modified = true;
}
if (modified || p_force_save) {
@@ -796,6 +824,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
for (int i = 0; i < files.size(); i++) {
paths.erase(files[i]);
}
+ } else if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) {
+ _export_find_customized_resources(p_preset, EditorFileSystem::get_singleton()->get_filesystem(), p_preset->get_file_export_mode("res://"), paths);
} else {
bool scenes_only = p_preset->get_export_filter() == EditorExportPreset::EXPORT_SELECTED_SCENES;
@@ -939,14 +969,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
LocalVector<Ref<EditorExportPlugin>> customize_scenes_plugins;
for (int i = 0; i < export_plugins.size(); i++) {
- if (export_plugins[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) {
+ if (export_plugins.write[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) {
customize_resources_plugins.push_back(export_plugins[i]);
custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash);
uint64_t hash = export_plugins[i]->_get_customization_configuration_hash();
custom_resources_hash = hash_murmur3_one_64(hash, custom_resources_hash);
}
- if (export_plugins[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) {
+ if (export_plugins.write[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) {
customize_scenes_plugins.push_back(export_plugins[i]);
custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash);
@@ -1218,6 +1248,9 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
}
}
+ for (int i = 0; i < export_plugins.size(); i++) {
+ custom_list.append_array(export_plugins[i]->_get_export_features(Ref<EditorExportPlatform>(this), p_debug));
+ }
ProjectSettings::CustomMap custom_map;
if (path_remaps.size()) {
@@ -1294,7 +1327,9 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
} else {
// Use default text server data.
String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp_icu_data");
- TS->save_support_data(icu_data_file);
+ if (!TS->save_support_data(icu_data_file)) {
+ return ERR_INVALID_DATA;
+ }
Vector<uint8_t> array = FileAccess::get_file_as_bytes(icu_data_file);
err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key);
DirAccess::remove_file_or_error(icu_data_file);
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 1fb35759ac..3b4e92c9bd 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -91,6 +91,7 @@ private:
Vector<ExportMessage> messages;
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
+ void _export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths);
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
@@ -112,6 +113,7 @@ private:
bool _export_customize_array(Array &array, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
bool _export_customize_object(Object *p_object, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
bool _export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
+ bool _is_editable_ancestor(Node *p_root, Node *p_node);
String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save);
diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp
index dfd4520eec..0add55820f 100644
--- a/editor/export/editor_export_plugin.cpp
+++ b/editor/export/editor_export_plugin.cpp
@@ -141,7 +141,7 @@ void EditorExportPlugin::_export_end_script() {
// Customization
-bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const {
+bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
bool ret = false;
GDVIRTUAL_CALL(_begin_customize_resources, p_platform, p_features, ret);
return ret;
@@ -153,7 +153,7 @@ Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_res
return ret;
}
-bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const {
+bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
bool ret = false;
GDVIRTUAL_CALL(_begin_customize_scenes, p_platform, p_features, ret);
return ret;
@@ -185,6 +185,12 @@ String EditorExportPlugin::_get_name() const {
return ret;
}
+PackedStringArray EditorExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const {
+ PackedStringArray ret;
+ GDVIRTUAL_CALL(_get_export_features, p_platform, p_debug, ret);
+ return ret;
+}
+
void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
}
diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h
index 5ac0a70c3e..fad647a67b 100644
--- a/editor/export/editor_export_plugin.h
+++ b/editor/export/editor_export_plugin.h
@@ -120,18 +120,22 @@ protected:
GDVIRTUAL0(_end_customize_scenes)
GDVIRTUAL0(_end_customize_resources)
+ GDVIRTUAL2RC(PackedStringArray, _get_export_features, const Ref<EditorExportPlatform> &, bool);
+
GDVIRTUAL0RC(String, _get_name)
- bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const; // Return true if this plugin does property export customization
- Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made.
+ virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization
+ virtual Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made.
+
+ virtual bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization
+ virtual Node *_customize_scene(Node *p_root, const String &p_path); // Return true if a change was made
- bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const; // Return true if this plugin does property export customization
- Node *_customize_scene(Node *p_root, const String &p_path); // Return true if a change was made
+ virtual uint64_t _get_customization_configuration_hash() const; // Hash used for caching customized resources and scenes.
- uint64_t _get_customization_configuration_hash() const; // Hash used for caching customized resources and scenes.
+ virtual void _end_customize_scenes();
+ virtual void _end_customize_resources();
- void _end_customize_scenes();
- void _end_customize_resources();
+ virtual PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
virtual String _get_name() const;
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index c6365806b3..6beef623bc 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -64,15 +64,29 @@ Ref<EditorExportPlatform> EditorExportPreset::get_platform() const {
return platform;
}
-void EditorExportPreset::update_files_to_export() {
- Vector<String> to_remove;
- for (const String &E : selected_files) {
- if (!FileAccess::exists(E)) {
- to_remove.push_back(E);
+void EditorExportPreset::update_files() {
+ {
+ Vector<String> to_remove;
+ for (const String &E : selected_files) {
+ if (!FileAccess::exists(E)) {
+ to_remove.push_back(E);
+ }
+ }
+ for (int i = 0; i < to_remove.size(); ++i) {
+ selected_files.erase(to_remove[i]);
}
}
- for (int i = 0; i < to_remove.size(); ++i) {
- selected_files.erase(to_remove[i]);
+
+ {
+ Vector<String> to_remove;
+ for (const KeyValue<String, FileExportMode> &E : customized_files) {
+ if (!FileAccess::exists(E.key) && !DirAccess::exists(E.key)) {
+ to_remove.push_back(E.key);
+ }
+ }
+ for (int i = 0; i < to_remove.size(); ++i) {
+ customized_files.erase(to_remove[i]);
+ }
}
}
@@ -84,6 +98,48 @@ Vector<String> EditorExportPreset::get_files_to_export() const {
return files;
}
+Dictionary EditorExportPreset::get_customized_files() const {
+ Dictionary files;
+ for (const KeyValue<String, FileExportMode> &E : customized_files) {
+ String mode;
+ switch (E.value) {
+ case MODE_FILE_NOT_CUSTOMIZED: {
+ continue;
+ } break;
+ case MODE_FILE_STRIP: {
+ mode = "strip";
+ } break;
+ case MODE_FILE_KEEP: {
+ mode = "keep";
+ } break;
+ case MODE_FILE_REMOVE: {
+ mode = "remove";
+ }
+ }
+ files[E.key] = mode;
+ }
+ return files;
+}
+
+int EditorExportPreset::get_customized_files_count() const {
+ return customized_files.size();
+}
+
+void EditorExportPreset::set_customized_files(const Dictionary &p_files) {
+ for (const Variant *key = p_files.next(nullptr); key; key = p_files.next(key)) {
+ EditorExportPreset::FileExportMode mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
+ String value = p_files[*key];
+ if (value == "strip") {
+ mode = EditorExportPreset::MODE_FILE_STRIP;
+ } else if (value == "keep") {
+ mode = EditorExportPreset::MODE_FILE_KEEP;
+ } else if (value == "remove") {
+ mode = EditorExportPreset::MODE_FILE_REMOVE;
+ }
+ set_file_export_mode(*key, mode);
+ }
+}
+
void EditorExportPreset::set_name(const String &p_name) {
name = p_name;
EditorExport::singleton->save_presets();
@@ -102,6 +158,15 @@ bool EditorExportPreset::is_runnable() const {
return runnable;
}
+void EditorExportPreset::set_dedicated_server(bool p_enable) {
+ dedicated_server = p_enable;
+ EditorExport::singleton->save_presets();
+}
+
+bool EditorExportPreset::is_dedicated_server() const {
+ return dedicated_server;
+}
+
void EditorExportPreset::set_export_filter(ExportFilter p_filter) {
export_filter = p_filter;
EditorExport::singleton->save_presets();
@@ -158,6 +223,23 @@ bool EditorExportPreset::has_export_file(const String &p_path) {
return selected_files.has(p_path);
}
+void EditorExportPreset::set_file_export_mode(const String &p_path, EditorExportPreset::FileExportMode p_mode) {
+ if (p_mode == FileExportMode::MODE_FILE_NOT_CUSTOMIZED) {
+ customized_files.erase(p_path);
+ } else {
+ customized_files.insert(p_path, p_mode);
+ }
+ EditorExport::singleton->save_presets();
+}
+
+EditorExportPreset::FileExportMode EditorExportPreset::get_file_export_mode(const String &p_path, EditorExportPreset::FileExportMode p_default) const {
+ HashMap<String, FileExportMode>::ConstIterator i = customized_files.find(p_path);
+ if (i) {
+ return i->value;
+ }
+ return p_default;
+}
+
void EditorExportPreset::set_custom_features(const String &p_custom_features) {
custom_features = p_custom_features;
EditorExport::singleton->save_presets();
diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h
index de208f410e..db139d8860 100644
--- a/editor/export/editor_export_preset.h
+++ b/editor/export/editor_export_preset.h
@@ -44,6 +44,14 @@ public:
EXPORT_SELECTED_SCENES,
EXPORT_SELECTED_RESOURCES,
EXCLUDE_SELECTED_RESOURCES,
+ EXPORT_CUSTOMIZED,
+ };
+
+ enum FileExportMode {
+ MODE_FILE_NOT_CUSTOMIZED,
+ MODE_FILE_STRIP,
+ MODE_FILE_KEEP,
+ MODE_FILE_REMOVE,
};
private:
@@ -55,7 +63,9 @@ private:
String exporter;
HashSet<String> selected_files;
+ HashMap<String, FileExportMode> customized_files;
bool runnable = false;
+ bool dedicated_server = false;
friend class EditorExport;
friend class EditorExportPlatform;
@@ -85,20 +95,29 @@ public:
bool has(const StringName &p_property) const { return values.has(p_property); }
- void update_files_to_export();
+ void update_files();
Vector<String> get_files_to_export() const;
+ Dictionary get_customized_files() const;
+ int get_customized_files_count() const;
+ void set_customized_files(const Dictionary &p_files);
void add_export_file(const String &p_path);
void remove_export_file(const String &p_path);
bool has_export_file(const String &p_path);
+ void set_file_export_mode(const String &p_path, FileExportMode p_mode);
+ FileExportMode get_file_export_mode(const String &p_path, FileExportMode p_default = MODE_FILE_NOT_CUSTOMIZED) const;
+
void set_name(const String &p_name);
String get_name() const;
void set_runnable(bool p_enable);
bool is_runnable() const;
+ void set_dedicated_server(bool p_enable);
+ bool is_dedicated_server() const;
+
void set_export_filter(ExportFilter p_filter);
ExportFilter get_export_filter() const;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 2caebc6f04..52c192164f 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -45,6 +45,7 @@
#include "scene/gui/link_button.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
+#include "scene/gui/popup_menu.h"
#include "scene/gui/split_container.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
@@ -165,7 +166,7 @@ void ProjectExportDialog::_update_presets() {
if (preset->is_runnable()) {
preset_name += " (" + TTR("Runnable") + ")";
}
- preset->update_files_to_export();
+ preset->update_files();
presets->add_item(preset_name, preset->get_platform()->get_logo());
}
@@ -244,6 +245,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_filter->select(current->get_export_filter());
include_filters->set_text(current->get_include_filter());
exclude_filters->set_text(current->get_exclude_filter());
+ server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
_fill_resource_tree();
@@ -570,6 +572,7 @@ void ProjectExportDialog::_duplicate_preset() {
if (make_runnable) {
preset->set_runnable(make_runnable);
}
+ preset->set_dedicated_server(current->is_dedicated_server());
preset->set_export_filter(current->get_export_filter());
preset->set_include_filter(current->get_include_filter());
preset->set_exclude_filter(current->get_exclude_filter());
@@ -692,7 +695,16 @@ void ProjectExportDialog::_export_type_changed(int p_which) {
return;
}
- current->set_export_filter(EditorExportPreset::ExportFilter(p_which));
+ EditorExportPreset::ExportFilter filter_type = (EditorExportPreset::ExportFilter)p_which;
+ current->set_export_filter(filter_type);
+ current->set_dedicated_server(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED);
+ server_strip_message->set_visible(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED);
+
+ // Default to stripping everything when first switching to server build.
+ if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) {
+ current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP);
+ }
+
updating = true;
_fill_resource_tree();
updating = false;
@@ -728,25 +740,53 @@ void ProjectExportDialog::_fill_resource_tree() {
return;
}
+ TreeItem *root = include_files->create_item();
+
+ if (f == EditorExportPreset::EXPORT_CUSTOMIZED) {
+ include_files->set_columns(2);
+ include_files->set_column_expand(1, false);
+ include_files->set_column_custom_minimum_width(1, 250 * EDSCALE);
+ } else {
+ include_files->set_columns(1);
+ }
+
include_label->show();
include_margin->show();
- TreeItem *root = include_files->create_item();
+ _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f);
+}
- _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f == EditorExportPreset::EXPORT_SELECTED_SCENES);
+void ProjectExportDialog::_setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode) {
+ if (p_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
+ p_item->set_checked(0, false);
+ p_item->set_cell_mode(1, TreeItem::CELL_MODE_STRING);
+ p_item->set_text(1, "");
+ p_item->set_editable(1, false);
+ p_item->set_selectable(1, false);
+ } else {
+ p_item->set_checked(0, true);
+ p_item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM);
+ p_item->set_text(1, file_mode_popup->get_item_text(file_mode_popup->get_item_index(p_mode)));
+ p_item->set_editable(1, true);
+ p_item->set_selectable(1, true);
+ }
}
-bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, bool p_only_scenes) {
+bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, EditorExportPreset::ExportFilter p_export_filter) {
p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
p_item->set_icon(0, presets->get_theme_icon(SNAME("folder"), SNAME("FileDialog")));
p_item->set_text(0, p_dir->get_name() + "/");
p_item->set_editable(0, true);
p_item->set_metadata(0, p_dir->get_path());
+ if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) {
+ _setup_item_for_file_mode(p_item, current->get_file_export_mode(p_dir->get_path()));
+ }
+
bool used = false;
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
TreeItem *subdir = include_files->create_item(p_item);
- if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) {
+ if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_export_filter)) {
used = true;
} else {
memdelete(subdir);
@@ -755,7 +795,7 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem
for (int i = 0; i < p_dir->get_file_count(); i++) {
String type = p_dir->get_file_type(i);
- if (p_only_scenes && type != "PackedScene") {
+ if (p_export_filter == EditorExportPreset::EXPORT_SELECTED_SCENES && type != "PackedScene") {
continue;
}
if (type == "TextFile") {
@@ -770,9 +810,14 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem
file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type));
file->set_editable(0, true);
- file->set_checked(0, current->has_export_file(path));
file->set_metadata(0, path);
- file->propagate_check(0);
+
+ if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) {
+ _setup_item_for_file_mode(file, current->get_file_export_mode(path));
+ } else {
+ file->set_checked(0, current->has_export_file(path));
+ file->propagate_check(0);
+ }
used = true;
}
@@ -794,7 +839,19 @@ void ProjectExportDialog::_tree_changed() {
return;
}
- item->propagate_check(0);
+ if (current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) {
+ EditorExportPreset::FileExportMode file_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
+ String path = item->get_metadata(0);
+
+ if (item->is_checked(0)) {
+ file_mode = current->get_file_export_mode(path, EditorExportPreset::MODE_FILE_STRIP);
+ }
+
+ _setup_item_for_file_mode(item, file_mode);
+ current->set_file_export_mode(path, file_mode);
+ } else {
+ item->propagate_check(0);
+ }
}
void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) {
@@ -814,6 +871,30 @@ void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) {
}
}
+void ProjectExportDialog::_tree_popup_edited(bool p_arrow_clicked) {
+ Rect2 bounds = include_files->get_custom_popup_rect();
+ bounds.position += get_global_canvas_transform().get_origin();
+ bounds.size *= get_global_canvas_transform().get_scale();
+ if (!is_embedding_subwindows()) {
+ bounds.position += get_position();
+ }
+ file_mode_popup->popup(bounds);
+}
+
+void ProjectExportDialog::_set_file_export_mode(int p_id) {
+ Ref<EditorExportPreset> current = get_current_preset();
+ if (current.is_null()) {
+ return;
+ }
+
+ TreeItem *item = include_files->get_edited();
+ String path = item->get_metadata(0);
+
+ current->set_file_export_mode(path, (EditorExportPreset::FileExportMode)p_id);
+
+ item->set_text(1, file_mode_popup->get_item_text(file_mode_popup->get_item_index(p_id)));
+}
+
void ProjectExportDialog::_export_pck_zip() {
Ref<EditorExportPreset> current = get_current_preset();
ERR_FAIL_COND(current.is_null());
@@ -1068,6 +1149,7 @@ ProjectExportDialog::ProjectExportDialog() {
export_filter->add_item(TTR("Export selected scenes (and dependencies)"));
export_filter->add_item(TTR("Export selected resources (and dependencies)"));
export_filter->add_item(TTR("Export all resources in the project except resources checked below"));
+ export_filter->add_item(TTR("Export as dedicated server"));
resources_vb->add_margin_child(TTR("Export Mode:"), export_filter);
export_filter->connect("item_selected", callable_mp(this, &ProjectExportDialog::_export_type_changed));
@@ -1082,6 +1164,36 @@ ProjectExportDialog::ProjectExportDialog() {
include_margin->add_child(include_files);
include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
include_files->connect("check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item));
+ include_files->connect("custom_popup_edited", callable_mp(this, &ProjectExportDialog::_tree_popup_edited));
+
+ server_strip_message = memnew(Label);
+ server_strip_message->set_visible(false);
+ server_strip_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
+ resources_vb->add_child(server_strip_message);
+
+ {
+ List<StringName> resource_names;
+ ClassDB::get_inheriters_from_class("Resource", &resource_names);
+
+ PackedStringArray strippable;
+ for (StringName resource_name : resource_names) {
+ if (ClassDB::has_method(resource_name, "create_placeholder", true)) {
+ strippable.push_back(resource_name);
+ }
+ }
+ strippable.sort();
+
+ String message = TTR("\"Strip Visuals\" will replace the following resources with placeholders:") + " ";
+ message += String(", ").join(strippable);
+ server_strip_message->set_text(message);
+ }
+
+ file_mode_popup = memnew(PopupMenu);
+ add_child(file_mode_popup);
+ file_mode_popup->add_item(TTR("Strip Visuals"), EditorExportPreset::MODE_FILE_STRIP);
+ file_mode_popup->add_item(TTR("Keep"), EditorExportPreset::MODE_FILE_KEEP);
+ file_mode_popup->add_item(TTR("Remove"), EditorExportPreset::MODE_FILE_REMOVE);
+ file_mode_popup->connect("id_pressed", callable_mp(this, &ProjectExportDialog::_set_file_export_mode));
include_filters = memnew(LineEdit);
resources_vb->add_margin_child(
diff --git a/editor/export/project_export.h b/editor/export/project_export.h
index d392dba2a4..63f8fc4a2e 100644
--- a/editor/export/project_export.h
+++ b/editor/export/project_export.h
@@ -31,11 +31,11 @@
#ifndef PROJECT_EXPORT_H
#define PROJECT_EXPORT_H
+#include "editor/export/editor_export_preset.h"
#include "scene/gui/dialogs.h"
class CheckBox;
class CheckButton;
-class EditorExportPreset;
class EditorFileDialog;
class EditorFileSystemDirectory;
class EditorInspector;
@@ -43,6 +43,7 @@ class EditorPropertyPath;
class ItemList;
class MenuButton;
class OptionButton;
+class PopupMenu;
class RichTextLabel;
class TabContainer;
class Tree;
@@ -75,6 +76,8 @@ private:
LineEdit *include_filters = nullptr;
LineEdit *exclude_filters = nullptr;
Tree *include_files = nullptr;
+ Label *server_strip_message = nullptr;
+ PopupMenu *file_mode_popup = nullptr;
Label *include_label = nullptr;
MarginContainer *include_margin = nullptr;
@@ -113,9 +116,12 @@ private:
void _export_type_changed(int p_which);
void _filter_changed(const String &p_filter);
void _fill_resource_tree();
- bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, bool p_only_scenes);
+ void _setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode);
+ bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, EditorExportPreset::ExportFilter p_export_filter);
void _tree_changed();
void _check_propagated_to_item(Object *p_obj, int column);
+ void _tree_popup_edited(bool p_arrow_clicked);
+ void _set_file_export_mode(int p_id);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 70a6ded690..378e06b4c9 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2652,7 +2652,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
p_popup->add_icon_shortcut(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), item_text);
if (!is_directory) {
- p_popup->add_icon_item(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), TTR("Open in External Program"), FILE_OPEN_EXTERNAL);
+ p_popup->add_icon_shortcut(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL);
}
path = fpath;
}
@@ -2873,6 +2873,8 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) {
_tree_rmb_option(FILE_RENAME);
} else if (ED_IS_SHORTCUT("filesystem_dock/show_in_explorer", p_event)) {
_tree_rmb_option(FILE_SHOW_IN_EXPLORER);
+ } else if (ED_IS_SHORTCUT("filesystem_dock/open_in_external_program", p_event)) {
+ _tree_rmb_option(FILE_OPEN_EXTERNAL);
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
focus_on_filter();
} else {
@@ -2943,12 +2945,19 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) {
}
}
-void FileSystemDock::_get_imported_files(const String &p_path, Vector<String> &r_files) const {
+bool FileSystemDock::_get_imported_files(const String &p_path, String &r_extension, Vector<String> &r_files) const {
if (!p_path.ends_with("/")) {
if (FileAccess::exists(p_path + ".import")) {
+ if (r_extension.is_empty()) {
+ r_extension = p_path.get_extension();
+ } else if (r_extension != p_path.get_extension()) {
+ r_files.clear();
+ return false; // File type mismatch, stop search.
+ }
+
r_files.push_back(p_path);
}
- return;
+ return true;
}
Ref<DirAccess> da = DirAccess::open(p_path);
@@ -2957,11 +2966,14 @@ void FileSystemDock::_get_imported_files(const String &p_path, Vector<String> &r
while (!n.is_empty()) {
if (n != "." && n != ".." && !n.ends_with(".import")) {
String npath = p_path + n + (da->current_is_dir() ? "/" : "");
- _get_imported_files(npath, r_files);
+ if (!_get_imported_files(npath, r_extension, r_files)) {
+ return false;
+ }
}
n = da->get_next();
}
da->list_dir_end();
+ return true;
}
void FileSystemDock::_update_import_dock() {
@@ -2986,10 +2998,16 @@ void FileSystemDock::_update_import_dock() {
}
}
- // Expand directory selection
+ if (!selected.is_empty() && selected[0] == "res://") {
+ // Scanning res:// is costly and unlikely to yield any useful results.
+ return;
+ }
+
+ // Expand directory selection.
Vector<String> efiles;
- for (int i = 0; i < selected.size(); i++) {
- _get_imported_files(selected[i], efiles);
+ String extension;
+ for (const String &fpath : selected) {
+ _get_imported_files(fpath, extension, efiles);
}
// Check import.
@@ -3103,6 +3121,7 @@ FileSystemDock::FileSystemDock() {
ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2);
ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER);
ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager"));
+ ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program"));
VBoxContainer *top_vbc = memnew(VBoxContainer);
add_child(top_vbc);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 08fb63dfe4..ede6869eea 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -214,7 +214,7 @@ private:
void _file_multi_selected(int p_index, bool p_selected);
void _tree_multi_selected(Object *p_item, int p_column, bool p_selected);
- void _get_imported_files(const String &p_path, Vector<String> &r_files) const;
+ bool _get_imported_files(const String &p_path, String &r_extension, Vector<String> &r_files) const;
void _update_import_dock();
void _get_all_items_in_dir(EditorFileSystemDirectory *p_efsd, Vector<String> &r_files, Vector<String> &r_folders) const;
diff --git a/editor/icons/BoneMapHumanBody.svg b/editor/icons/BoneMapHumanBody.svg
index 8674157aaa..818ee63069 100644
--- a/editor/icons/BoneMapHumanBody.svg
+++ b/editor/icons/BoneMapHumanBody.svg
@@ -1,26 +1 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
-<path fill="#3F3F3F" d="M0,0h1024v1024H0V0z"/>
-<path fill="#B2B2B2" d="M512,536.162c7,35,11.645,66.898,14,114c2,40,4,51,2,66c-7.384,55.369,6.77,183.898,8.666,206.667
- c2,24-7.653,24.241-10.666,46.333c-2.449,17.958,79,18.439,65-9c-25-49-2-84,4-221c0.521-11.921-8.967-47.874-2-94
- c11.086-73.414,8.42-107.242,6.5-145.662c-1.245-31.973-1-56.963-9-138.963c-0.976-10.002,5.915-79.268,11.954-79.088
- c42,1.25,97.313-5.009,118.145-14.68c28.901,3.73,97.81-12.047,127.887-16.126c14.541,9.407,16.673,3.335,37.515,9.019
- c5.5,1.5,17.336-1.443,12-5c-7.409-4.937-20.75-8.25-23-12c10.75-2.5,22.365-9.578,36-13.166c9.5-2.5,18.866-11.748,15.5-12.334l0,0
- c-11.5-2-26.03,4.547-37.5,6.5c-15.724,2.678-25.238,3.24-33.334,5.167c-1.227,0.292-3.103,0.763-5.791,0.958
- c0,0-0.02,0.16-0.053,0.437c-36.818,0.994-80.322-9.724-130.31-5.569c-34.026-3.925-94.181-5.16-113.513-5.493
- c-13.911-0.239-59.293-2.583-71.75-0.5c-0.668-4.083-1.5-9.75,0.949-16.468c14.881-7.246,19.188-17.796,27.301-34.694
- c0.922,4.424,6.252,4.929,12.459-14.231c5.661-17.478,2.323-22.254-2.313-22.525c0.172-2.056,0.279-4.105,0.313-6.142
- C573.746,76.562,566,42.163,512,42.163s-61.746,34.399-60.959,82.44c0.034,2.037,0.142,4.086,0.313,6.142
- c-4.637,0.271-7.975,5.047-2.313,22.525c6.207,19.16,11.537,18.655,12.459,14.231c8.112,16.898,12.42,27.448,27.301,34.694
- c2.449,6.718,1.617,12.385,0.949,16.468c-12.457-2.083-57.839,0.261-71.75,0.5c-19.332,0.333-79.486,1.568-113.513,5.493
- c-49.987-4.155-93.491,6.563-130.31,5.569c-0.033-0.277-0.053-0.437-0.053-0.437c-2.688-0.195-4.564-0.666-5.791-0.958
- c-8.096-1.927-17.61-2.489-33.334-5.167c-11.47-1.953-26-8.5-37.5-6.5l0,0c-3.366,0.586,6,9.834,15.5,12.334
- c13.635,3.588,25.25,10.666,36,13.166c-2.25,3.75-15.591,7.063-23,12c-5.336,3.557,6.5,6.5,12,5
- c20.842-5.684,22.974,0.388,37.515-9.019c30.077,4.079,98.985,19.857,127.887,16.126c20.832,9.671,76.145,15.93,118.145,14.68
- c6.039-0.18,12.93,69.085,11.954,79.088c-8,82-7.755,106.99-9,138.963c-1.92,38.419-4.586,72.248,6.5,145.662
- c6.967,46.126-2.521,82.079-2,94c6,137,29,172,4,221c-14,27.439,67.449,26.958,65,9c-3.013-22.092-12.666-22.333-10.666-46.333
- c1.896-22.769,16.05-151.298,8.666-206.667c-2-15,0-26,2-66C500.356,603.061,505,571.162,512,536.162z"/>
-</svg>
+<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_455)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M128 134.04C129.75 142.79 130.911 150.765 131.5 162.54C132 172.54 132.5 175.29 132 179.04C130.154 192.883 133.692 225.015 134.166 230.707C134.666 236.707 132.253 236.767 131.5 242.29C130.888 246.78 151.25 246.9 147.75 240.04C141.5 227.79 147.25 219.04 148.75 184.79C148.88 181.81 146.508 172.822 148.25 161.29C151.021 142.937 150.355 134.48 149.875 124.875C149.564 116.882 149.625 110.634 147.625 90.134C147.381 87.6335 149.104 70.317 150.613 70.362C161.113 70.6745 174.942 69.1098 180.15 66.692C187.375 67.6245 204.602 63.6803 212.121 62.6605C215.757 65.0123 216.29 63.4943 221.5 64.9153C222.875 65.2903 225.834 64.5545 224.5 63.6653C222.648 62.431 219.313 61.6028 218.75 60.6653C221.438 60.0403 224.341 58.2708 227.75 57.3738C230.125 56.7488 232.467 54.4368 231.625 54.2903C228.75 53.7903 225.118 55.427 222.25 55.9153C218.319 56.5848 215.941 56.7253 213.917 57.207C213.61 57.28 213.141 57.3978 212.469 57.4465C212.469 57.4465 212.464 57.4865 212.456 57.5558C203.251 57.8043 192.375 55.1248 179.878 56.1635C171.372 55.1823 156.333 54.8735 151.5 54.7903C148.022 54.7305 136.677 54.1445 133.562 54.6653C133.395 53.6445 133.187 52.2278 133.8 50.5483C137.52 48.7368 138.597 46.0993 140.625 41.8748C140.855 42.9808 142.188 43.107 143.74 38.317C145.155 33.9475 144.32 32.7535 143.161 32.6858C143.204 32.1718 143.231 31.6595 143.24 31.1503C143.436 19.1403 141.5 10.5405 128 10.5405C114.5 10.5405 112.563 19.1403 112.76 31.1505C112.769 31.6598 112.796 32.172 112.838 32.686C111.679 32.7538 110.845 33.9478 112.26 38.3173C113.812 43.1073 115.144 42.981 115.375 41.875C117.403 46.0995 118.48 48.737 122.2 50.5485C122.812 52.228 122.604 53.6448 122.437 54.6655C119.323 54.1448 107.978 54.7308 104.5 54.7905C99.6669 54.8738 84.6284 55.1825 76.1217 56.1638C63.6249 55.125 52.7489 57.8045 43.5442 57.556C43.5359 57.4868 43.5309 57.4468 43.5309 57.4468C42.8589 57.398 42.3899 57.2803 42.0832 57.2073C40.0592 56.7255 37.6807 56.585 33.7497 55.9155C30.8822 55.4273 27.2497 53.7905 24.3747 54.2905C23.5332 54.437 25.8747 56.749 28.2497 57.374C31.6584 58.271 34.5622 60.0405 37.2497 60.6655C36.6872 61.603 33.3519 62.4313 31.4997 63.6655C30.1657 64.5548 33.1247 65.2905 34.4997 64.9155C39.7102 63.4945 40.2432 65.0125 43.8784 62.6608C51.3977 63.6805 68.6247 67.625 75.8502 66.6923C81.0582 69.11 94.8864 70.6748 105.386 70.3623C106.896 70.3173 108.619 87.6335 108.375 90.1343C106.375 110.634 106.436 116.882 106.125 124.875C105.645 134.48 104.978 142.937 107.75 161.291C109.492 172.822 107.12 181.81 107.25 184.791C108.75 219.041 114.5 227.791 108.25 240.041C104.75 246.9 125.112 246.78 124.5 242.291C123.747 236.768 121.333 236.707 121.833 230.707C122.307 225.015 125.846 192.883 124 179.041C123.5 175.291 124 172.541 124.5 162.541C125.089 150.765 126.25 142.79 128 134.04Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_455"><rect width="256" height="256" fill="white"/></clipPath></defs></svg>
diff --git a/editor/icons/BoneMapHumanFace.svg b/editor/icons/BoneMapHumanFace.svg
index 6cb21140bc..e38c5cb790 100644
--- a/editor/icons/BoneMapHumanFace.svg
+++ b/editor/icons/BoneMapHumanFace.svg
@@ -1 +1 @@
-<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m788.105 552.967c17.995-57.892 31.896-124.566 30.875-198.071-3.758-270.403-249.846-251.479-295.568-244.947-359.868 51.409-219.047 452.358-220.453 496.426-4.899 153.499 83.686 170.991 161.665 215.554 2.646 1.512 7.259 1.786 13.313 1.111 7.223 25.179 11.762 59.035 9.548 75.638-3.266 24.495 209.021 24.495 209.021 0 0-62.883 12.233-124.363 33.827-188.89 7.143-2.284 16.054-7.601 25.963-16.95 13.681-12.908 34.839-21.774 45.726-63.145 15.615-59.338 3.869-76.074-13.917-76.726z" fill="#b2b2b2"/></svg>
+<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_458)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M197.026 138.242C201.525 123.769 205 107.1 204.745 88.724C203.805 21.1232 142.283 25.8542 130.853 27.4872C40.8859 40.3395 76.0912 140.577 75.7397 151.594C74.5149 189.968 96.6612 194.342 116.156 205.482C116.817 205.86 117.971 205.929 119.484 205.76C121.29 212.055 122.425 220.519 121.871 224.669C121.055 230.793 174.126 230.793 174.126 224.669C174.126 208.949 177.185 193.579 182.583 177.447C184.369 176.876 186.597 175.547 189.074 173.21C192.494 169.983 197.784 167.766 200.505 157.423C204.409 142.589 201.473 138.405 197.026 138.242Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_458"><rect width="256" height="256" fill="white"/></clipPath></defs></svg>
diff --git a/editor/icons/BoneMapHumanLeftHand.svg b/editor/icons/BoneMapHumanLeftHand.svg
index 08c68bb4be..a9861f818c 100644
--- a/editor/icons/BoneMapHumanLeftHand.svg
+++ b/editor/icons/BoneMapHumanLeftHand.svg
@@ -1 +1 @@
-<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m703.906 786.098c7.046-66.929 28.135-153.363 18.529-260.192-1.143-12.71-4.5-48.282-4.46-82.732.025-21.174-2.111-48.505-1.975-64.174.167-19.333-.428-41.584-.625-55.755-1.052-75.44-13.225-85.827-30.813-85.827-17.246 0-26.77 14.266-27.062 84.582-.061 14.42.5 51 .5 58.5 0 17.508-.333 34.167 0 53.5.447 25.955-4.279 68-9 68-3.902 0-8.099-39.299-9.575-76.999-.756-19.326-3.219-58.336-2.6-70.102 1.759-33.413.474-58.914 1.537-90.165 3.183-93.607-13.016-111.729-34.695-111.729-21.973 0-35.979 57.688-34.849 114.224.128 6.394-1.165 50.739.188 89.859.754 21.811-1.07 49.627-1.683 69.67-1.095 35.768-5.755 63.896-8.869 63.896-2.641 0-4.135-32.584-5.456-65.706-.859-21.557-4.468-58.477-3.664-83.616 1.886-59.012-1.139-110.226-1.063-121.501.635-94.955-14.66-123.101-36.052-123.101-21.476 0-37.188 30.192-36.6 123.343.067 10.53-2.62 99.926-1.759 121.816.865 21.992-2.773 65.062-3.517 84.818-1.299 34.521-6.49 63.947-9.124 63.947-3.281 0-10.794-25.638-11.724-60.965-.587-22.275 1.231-50.99.624-70.688-1.257-40.707-3.175-64.631-3.877-99.708-1.945-97.182-16.352-106.289-38.142-106.289-17.957 0-32.453 28.673-32.657 115.03-.065 27.702-2.429 62.626-.315 94.329.805 12.081-.622 42.512-1.875 73.894-.799 20.007-1.102 47.501-1.137 63.775-.17 78.595-26.712 133.424-36.555 131.308-30.333-6.521-51.648-43.918-71.219-117.307-10.551-39.566-36.667-71.149-69.9-77.813-25.9-5.193-19.783 46.161-1.319 125.293 8.65 37.068 27.909 86.227 39.566 122.655 31.653 98.917 125.574 188.563 160.903 228.546 17.146 19.403 236.894 19.403 264.59 0 11.525-8.07 43.087-101.557 45.724-126.616z" fill="#b2b2b2"/></svg>
+<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_461)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M175.976 196.525C177.738 179.792 183.01 158.184 180.609 131.477C180.323 128.299 179.484 119.406 179.494 110.794C179.5 105.5 178.966 98.6674 179 94.7501C179.042 89.9169 178.893 84.3541 178.844 80.8114C178.581 61.9514 175.537 59.3546 171.14 59.3546C166.829 59.3546 164.448 62.9211 164.375 80.5001C164.36 84.1051 164.5 93.2501 164.5 95.1251C164.5 99.5021 164.417 103.667 164.5 108.5C164.612 114.989 163.43 125.5 162.25 125.5C161.274 125.5 160.225 115.675 159.856 106.25C159.667 101.419 159.051 91.6664 159.206 88.7249C159.646 80.3716 159.325 73.9964 159.59 66.1836C160.386 42.7819 156.336 38.2514 150.917 38.2514C145.423 38.2514 141.922 52.6734 142.204 66.8074C142.236 68.4059 141.913 79.4921 142.251 89.2721C142.44 94.7249 141.984 101.679 141.831 106.69C141.557 115.632 140.392 122.664 139.613 122.664C138.953 122.664 138.58 114.518 138.249 106.237C138.035 100.848 137.132 91.6179 137.333 85.3331C137.805 70.5801 137.049 57.7766 137.068 54.9579C137.226 31.2191 133.403 24.1826 128.055 24.1826C122.686 24.1826 118.758 31.7306 118.905 55.0184C118.921 57.6509 118.25 79.9999 118.465 85.4724C118.681 90.9704 117.772 101.738 117.586 106.677C117.261 115.307 115.963 122.664 115.305 122.664C114.484 122.664 112.606 116.254 112.374 107.422C112.227 101.854 112.681 94.6749 112.53 89.7504C112.215 79.5736 111.736 73.5926 111.56 64.8234C111.074 40.5279 107.472 38.2511 102.025 38.2511C97.5357 38.2511 93.9117 45.4194 93.8607 67.0086C93.8445 73.9341 93.2535 82.6651 93.782 90.5909C93.9832 93.6111 93.6265 101.219 93.3132 109.064C93.1135 114.066 93.0377 120.94 93.029 125.008C92.9865 144.657 86.351 158.364 83.8902 157.835C76.307 156.205 70.9782 146.856 66.0855 128.508C63.4477 118.617 56.9187 110.721 48.6105 109.055C42.1355 107.757 43.6647 120.595 48.2807 140.378C50.4432 149.645 55.258 161.935 58.1722 171.042C66.0855 195.771 89.5657 218.183 98.398 228.179C102.684 233.029 157.621 233.029 164.545 228.179C167.427 226.161 175.317 202.789 175.976 196.525Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_461"><rect width="256" height="256" fill="white"/></clipPath></defs></svg>
diff --git a/editor/icons/BoneMapHumanRightHand.svg b/editor/icons/BoneMapHumanRightHand.svg
index 4e40af35d8..e92898152f 100644
--- a/editor/icons/BoneMapHumanRightHand.svg
+++ b/editor/icons/BoneMapHumanRightHand.svg
@@ -1 +1 @@
-<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m320.094 786.098c-7.046-66.929-28.135-153.363-18.529-260.192 1.143-12.71 4.5-48.282 4.46-82.732-.025-21.174 2.111-48.505 1.975-64.174-.167-19.333.428-41.584.625-55.755 1.052-75.44 13.225-85.827 30.813-85.827 17.246 0 26.77 14.266 27.062 84.582.061 14.42-.5 51-.5 58.5 0 17.508.333 34.167 0 53.5-.447 25.955 4.279 68 9 68 3.902 0 8.099-39.299 9.575-76.999.756-19.326 3.219-58.336 2.6-70.102-1.759-33.413-.474-58.914-1.537-90.165-3.183-93.607 13.016-111.729 34.695-111.729 21.973 0 35.979 57.688 34.849 114.224-.128 6.394 1.165 50.739-.188 89.859-.754 21.811 1.07 49.627 1.683 69.67 1.095 35.768 5.755 63.896 8.869 63.896 2.641 0 4.135-32.584 5.456-65.706.859-21.557 4.468-58.477 3.664-83.616-1.886-59.012 1.139-110.226 1.063-121.501-.635-94.955 14.66-123.101 36.052-123.101 21.476 0 37.188 30.192 36.6 123.343-.067 10.53 2.62 99.926 1.759 121.816-.865 21.992 2.773 65.062 3.517 84.818 1.299 34.521 6.49 63.947 9.124 63.947 3.281 0 10.794-25.638 11.724-60.965.587-22.275-1.231-50.99-.624-70.688 1.257-40.707 3.176-64.631 3.877-99.708 1.945-97.182 16.352-106.289 38.142-106.289 17.957 0 32.453 28.673 32.657 115.03.065 27.702 2.429 62.626.314 94.329-.805 12.081.622 42.512 1.875 73.894.799 20.007 1.102 47.501 1.137 63.775.171 78.595 26.713 133.424 36.556 131.308 30.333-6.521 51.648-43.918 71.219-117.307 10.551-39.566 36.667-71.149 69.9-77.813 25.9-5.193 19.783 46.161 1.318 125.293-8.649 37.068-27.909 86.227-39.566 122.655-31.652 98.917-125.573 188.563-160.902 228.546-17.146 19.403-236.894 19.403-264.59 0-11.525-8.07-43.087-101.557-45.724-126.616z" fill="#b2b2b2"/></svg>
+<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_464)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M80.0233 196.525C78.2618 179.792 72.9896 158.184 75.3911 131.477C75.6768 128.299 76.5161 119.406 76.5061 110.794C76.4998 105.5 77.0338 98.6674 76.9998 94.7501C76.9581 89.9169 77.1068 84.3541 77.1561 80.8114C77.4191 61.9514 80.4623 59.3546 84.8593 59.3546C89.1708 59.3546 91.5518 62.9211 91.6248 80.5001C91.6401 84.1051 91.4998 93.2501 91.4998 95.1251C91.4998 99.5021 91.5831 103.667 91.4998 108.5C91.3881 114.989 92.5696 125.5 93.7498 125.5C94.7253 125.5 95.7746 115.675 96.1436 106.25C96.3326 101.419 96.9483 91.6664 96.7935 88.7249C96.3538 80.3716 96.6751 73.9964 96.4093 66.1836C95.6136 42.7819 99.6633 38.2514 105.083 38.2514C110.576 38.2514 114.078 52.6734 113.795 66.8074C113.763 68.4059 114.087 79.4921 113.748 89.2721C113.56 94.7249 114.016 101.679 114.169 106.69C114.443 115.632 115.608 122.664 116.386 122.664C117.047 122.664 117.42 114.518 117.75 106.237C117.965 100.848 118.867 91.6179 118.666 85.3331C118.195 70.5801 118.951 57.7766 118.932 54.9579C118.773 31.2191 122.597 24.1826 127.945 24.1826C133.314 24.1826 137.242 31.7306 137.095 55.0184C137.078 57.6509 137.75 79.9999 137.535 85.4724C137.319 90.9704 138.228 101.738 138.414 106.677C138.739 115.307 140.037 122.664 140.695 122.664C141.515 122.664 143.394 116.254 143.626 107.422C143.773 101.854 143.318 94.6749 143.47 89.7504C143.784 79.5736 144.264 73.5926 144.439 64.8234C144.926 40.5279 148.527 38.2511 153.975 38.2511C158.464 38.2511 162.088 45.4194 162.139 67.0086C162.155 73.9341 162.746 82.6651 162.218 90.5909C162.016 93.6111 162.373 101.219 162.686 109.064C162.886 114.066 162.962 120.94 162.971 125.008C163.013 144.657 169.649 158.364 172.11 157.835C179.693 156.205 185.022 146.856 189.914 128.508C192.552 118.617 199.081 110.721 207.389 109.055C213.864 107.757 212.335 120.595 207.719 140.378C205.557 149.645 200.742 161.935 197.827 171.042C189.914 195.771 166.434 218.183 157.602 228.179C153.315 233.029 98.3783 233.029 91.4543 228.179C88.5731 226.161 80.6826 202.789 80.0233 196.525Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_464"><rect width="256" height="256" fill="white"/></clipPath></defs></svg>
diff --git a/editor/icons/Keyboard.svg b/editor/icons/Keyboard.svg
index b9dfab71ed..b6d963f9d7 100644
--- a/editor/icons/Keyboard.svg
+++ b/editor/icons/Keyboard.svg
@@ -1 +1 @@
-<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".996"><path d="m4 2a1 1 0 0 0 -1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916v-9.084a1 1 0 0 0 -1-1zm1.543 1.139h1.393l1.834 4.199h1.295v.437c.708.052 1.246.239 1.61.559.368.316.55.747.55 1.295 0 .552-.182.99-.55 1.314-.368.32-.906.505-1.61.553v.467h-1.294v-.473c-.708-.06-1.247-.248-1.615-.564-.364-.316-.545-.75-.545-1.297 0-.548.181-.977.545-1.29.368-.315.907-.504 1.615-.564v-.437h-1.464l-.282-.733h-1.595l-.284.733h-1.439l1.836-4.2zm.684 1.39-.409 1.057h.817zm3.84 4.338v1.526c.28-.04.483-.12.607-.24.124-.125.185-.302.185-.53 0-.224-.063-.396-.191-.516-.124-.12-.326-.2-.602-.24zm-1.296.006c-.284.04-.487.12-.61.24-.12.116-.182.288-.182.516 0 .22.065.392.193.512.132.12.331.202.6.246v-1.514z" fill="#e0e0e0"/><path d="m27 2h7v14h-7z" fill="#fff"/><path d="m1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9z" fill="#e0e0e0"/></g></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path d="M6.584 5.135 6.17 4.059l-.412 1.076h.826zm3.203 2.976a1.032 1.032 0 0 0-.287.041.953.953 0 0 0-.09.034c-.028.012-.057.024-.084.039a.912.912 0 0 0-.152.107.988.988 0 0 0-.23.305.937.937 0 0 0-.04.097 1.017 1.017 0 0 0-.068.323 1.553 1.553 0 0 0 0 .244 1.374 1.374 0 0 0 .068.328 1.03 1.03 0 0 0 .201.336c.023.022.045.044.069.064a.96.96 0 0 0 .152.104c.027.015.056.027.084.039.03.012.06.024.09.033a.965.965 0 0 0 .19.035 1.028 1.028 0 0 0 .197 0 .974.974 0 0 0 .36-.107.876.876 0 0 0 .077-.049.872.872 0 0 0 .074-.055.882.882 0 0 0 .13-.136.978.978 0 0 0 .177-.368 1.225 1.225 0 0 0 .035-.224 1.61 1.61 0 0 0 0-.244 1.361 1.361 0 0 0-.035-.223 1.092 1.092 0 0 0-.121-.285.87.87 0 0 0-.12-.15.862.862 0 0 0-.14-.124c-.025-.017-.05-.035-.078-.05-.027-.015-.056-.027-.086-.04a.892.892 0 0 0-.373-.074z" style="fill-opacity:.99607843;fill:#e0e0e0"/><path d="M4 2c-.616-.02-1.084.59-1 1.178.003 3-.007 6 .005 9 .057.577.672.889 1.203.822 2.631-.003 5.263.006 7.894-.005a.973.973 0 0 0 .898-1.09c-.003-3.002.007-6.005-.005-9.007-.042-.592-.643-.976-1.203-.898H4Zm1.475.646H6.89l1.865 4.27H7.268l-.286-.744H5.36l-.287.744H3.61l1.866-4.27Zm4.312 4.301c1.069-.042 2.164.679 2.363 1.766.232 1.01-.34 2.144-1.326 2.502.296.465.837-.109 1.06-.007l.544.642c-.64.756-1.883.677-2.605.084-.394-.448-.866-.673-1.409-.887-1.175-.66-1.391-2.456-.43-3.39.463-.486 1.141-.716 1.803-.71Z" style="fill:#e0e0e0;fill-opacity:.996078"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:.996"/></svg>
diff --git a/editor/icons/KeyboardError.svg b/editor/icons/KeyboardError.svg
new file mode 100644
index 0000000000..e20d133155
--- /dev/null
+++ b/editor/icons/KeyboardError.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path d="M4 2c-.616-.02-1.084.59-1 1.178.003 3-.007 6 .005 9 .057.577.672.889 1.203.822 2.631-.003 5.263.006 7.894-.005a.973.973 0 0 0 .898-1.09c-.003-3.002.007-6.005-.005-9.007-.042-.592-.643-.976-1.203-.898H4Zm4.223 1.262c1.06.005 2.29.257 2.92 1.197.532.862.275 2.057-.484 2.703-.346.382-.862.629-1.075 1.117.055.345-.33.172-.537.213H7.148c-.037-.749.503-1.335 1.026-1.796.406-.253.744-1.002.129-1.22-.626-.25-1.374.117-1.645.715l-2.08-1.039c.599-1.147 1.868-1.818 3.136-1.872.17-.013.339-.018.509-.018Zm.127 5.697c.798-.057 1.616.616 1.54 1.45-.023.81-.841 1.413-1.623 1.328-.833.022-1.6-.771-1.443-1.613.097-.721.83-1.195 1.526-1.165z" style="fill:#ff5f5f;fill-opacity:1"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:1;fill:#ff5f5f"/></svg>
diff --git a/editor/icons/KeyboardLabel.svg b/editor/icons/KeyboardLabel.svg
new file mode 100644
index 0000000000..07a687f447
--- /dev/null
+++ b/editor/icons/KeyboardLabel.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M11.814 1.977c-.078 0-.157.008-.232.023H4c-.7-.034-1.143.765-1 1.39.008 2.95-.017 5.9.014 8.848.13.705.965.847 1.562.76 2.542-.008 5.085.02 7.627-.014.734-.1.9-.952.797-1.564-.003-2.84.006-5.68-.006-8.522-.035-.594-.628-.927-1.18-.921zM9.797 4.016l.572.58-.572.578-.57-.578.57-.58zm-.606 1.05.594.606-.594.601-.591-.601.591-.606zm1.213 0 .596.606-.596.601-.593-.601.593-.606zm.717 1.436h1.06c.053.217.093.428.122.63.028.201.043.395.043.58a2.363 2.363 0 0 1-.133.724 1.425 1.425 0 0 1-.31.515c-.249.265-.598.399-1.05.399-.331 0-.594-.08-.785-.235a1.091 1.091 0 0 1-.236-.275c-.063.1-.14.19-.236.265-.206.163-.467.245-.787.245-.252 0-.457-.057-.614-.166a2.75 2.75 0 0 1-.095.42 1.936 1.936 0 0 1-.403.722c-.2.22-.452.383-.756.49-.303.11-.654.166-1.052.166-.466 0-.865-.089-1.2-.265a1.817 1.817 0 0 1-.765-.752c-.18-.327-.27-.715-.27-1.164 0-.256.027-.525.082-.809.055-.284.126-.545.21-.781h1.001c-.062.232-.112.46-.15.684a3.87 3.87 0 0 0-.053.613c0 .37.1.643.3.82.204.177.523.264.96.264.222 0 .425-.03.61-.092a.97.97 0 0 0 .439-.299.803.803 0 0 0 .166-.521 5.463 5.463 0 0 0-.051-.725 11.61 11.61 0 0 0-.068-.482 26.51 26.51 0 0 0-.096-.606h1.135l.043.276c.047.32.123.532.228.634.105.1.24.15.402.15.165 0 .284-.04.358-.124.076-.086.115-.224.115-.412v-.524h1.027v.524c0 .203.032.351.096.447.065.095.202.142.412.142a.637.637 0 0 0 .33-.078c.089-.052.133-.15.133-.297 0-.128-.019-.295-.054-.498l-.108-.605z" style="fill:#e0e0e0;fill-opacity:.996078"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:.996"/></svg>
diff --git a/editor/icons/KeyboardPhysical.svg b/editor/icons/KeyboardPhysical.svg
index 4364e0b4fa..7d4b7e2999 100644
--- a/editor/icons/KeyboardPhysical.svg
+++ b/editor/icons/KeyboardPhysical.svg
@@ -1 +1 @@
-<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".996"><path d="m4 2a1 1 0 0 0 -1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916v-9.084a1 1 0 0 0 -1-1zm2.762 1.768h2.476l3.264 7.464h-2.604l-.502-1.3h-2.835l-.502 1.3h-2.561zm1.217 2.474-.725 1.878h1.45z" fill="#e0e0e0"/><path d="m27 2h7v14h-7z" fill="#fff"/><path d="m1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9z" fill="#e0e0e0"/></g></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M4 2a1 1 0 0 0-1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916V3a1 1 0 0 0-1-1Zm2.762 1.768h2.476l3.264 7.464H9.898l-.502-1.3H6.561l-.502 1.3H3.498Zm1.217 2.474L7.254 8.12h1.45z" style="fill-opacity:.996"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:.996"/></svg>
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 4b0ece3367..1ffede6502 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -734,7 +734,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
/* CREATE PRIMITIVE ARRAY */
/**************************/
- // The way collada uses indices is more optimal, and friendlier with 3D modelling software,
+ // The way collada uses indices is more optimal, and friendlier with 3D modeling software,
// because it can index everything, not only vertices (similar to how the WII works).
// This is, however, more incompatible with standard video cards, so arrays must be converted.
// Must convert to GL/DX format.
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index 08d4bfff4a..48e3759e57 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -38,9 +38,10 @@
#include "scene/gui/separator.h"
#include "scene/gui/tree.h"
-void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection) {
+void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection) {
if (p_event.is_valid()) {
event = p_event;
+ original_event = p_original_event;
// If the event is changed to something which is not the same as the listener,
// clear out the event from the listener text box to avoid confusion.
@@ -61,7 +62,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
// Update option values and visibility
bool show_mods = false;
bool show_device = false;
- bool show_phys_key = false;
+ bool show_key = false;
if (mod.is_valid()) {
show_mods = true;
@@ -74,9 +75,25 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
}
if (k.is_valid()) {
- show_phys_key = true;
- physical_key_checkbox->set_pressed(k->get_physical_keycode() != Key::NONE && k->get_keycode() == Key::NONE);
-
+ show_key = true;
+ if (k->get_keycode() == Key::NONE && k->get_physical_keycode() == Key::NONE && k->get_key_label() != Key::NONE) {
+ key_mode->select(KEYMODE_UNICODE);
+ } else if (k->get_keycode() != Key::NONE) {
+ key_mode->select(KEYMODE_KEYCODE);
+ } else if (k->get_physical_keycode() != Key::NONE) {
+ key_mode->select(KEYMODE_PHY_KEYCODE);
+ } else {
+ // Invalid key.
+ event = Ref<InputEvent>();
+ original_event = Ref<InputEvent>();
+ event_listener->clear_event();
+ event_as_text->set_text(TTR("No Event Configured"));
+
+ additional_options_container->hide();
+ input_list_tree->deselect_all();
+ _update_input_list();
+ return;
+ }
} else if (joyb.is_valid() || joym.is_valid() || mb.is_valid()) {
show_device = true;
_set_current_device(event->get_device());
@@ -84,11 +101,20 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
mod_container->set_visible(show_mods);
device_container->set_visible(show_device);
- physical_key_checkbox->set_visible(show_phys_key);
+ key_mode->set_visible(show_key);
additional_options_container->show();
+ // Update mode selector based on original key event.
+ Ref<InputEventKey> ko = p_original_event;
+ if (ko.is_valid()) {
+ key_mode->set_item_disabled(KEYMODE_KEYCODE, ko->get_keycode() == Key::NONE);
+ key_mode->set_item_disabled(KEYMODE_PHY_KEYCODE, ko->get_physical_keycode() == Key::NONE);
+ key_mode->set_item_disabled(KEYMODE_UNICODE, ko->get_key_label() == Key::NONE);
+ }
+
// Update selected item in input list.
if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) {
+ in_tree_update = true;
TreeItem *category = input_list_tree->get_root()->get_first_child();
while (category) {
TreeItem *input_item = category->get_first_child();
@@ -97,6 +123,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
// input_type should always be > 0, unless the tree structure has been misconfigured.
int input_type = input_item->get_parent()->get_meta("__type", 0);
if (input_type == 0) {
+ in_tree_update = false;
return;
}
@@ -112,6 +139,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
category->set_collapsed(false);
input_item->select(0);
input_list_tree->ensure_cursor_is_visible();
+ in_tree_update = false;
return;
}
input_item = input_item->get_next();
@@ -122,10 +150,12 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
category->set_collapsed(true); // Event not in this category, so collapse;
category = category->get_next();
}
+ in_tree_update = false;
}
} else {
// Event is not valid, reset dialog
- event = p_event;
+ event = Ref<InputEvent>();
+ original_event = Ref<InputEvent>();
event_listener->clear_event();
event_as_text->set_text(TTR("No Event Configured"));
@@ -141,8 +171,10 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven
return;
}
- // Create an editable reference
+ // Create an editable reference and a copy of full event.
Ref<InputEvent> received_event = p_event;
+ Ref<InputEvent> received_original_event = received_event->duplicate();
+
// Check what the type is and if it is allowed.
Ref<InputEventKey> k = received_event;
Ref<InputEventJoypadButton> joyb = received_event;
@@ -170,11 +202,15 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven
if (k.is_valid()) {
k->set_pressed(false); // To avoid serialisation of 'pressed' property - doesn't matter for actions anyway.
- // Maintain physical keycode option state
- if (physical_key_checkbox->is_pressed()) {
+ if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {
+ k->set_physical_keycode(Key::NONE);
+ k->set_key_label(Key::NONE);
+ } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {
k->set_keycode(Key::NONE);
- } else {
+ k->set_key_label(Key::NONE);
+ } else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {
k->set_physical_keycode(Key::NONE);
+ k->set_keycode(Key::NONE);
}
}
@@ -186,7 +222,7 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven
// Maintain device selection.
received_event->set_device(_get_current_device());
- _set_event(received_event);
+ _set_event(received_event, received_original_event);
}
void InputEventConfigurationDialog::_on_listen_focus_changed() {
@@ -326,14 +362,14 @@ void InputEventConfigurationDialog::_mod_toggled(bool p_checked, int p_index) {
}
}
- _set_event(ie);
+ _set_event(ie, original_event);
}
void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p_checked) {
Ref<InputEventWithModifiers> ie = event;
if (ie.is_valid()) {
ie->set_command_or_control_autoremap(p_checked);
- _set_event(ie);
+ _set_event(ie, original_event);
}
if (p_checked) {
@@ -345,27 +381,38 @@ void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p
}
}
-void InputEventConfigurationDialog::_physical_keycode_toggled(bool p_checked) {
+void InputEventConfigurationDialog::_key_mode_selected(int p_mode) {
Ref<InputEventKey> k = event;
-
- if (k.is_null()) {
+ Ref<InputEventKey> ko = original_event;
+ if (k.is_null() || ko.is_null()) {
return;
}
- if (p_checked) {
- k->set_physical_keycode(k->get_keycode());
+ if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {
+ k->set_keycode(ko->get_keycode());
+ k->set_physical_keycode(Key::NONE);
+ k->set_key_label(Key::NONE);
+ } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {
k->set_keycode(Key::NONE);
- } else {
- k->set_keycode((Key)k->get_physical_keycode());
+ k->set_physical_keycode(ko->get_physical_keycode());
+ k->set_key_label(Key::NONE);
+ } else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {
k->set_physical_keycode(Key::NONE);
+ k->set_keycode(Key::NONE);
+ k->set_key_label(ko->get_key_label());
}
- _set_event(k);
+ _set_event(k, original_event);
}
void InputEventConfigurationDialog::_input_list_item_selected() {
TreeItem *selected = input_list_tree->get_selected();
+ // Called form _set_event, do not update for a second time.
+ if (in_tree_update) {
+ return;
+ }
+
// Invalid tree selection - type only exists on the "category" items, which are not a valid selection.
if (selected->has_meta("__type")) {
return;
@@ -379,15 +426,11 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
Ref<InputEventKey> k;
k.instantiate();
- if (physical_key_checkbox->is_pressed()) {
- k->set_physical_keycode(keycode);
- k->set_keycode(Key::NONE);
- } else {
- k->set_physical_keycode(Key::NONE);
- k->set_keycode(keycode);
- }
+ k->set_physical_keycode(keycode);
+ k->set_keycode(keycode);
+ k->set_key_label(keycode);
- // Maintain modifier state from checkboxes
+ // Maintain modifier state from checkboxes.
k->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());
k->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());
if (autoremap_command_or_control_checkbox->is_pressed()) {
@@ -397,7 +440,23 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
k->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());
}
- _set_event(k, false);
+ Ref<InputEventKey> ko = k->duplicate();
+
+ if (key_mode->get_selected_id() == KEYMODE_UNICODE) {
+ key_mode->select(KEYMODE_PHY_KEYCODE);
+ }
+
+ if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {
+ k->set_physical_keycode(Key::NONE);
+ k->set_keycode(keycode);
+ k->set_key_label(Key::NONE);
+ } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {
+ k->set_physical_keycode(keycode);
+ k->set_keycode(Key::NONE);
+ k->set_key_label(Key::NONE);
+ }
+
+ _set_event(k, ko, false);
} break;
case INPUT_MOUSE_BUTTON: {
MouseButton idx = (MouseButton)(int)selected->get_meta("__index");
@@ -417,7 +476,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
// Maintain selected device
mb->set_device(_get_current_device());
- _set_event(mb, false);
+ _set_event(mb, mb, false);
} break;
case INPUT_JOY_BUTTON: {
JoyButton idx = (JoyButton)(int)selected->get_meta("__index");
@@ -426,7 +485,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
// Maintain selected device
jb->set_device(_get_current_device());
- _set_event(jb, false);
+ _set_event(jb, jb, false);
} break;
case INPUT_JOY_MOTION: {
JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis");
@@ -440,7 +499,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
// Maintain selected device
jm->set_device(_get_current_device());
- _set_event(jm, false);
+ _set_event(jm, jm, false);
} break;
}
}
@@ -470,7 +529,9 @@ void InputEventConfigurationDialog::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
input_list_search->set_right_icon(input_list_search->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- physical_key_checkbox->set_icon(get_theme_icon(SNAME("KeyboardPhysical"), SNAME("EditorIcons")));
+ key_mode->set_item_icon(KEYMODE_KEYCODE, get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons")));
+ key_mode->set_item_icon(KEYMODE_PHY_KEYCODE, get_theme_icon(SNAME("KeyboardPhysical"), SNAME("EditorIcons")));
+ key_mode->set_item_icon(KEYMODE_UNICODE, get_theme_icon(SNAME("KeyboardLabel"), SNAME("EditorIcons")));
icon_cache.keyboard = get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons"));
icon_cache.mouse = get_theme_icon(SNAME("Mouse"), SNAME("EditorIcons"));
@@ -484,22 +545,22 @@ void InputEventConfigurationDialog::_notification(int p_what) {
void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event) {
if (p_event.is_valid()) {
- _set_event(p_event);
+ _set_event(p_event, p_event->duplicate());
} else {
// Clear Event
- _set_event(p_event);
+ _set_event(Ref<InputEvent>(), Ref<InputEvent>());
// Clear Checkbox Values
for (int i = 0; i < MOD_MAX; i++) {
mod_checkboxes[i]->set_pressed(false);
}
- // Enable the Physical Key checkbox by default to encourage its use.
+ // Enable the Physical Key by default to encourage its use.
// Physical Key should be used for most game inputs as it allows keys to work
// on non-QWERTY layouts out of the box.
// This is especially important for WASD movement layouts.
- physical_key_checkbox->set_pressed(true);
+ key_mode->select(KEYMODE_PHY_KEYCODE);
autoremap_command_or_control_checkbox->set_pressed(false);
// Select "All Devices" by default.
@@ -621,14 +682,15 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
mod_container->hide();
additional_options_container->add_child(mod_container);
- // Physical Key Checkbox
+ // Key Mode Selection
- physical_key_checkbox = memnew(CheckBox);
- physical_key_checkbox->set_text(TTR("Use Physical Keycode"));
- physical_key_checkbox->set_tooltip_text(TTR("Stores the physical position of the key on the keyboard rather than the key's value. Used for compatibility with non-latin layouts.\nThis should generally be enabled for most game shortcuts, but not in non-game applications."));
- physical_key_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_physical_keycode_toggled));
- physical_key_checkbox->hide();
- additional_options_container->add_child(physical_key_checkbox);
+ key_mode = memnew(OptionButton);
+ key_mode->add_item("Keycode (Latin equvialent)", KEYMODE_KEYCODE);
+ key_mode->add_item("Physical Keycode (poistion of US QWERTY keyboard)", KEYMODE_PHY_KEYCODE);
+ key_mode->add_item("Unicode (case-insencetive)", KEYMODE_UNICODE);
+ key_mode->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected));
+ key_mode->hide();
+ additional_options_container->add_child(key_mode);
main_vbox->add_child(additional_options_container);
}
diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h
index 67906233dd..e7ab0da4d6 100644
--- a/editor/input_event_configuration_dialog.h
+++ b/editor/input_event_configuration_dialog.h
@@ -50,7 +50,10 @@ private:
Ref<Texture2D> joypad_axis;
} icon_cache;
- Ref<InputEvent> event = Ref<InputEvent>();
+ Ref<InputEvent> event;
+ Ref<InputEvent> original_event;
+
+ bool in_tree_update = false;
// Listening for input
EventListenerLineEdit *event_listener = nullptr;
@@ -88,9 +91,15 @@ private:
CheckBox *mod_checkboxes[MOD_MAX];
CheckBox *autoremap_command_or_control_checkbox = nullptr;
- CheckBox *physical_key_checkbox = nullptr;
+ enum KeyMode {
+ KEYMODE_KEYCODE,
+ KEYMODE_PHY_KEYCODE,
+ KEYMODE_UNICODE,
+ };
+
+ OptionButton *key_mode = nullptr;
- void _set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection = true);
+ void _set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection = true);
void _on_listen_input_changed(const Ref<InputEvent> &p_event);
void _on_listen_focus_changed();
@@ -100,7 +109,7 @@ private:
void _mod_toggled(bool p_checked, int p_index);
void _autoremap_command_or_control_toggled(bool p_checked);
- void _physical_keycode_toggled(bool p_checked);
+ void _key_mode_selected(int p_mode);
void _device_selection_changed(int p_option_button_index);
void _set_current_device(int p_device);
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index 4f6487fa77..97398b8b69 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -92,7 +92,7 @@ void PluginConfigDialog::_on_confirmed() {
_clear_fields();
}
-void PluginConfigDialog::_on_cancelled() {
+void PluginConfigDialog::_on_canceled() {
_clear_fields();
}
@@ -162,7 +162,7 @@ void PluginConfigDialog::_notification(int p_what) {
case NOTIFICATION_READY: {
connect("confirmed", callable_mp(this, &PluginConfigDialog::_on_confirmed));
- get_cancel_button()->connect("pressed", callable_mp(this, &PluginConfigDialog::_on_cancelled));
+ get_cancel_button()->connect("pressed", callable_mp(this, &PluginConfigDialog::_on_canceled));
} break;
}
}
diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h
index e8df308c54..50b901a39e 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugin_config_dialog.h
@@ -58,7 +58,7 @@ class PluginConfigDialog : public ConfirmationDialog {
void _clear_fields();
void _on_confirmed();
- void _on_cancelled();
+ void _on_canceled();
void _on_language_changed(const int p_language);
void _on_required_text_changed(const String &p_text);
String _get_subfolder();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 9806cf8555..5d0555a10e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -246,7 +246,7 @@ public:
}
};
-bool CanvasItemEditor::_is_node_locked(const Node *p_node) {
+bool CanvasItemEditor::_is_node_locked(const Node *p_node) const {
return p_node->get_meta("_edit_lock_", false);
}
@@ -770,7 +770,7 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
return still_selected;
}
-List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) {
+List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) const {
List<CanvasItem *> selection;
for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
CanvasItem *ci = Object::cast_to<CanvasItem>(E.key);
@@ -1226,7 +1226,6 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
bool panner_active = panner->gui_input(p_event, warped_panning ? viewport->get_global_rect() : Rect2());
if (panner->is_panning() != pan_pressed) {
pan_pressed = panner->is_panning();
- _update_cursor();
}
if (panner_active) {
@@ -2570,8 +2569,10 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
// Handles the mouse hovering
_gui_input_hover(p_event);
- // Change the cursor
- _update_cursor();
+ if (mb.is_valid()) {
+ // Update the default cursor.
+ _update_cursor();
+ }
// Grab focus
if (!viewport->has_focus() && (!get_viewport()->gui_get_focus_owner() || !get_viewport()->gui_get_focus_owner()->is_text_field())) {
@@ -2580,6 +2581,31 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
}
void CanvasItemEditor::_update_cursor() {
+ // Choose the correct default cursor.
+ CursorShape c = CURSOR_ARROW;
+ switch (tool) {
+ case TOOL_MOVE:
+ c = CURSOR_MOVE;
+ break;
+ case TOOL_EDIT_PIVOT:
+ c = CURSOR_CROSS;
+ break;
+ case TOOL_PAN:
+ c = CURSOR_DRAG;
+ break;
+ case TOOL_RULER:
+ c = CURSOR_CROSS;
+ break;
+ default:
+ break;
+ }
+ if (pan_pressed) {
+ c = CURSOR_DRAG;
+ }
+ set_default_cursor_shape(c);
+}
+
+Control::CursorShape CanvasItemEditor::get_cursor_shape(const Point2 &p_pos) const {
// Compute an eventual rotation of the cursor
const CursorShape rotation_array[4] = { CURSOR_HSIZE, CURSOR_BDIAGSIZE, CURSOR_VSIZE, CURSOR_FDIAGSIZE };
int rotation_array_index = 0;
@@ -2601,26 +2627,8 @@ void CanvasItemEditor::_update_cursor() {
}
// Choose the correct cursor
- CursorShape c = CURSOR_ARROW;
+ CursorShape c = get_default_cursor_shape();
switch (drag_type) {
- case DRAG_NONE:
- switch (tool) {
- case TOOL_MOVE:
- c = CURSOR_MOVE;
- break;
- case TOOL_EDIT_PIVOT:
- c = CURSOR_CROSS;
- break;
- case TOOL_PAN:
- c = CURSOR_DRAG;
- break;
- case TOOL_RULER:
- c = CURSOR_CROSS;
- break;
- default:
- break;
- }
- break;
case DRAG_LEFT:
case DRAG_RIGHT:
c = rotation_array[rotation_array_index];
@@ -2662,16 +2670,7 @@ void CanvasItemEditor::_update_cursor() {
if (pan_pressed) {
c = CURSOR_DRAG;
}
-
- if (c != viewport->get_default_cursor_shape()) {
- viewport->set_default_cursor_shape(c);
-
- // Force refresh cursor if it's over the viewport.
- if (viewport->get_global_rect().has_point(get_global_mouse_position())) {
- DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)viewport->get_default_cursor_shape();
- DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
- }
- }
+ return c;
}
void CanvasItemEditor::_draw_text_at_position(Point2 p_position, String p_string, Side p_side) {
@@ -3990,6 +3989,10 @@ void CanvasItemEditor::_selection_changed() {
}
void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
+ if (!p_canvas_item) {
+ return;
+ }
+
Array selection = editor_selection->get_selected_nodes();
if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) {
_reset_drag();
@@ -5925,7 +5928,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it
EditorNode::get_singleton()->get_gui_base()->add_child(selector);
selector->set_title(TTR("Change Default Type"));
selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed));
- selector->connect("cancelled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed));
+ selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed));
VBoxContainer *vbc = memnew(VBoxContainer);
selector->add_child(vbc);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index f4fcc8a3d2..1e01eac82d 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -368,7 +368,7 @@ private:
void _pan_callback(Vector2 p_scroll_vec);
void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
- bool _is_node_locked(const Node *p_node);
+ bool _is_node_locked(const Node *p_node) const;
bool _is_node_movable(const Node *p_node, bool p_popup_warning = false);
void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, bool p_allow_locked = false);
@@ -402,7 +402,7 @@ private:
void _prepare_grid_menu();
void _on_grid_menu_id_pressed(int p_id);
- List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
+ List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true) const;
Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true);
Rect2 _get_encompassing_rect(const Node *p_node);
@@ -552,6 +552,8 @@ public:
void focus_selection();
void center_at(const Point2 &p_pos);
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
+
EditorSelection *editor_selection = nullptr;
CanvasItemEditor();
diff --git a/editor/plugins/dedicated_server_export_plugin.cpp b/editor/plugins/dedicated_server_export_plugin.cpp
new file mode 100644
index 0000000000..013706034e
--- /dev/null
+++ b/editor/plugins/dedicated_server_export_plugin.cpp
@@ -0,0 +1,139 @@
+/**************************************************************************/
+/* dedicated_server_export_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "dedicated_server_export_plugin.h"
+
+EditorExportPreset::FileExportMode DedicatedServerExportPlugin::_get_export_mode_for_path(const String &p_path) {
+ Ref<EditorExportPreset> preset = get_export_preset();
+ ERR_FAIL_COND_V(preset.is_null(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
+
+ EditorExportPreset::FileExportMode mode = preset->get_file_export_mode(p_path);
+ if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
+ return mode;
+ }
+
+ String path = p_path;
+ if (path.begins_with("res://")) {
+ path = path.substr(6);
+ }
+
+ Vector<String> parts = path.split("/");
+
+ while (parts.size() > 0) {
+ parts.resize(parts.size() - 1);
+
+ String test_path = "res://";
+ if (parts.size() > 0) {
+ test_path += String("/").join(parts) + "/";
+ }
+
+ mode = preset->get_file_export_mode(test_path);
+ if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
+ break;
+ }
+ }
+
+ return mode;
+}
+
+PackedStringArray DedicatedServerExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const {
+ PackedStringArray ret;
+
+ Ref<EditorExportPreset> preset = get_export_preset();
+ ERR_FAIL_COND_V(preset.is_null(), ret);
+
+ if (preset->is_dedicated_server()) {
+ ret.append("dedicated_server");
+ }
+ return ret;
+}
+
+uint64_t DedicatedServerExportPlugin::_get_customization_configuration_hash() const {
+ Ref<EditorExportPreset> preset = get_export_preset();
+ ERR_FAIL_COND_V(preset.is_null(), 0);
+
+ if (preset->get_export_filter() != EditorExportPreset::EXPORT_CUSTOMIZED) {
+ return 0;
+ }
+
+ return preset->get_customized_files().hash();
+}
+
+bool DedicatedServerExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
+ Ref<EditorExportPreset> preset = get_export_preset();
+ ERR_FAIL_COND_V(preset.is_null(), false);
+
+ current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
+
+ return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED;
+}
+
+bool DedicatedServerExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
+ Ref<EditorExportPreset> preset = get_export_preset();
+ ERR_FAIL_COND_V(preset.is_null(), false);
+
+ current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
+
+ return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED;
+}
+
+Node *DedicatedServerExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
+ // Simply set the export mode based on the scene path. All the real
+ // customization happens in _customize_resource().
+ current_export_mode = _get_export_mode_for_path(p_path);
+ return nullptr;
+}
+
+Ref<Resource> DedicatedServerExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
+ // If the resource has a path, we use that to get our export mode. But if it
+ // doesn't, we assume that this resource is embedded in the last resource with
+ // a path.
+ if (p_path != "") {
+ current_export_mode = _get_export_mode_for_path(p_path);
+ }
+
+ if (p_resource.is_valid() && current_export_mode == EditorExportPreset::MODE_FILE_STRIP && p_resource->has_method("create_placeholder")) {
+ Callable::CallError err;
+ Ref<Resource> result = const_cast<Resource *>(p_resource.ptr())->callp("create_placeholder", nullptr, 0, err);
+ if (err.error == Callable::CallError::CALL_OK) {
+ return result;
+ }
+ }
+
+ return Ref<Resource>();
+}
+
+void DedicatedServerExportPlugin::_end_customize_scenes() {
+ current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
+}
+
+void DedicatedServerExportPlugin::_end_customize_resources() {
+ current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
+}
diff --git a/editor/plugins/dedicated_server_export_plugin.h b/editor/plugins/dedicated_server_export_plugin.h
new file mode 100644
index 0000000000..cb014ae52d
--- /dev/null
+++ b/editor/plugins/dedicated_server_export_plugin.h
@@ -0,0 +1,58 @@
+/**************************************************************************/
+/* dedicated_server_export_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DEDICATED_SERVER_EXPORT_PLUGIN_H
+#define DEDICATED_SERVER_EXPORT_PLUGIN_H
+
+#include "editor/export/editor_export.h"
+
+class DedicatedServerExportPlugin : public EditorExportPlugin {
+private:
+ EditorExportPreset::FileExportMode current_export_mode;
+
+ EditorExportPreset::FileExportMode _get_export_mode_for_path(const String &p_path);
+
+protected:
+ String _get_name() const override { return "DedicatedServer"; }
+
+ PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const override;
+ uint64_t _get_customization_configuration_hash() const override;
+
+ bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
+ bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
+
+ Node *_customize_scene(Node *p_root, const String &p_path) override;
+ Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path) override;
+
+ void _end_customize_scenes() override;
+ void _end_customize_resources() override;
+};
+
+#endif // DEDICATED_SERVER_EXPORT_PLUGIN_H
diff --git a/editor/plugins/gradient_editor.cpp b/editor/plugins/gradient_editor.cpp
index 3676c2c222..2eb17b3f13 100644
--- a/editor/plugins/gradient_editor.cpp
+++ b/editor/plugins/gradient_editor.cpp
@@ -52,12 +52,12 @@ void GradientEditor::reverse_gradient() {
int GradientEditor::_get_point_from_pos(int x) {
int result = -1;
- int total_w = get_size().width - get_size().height - draw_spacing;
+ int total_w = get_size().width - get_size().height - draw_spacing - handle_width;
float min_distance = 1e20;
for (int i = 0; i < points.size(); i++) {
// Check if we clicked at point.
float distance = ABS(x - points[i].offset * total_w);
- float min = (handle_width / 2 * 1.7); // Make it easier to grab.
+ float min = handle_width * 0.85; // Allow the mouse to be more than half a handle width away for ease of grabbing.
if (distance <= min && distance < min_distance) {
result = i;
min_distance = distance;
@@ -198,116 +198,87 @@ void GradientEditor::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> mb = p_event;
- // Show color picker on double click.
- if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_double_click() && mb->is_pressed()) {
- grabbed = _get_point_from_pos(mb->get_position().x);
- _show_color_picker();
- accept_event();
- return;
- }
-
- // Delete point on right click.
- if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
- grabbed = _get_point_from_pos(mb->get_position().x);
- if (grabbed != -1) {
- points.remove_at(grabbed);
- grabbed = -1;
- grabbing = false;
- queue_redraw();
- emit_signal(SNAME("ramp_changed"));
- accept_event();
- }
- }
-
- // Hold alt key to duplicate selected color.
- if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed() && mb->is_alt_pressed()) {
- int x = mb->get_position().x;
- grabbed = _get_point_from_pos(x);
- if (grabbed != -1) {
- int total_w = get_size().width - get_size().height - draw_spacing;
- Gradient::Point new_point = points[grabbed];
- new_point.offset = CLAMP(x / float(total_w), 0, 1);
+ if (mb.is_valid() && mb->is_pressed()) {
+ float adjusted_mb_x = mb->get_position().x - handle_width / 2;
- points.push_back(new_point);
- points.sort();
- for (int i = 0; i < points.size(); ++i) {
- if (points[i].offset == new_point.offset) {
- grabbed = i;
- break;
- }
+ // Delete point on right click.
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ grabbed = _get_point_from_pos(adjusted_mb_x);
+ if (grabbed != -1) {
+ points.remove_at(grabbed);
+ grabbed = -1;
+ grabbing = false;
+ queue_redraw();
+ emit_signal(SNAME("ramp_changed"));
+ accept_event();
}
-
- emit_signal(SNAME("ramp_changed"));
- queue_redraw();
}
- }
- // Select.
- if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
- queue_redraw();
- int x = mb->get_position().x;
- int total_w = get_size().width - get_size().height - draw_spacing;
+ // Hold Alt key to duplicate selected color.
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_alt_pressed()) {
+ grabbed = _get_point_from_pos(adjusted_mb_x);
- //Check if color selector was clicked.
- if (x > total_w + draw_spacing) {
- _show_color_picker();
- return;
- }
-
- grabbing = true;
+ if (grabbed != -1) {
+ int total_w = get_size().width - get_size().height - draw_spacing - handle_width;
+ Gradient::Point new_point = points[grabbed];
+ new_point.offset = CLAMP(adjusted_mb_x / float(total_w), 0, 1);
+ points.push_back(new_point);
+ points.sort();
+ for (int i = 0; i < points.size(); ++i) {
+ if (points[i].offset == new_point.offset) {
+ grabbed = i;
+ break;
+ }
+ }
- grabbed = _get_point_from_pos(x);
- //grab or select
- if (grabbed != -1) {
- return;
+ emit_signal(SNAME("ramp_changed"));
+ queue_redraw();
+ }
}
- // Insert point.
- Gradient::Point new_point;
- new_point.offset = CLAMP(x / float(total_w), 0, 1);
-
- Gradient::Point prev;
- Gradient::Point next;
+ // Select.
+ if (mb->get_button_index() == MouseButton::LEFT) {
+ queue_redraw();
+ int total_w = get_size().width - get_size().height - draw_spacing - handle_width;
- int pos = -1;
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset < new_point.offset) {
- pos = i;
+ // Check if color selector was clicked or ramp was double-clicked.
+ if (adjusted_mb_x > total_w + draw_spacing) {
+ if (!mb->is_double_click()) {
+ _show_color_picker();
+ }
+ return;
+ } else if (mb->is_double_click()) {
+ grabbed = _get_point_from_pos(adjusted_mb_x);
+ _show_color_picker();
+ accept_event();
+ return;
}
- }
- if (pos == -1) {
- prev.color = Color(0, 0, 0);
- prev.offset = 0;
- if (points.size()) {
- next = points[0];
- } else {
- next.color = Color(1, 1, 1);
- next.offset = 1.0;
- }
- } else {
- if (pos == points.size() - 1) {
- next.color = Color(1, 1, 1);
- next.offset = 1.0;
- } else {
- next = points[pos + 1];
+ grabbing = true;
+ grabbed = _get_point_from_pos(adjusted_mb_x);
+
+ // Grab or select.
+ if (grabbed != -1) {
+ return;
}
- prev = points[pos];
- }
- new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset));
+ // Insert point.
+ Gradient::Point new_point;
+ new_point.offset = CLAMP(adjusted_mb_x / float(total_w), 0, 1);
+ new_point.color = gradient->get_color_at_offset(new_point.offset);
- points.push_back(new_point);
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == new_point.offset) {
- grabbed = i;
- break;
+ points.push_back(new_point);
+ points.sort();
+ for (int i = 0; i < points.size(); i++) {
+ if (points[i].offset == new_point.offset) {
+ grabbed = i;
+ break;
+ }
}
- }
- emit_signal(SNAME("ramp_changed"));
+ emit_signal(SNAME("ramp_changed"));
+ }
}
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
@@ -321,18 +292,16 @@ void GradientEditor::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && grabbing) {
- int total_w = get_size().width - get_size().height - draw_spacing;
-
- int x = mm->get_position().x;
-
- float newofs = CLAMP(x / float(total_w), 0, 1);
+ float adjusted_mm_x = mm->get_position().x - handle_width / 2;
+ int total_w = get_size().width - get_size().height - draw_spacing - handle_width;
+ float newofs = CLAMP(adjusted_mm_x / float(total_w), 0, 1);
// Snap to "round" coordinates if holding Ctrl.
// Be more precise if holding Shift as well.
if (mm->is_ctrl_pressed()) {
newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1);
} else if (mm->is_shift_pressed()) {
- // Snap to nearest point if holding just Shift
+ // Snap to nearest point if holding just Shift.
const float snap_threshold = 0.03;
float smallest_ofs = snap_threshold;
bool found = false;
@@ -421,7 +390,7 @@ void GradientEditor::_notification(int p_what) {
// Draw borders around color ramp if in focus.
if (has_focus()) {
- draw_rect(Rect2(handle_width / 2, 0, total_w, h), Color(1, 1, 1, 0.9), false);
+ draw_rect(Rect2(handle_width / 2, 0, total_w, h), Color(1, 1, 1, 0.9), false, 1);
}
// Draw point markers.
@@ -432,18 +401,18 @@ void GradientEditor::_notification(int p_what) {
draw_line(Vector2(points[i].offset * total_w + handle_width / 2, 0), Vector2(points[i].offset * total_w + handle_width / 2, h / 2), col);
Rect2 rect = Rect2(points[i].offset * total_w, h / 2, handle_width, h / 2);
draw_rect(rect, points[i].color, true);
- draw_rect(rect, col, false);
+ draw_rect(rect, col, false, 1);
if (grabbed == i) {
const Color focus_color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
rect = rect.grow(-1);
if (has_focus()) {
- draw_rect(rect, focus_color, false);
+ draw_rect(rect, focus_color, false, 1);
} else {
- draw_rect(rect, focus_color.darkened(0.4), false);
+ draw_rect(rect, focus_color.darkened(0.4), false, 1);
}
rect = rect.grow(-1);
- draw_rect(rect, col, false);
+ draw_rect(rect, col, false, 1);
}
}
@@ -454,7 +423,7 @@ void GradientEditor::_notification(int p_what) {
// Draw with selection color.
draw_rect(Rect2(button_offset, 0, h, h), points[grabbed].color);
} else {
- // If no color selected draw grey color with 'X' on top.
+ // If no color selected draw gray color with 'X' on top.
draw_rect(Rect2(button_offset, 0, h, h), Color(0.5, 0.5, 0.5, 1));
draw_line(Vector2(button_offset, 0), Vector2(button_offset + h, h), Color(1, 1, 1, 0.6));
draw_line(Vector2(button_offset, h), Vector2(button_offset + h, 0), Color(1, 1, 1, 0.6));
diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp
index 1adcc2a3b4..519cfcaa94 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.cpp
+++ b/editor/plugins/lightmap_gi_editor_plugin.cpp
@@ -35,12 +35,43 @@
void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
if (lightmap) {
- LightmapGI::BakeError err;
+ LightmapGI::BakeError err = LightmapGI::BAKE_ERROR_OK;
const uint64_t time_started = OS::get_singleton()->get_ticks_msec();
- if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) {
- err = lightmap->bake(lightmap, p_file, bake_func_step);
+ if (get_tree()->get_edited_scene_root()) {
+ Ref<LightmapGIData> lightmapGIData = lightmap->get_light_data();
+
+ if (lightmapGIData.is_valid()) {
+ String path = lightmapGIData->get_path();
+ if (!path.is_resource_file()) {
+ int srpos = path.find("::");
+ if (srpos != -1) {
+ String base = path.substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ err = LightmapGI::BAKE_ERROR_FOREIGN_DATA;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ err = LightmapGI::BAKE_ERROR_FOREIGN_DATA;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(path + ".import")) {
+ err = LightmapGI::BAKE_ERROR_FOREIGN_DATA;
+ }
+ }
+ }
+
+ if (err == LightmapGI::BAKE_ERROR_OK) {
+ if (get_tree()->get_edited_scene_root() == lightmap) {
+ err = lightmap->bake(lightmap, p_file, bake_func_step);
+ } else {
+ err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step);
+ }
+ }
} else {
- err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step);
+ err = LightmapGI::BAKE_ERROR_NO_SCENE_ROOT;
}
bake_func_end(time_started);
@@ -59,16 +90,21 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
file_dialog->set_current_path(scene_path);
file_dialog->popup_file_dialog();
-
} break;
- case LightmapGI::BAKE_ERROR_NO_MESHES:
+ case LightmapGI::BAKE_ERROR_NO_MESHES: {
EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
- break;
- case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE:
+ } break;
+ case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: {
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
- break;
+ } break;
+ case LightmapGI::BAKE_ERROR_NO_SCENE_ROOT: {
+ EditorNode::get_singleton()->show_warning(TTR("No editor scene root found."));
+ } break;
+ case LightmapGI::BAKE_ERROR_FOREIGN_DATA: {
+ EditorNode::get_singleton()->show_warning(TTR("Lightmap data is not local to the scene."));
+ } break;
default: {
- }
+ } break;
}
}
}
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 05fc464226..c30f0ec62d 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -1254,7 +1254,7 @@ Polygon2DEditor::Polygon2DEditor() {
uv_edit = memnew(AcceptDialog);
add_child(uv_edit);
uv_edit->set_title(TTR("Polygon 2D UV Editor"));
- uv_edit->connect("cancelled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide));
+ uv_edit->connect("canceled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide));
VBoxContainer *uv_main_vb = memnew(VBoxContainer);
uv_edit->add_child(uv_main_vb);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 3ee9823f3a..782e365138 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -266,15 +266,15 @@ void Skeleton3DEditor::reset_pose(const bool p_all_bones) {
if (!skeleton) {
return;
}
- const int bone_len = skeleton->get_bone_count();
- if (!bone_len) {
+ const int bone_count = skeleton->get_bone_count();
+ if (!bone_count) {
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
if (p_all_bones) {
- for (int i = 0; i < bone_len; i++) {
+ for (int i = 0; i < bone_count; i++) {
ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
@@ -333,15 +333,15 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
if (!skeleton) {
return;
}
- const int bone_len = skeleton->get_bone_count();
- if (!bone_len) {
+ const int bone_count = skeleton->get_bone_count();
+ if (!bone_count) {
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
if (p_all_bones) {
- for (int i = 0; i < bone_len; i++) {
+ for (int i = 0; i < bone_count; i++) {
ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i));
ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));
}
@@ -362,57 +362,56 @@ void Skeleton3DEditor::create_physical_skeleton() {
ERR_FAIL_COND(!get_tree());
Node *owner = get_tree()->get_edited_scene_root();
- const int bc = skeleton->get_bone_count();
+ const int bone_count = skeleton->get_bone_count();
- if (!bc) {
+ if (!bone_count) {
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Cannot create a physical skeleton for a Skeleton3D node with no bones.")));
return;
}
Vector<BoneInfo> bones_infos;
- bones_infos.resize(bc);
+ bones_infos.resize(bone_count);
- if (bc > 0) {
- ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);
- for (int bone_id = 0; bc > bone_id; ++bone_id) {
- const int parent = skeleton->get_bone_parent(bone_id);
+ ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);
+ for (int bone_id = 0; bone_count > bone_id; ++bone_id) {
+ const int parent = skeleton->get_bone_parent(bone_id);
- if (parent < 0) {
- bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
- } else {
- const int parent_parent = skeleton->get_bone_parent(parent);
-
- bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
-
- // Create physical bone on parent.
- if (!bones_infos[parent].physical_bone) {
- PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos);
- if (physical_bone && physical_bone->get_child(0)) {
- CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0));
- if (collision_shape) {
- bones_infos.write[parent].physical_bone = physical_bone;
-
- ur->add_do_method(skeleton, "add_child", physical_bone);
- ur->add_do_method(physical_bone, "set_owner", owner);
- ur->add_do_method(collision_shape, "set_owner", owner);
- ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent));
-
- // Create joint between parent of parent.
- if (parent_parent != -1) {
- ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN);
- }
+ if (parent < 0) {
+ bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
+ } else {
+ const int parent_parent = skeleton->get_bone_parent(parent);
+
+ bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
+
+ // Create physical bone on parent.
+ if (!bones_infos[parent].physical_bone) {
+ PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos);
+ if (physical_bone && physical_bone->get_child(0)) {
+ CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0));
+ if (collision_shape) {
+ bones_infos.write[parent].physical_bone = physical_bone;
+
+ ur->add_do_method(skeleton, "add_child", physical_bone);
+ ur->add_do_method(physical_bone, "set_owner", owner);
+ ur->add_do_method(collision_shape, "set_owner", owner);
+ ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent));
+
+ // Create joint between parent of parent.
+ if (parent_parent != -1) {
+ ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN);
+ }
- ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, physical_bone);
- ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, collision_shape);
+ ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, physical_bone);
+ ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, collision_shape);
- ur->add_do_reference(physical_bone);
- ur->add_undo_method(skeleton, "remove_child", physical_bone);
- }
+ ur->add_do_reference(physical_bone);
+ ur->add_undo_method(skeleton, "remove_child", physical_bone);
}
}
}
}
- ur->commit_action();
}
+ ur->commit_action();
}
PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) {
@@ -457,6 +456,11 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi
}
void Skeleton3DEditor::export_skeleton_profile() {
+ if (!skeleton->get_bone_count()) {
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Cannot export a SkeletonProfile for a Skeleton3D node with no bones.")));
+ return;
+ }
+
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_dialog->set_title(TTR("Export Skeleton Profile As..."));
@@ -481,9 +485,9 @@ void Skeleton3DEditor::_file_selected(const String &p_file) {
Vector2 position_max;
Vector2 position_min;
- int len = skeleton->get_bone_count();
- sp->set_bone_size(len);
- for (int i = 0; i < len; i++) {
+ const int bone_count = skeleton->get_bone_count();
+ sp->set_bone_size(bone_count);
+ for (int i = 0; i < bone_count; i++) {
sp->set_bone_name(i, skeleton->get_bone_name(i));
int parent = skeleton->get_bone_parent(i);
if (parent >= 0) {
@@ -509,7 +513,7 @@ void Skeleton3DEditor::_file_selected(const String &p_file) {
Vector2 center = Vector2((position_max.x + position_min.x) * 0.5, (position_max.y + position_min.y) * 0.5);
float nrm = MAX(bound.x, bound.y);
if (nrm > 0) {
- for (int i = 0; i < len; i++) {
+ for (int i = 0; i < bone_count; i++) {
handle_positions.write[i] = (handle_positions[i] - center) / nrm * 0.9;
sp->set_handle_offset(i, Vector2(0.5 + handle_positions[i].x, 0.5 - handle_positions[i].y));
}
@@ -980,25 +984,31 @@ void Skeleton3DEditor::_draw_gizmo() {
}
void Skeleton3DEditor::_draw_handles() {
- handles_mesh_instance->show();
+ const int bone_count = skeleton->get_bone_count();
- const int bone_len = skeleton->get_bone_count();
handles_mesh->clear_surfaces();
- handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS);
- for (int i = 0; i < bone_len; i++) {
- Color c;
- if (i == selected_bone) {
- c = Color(1, 1, 0);
- } else {
- c = Color(0.1, 0.25, 0.8);
+ if (bone_count) {
+ handles_mesh_instance->show();
+
+ handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS);
+
+ for (int i = 0; i < bone_count; i++) {
+ Color c;
+ if (i == selected_bone) {
+ c = Color(1, 1, 0);
+ } else {
+ c = Color(0.1, 0.25, 0.8);
+ }
+ Vector3 point = skeleton->get_bone_global_pose(i).origin;
+ handles_mesh->surface_set_color(c);
+ handles_mesh->surface_add_vertex(point);
}
- Vector3 point = skeleton->get_bone_global_pose(i).origin;
- handles_mesh->surface_set_color(c);
- handles_mesh->surface_add_vertex(point);
+ handles_mesh->surface_end();
+ handles_mesh->surface_set_material(0, handle_material);
+ } else {
+ handles_mesh_instance->hide();
}
- handles_mesh->surface_end();
- handles_mesh->surface_set_material(0, handle_material);
}
TreeItem *Skeleton3DEditor::_find(TreeItem *p_node, const NodePath &p_path) {
@@ -1253,8 +1263,8 @@ int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gi
Transform3D gt = skeleton->get_global_transform();
int closest_idx = -1;
real_t closest_dist = 1e10;
- const int bone_len = skeleton->get_bone_count();
- for (int i = 0; i < bone_len; i++) {
+ const int bone_count = skeleton->get_bone_count();
+ for (int i = 0; i < bone_count; i++) {
Vector3 joint_pos_3d = gt.xform(skeleton->get_bone_global_pose(i).origin);
Vector2 joint_pos_2d = p_camera->unproject_position(joint_pos_3d);
real_t dist_3d = ray_from.distance_to(joint_pos_3d);
@@ -1349,6 +1359,10 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
+ if (!skeleton->get_bone_count()) {
+ return;
+ }
+
int selected = -1;
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
if (se) {
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 40aac77a99..14ff59b442 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -3335,8 +3335,10 @@ void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) {
}
edited_theme = p_theme;
- edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
- _update_type_list();
+ if (edited_theme.is_valid()) {
+ edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
+ _update_type_list();
+ }
add_type_dialog->set_edited_theme(edited_theme);
}
@@ -3496,7 +3498,9 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) {
preview_tab->set_preview_theme(p_theme);
}
- theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file());
+ if (theme.is_valid()) {
+ theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file());
+ }
}
Ref<Theme> ThemeEditor::get_edited_theme() {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index dc19870f70..af70e64b6a 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -5289,7 +5289,7 @@ VisualShaderEditor::VisualShaderEditor() {
members_dialog->set_ok_button_text(TTR("Create"));
members_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualShaderEditor::_member_create));
members_dialog->get_ok_button()->set_disabled(true);
- members_dialog->connect("cancelled", callable_mp(this, &VisualShaderEditor::_member_cancel));
+ members_dialog->connect("canceled", callable_mp(this, &VisualShaderEditor::_member_cancel));
add_child(members_dialog);
// add varyings dialog
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index a3ccf392e6..f9f72fee77 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -35,7 +35,9 @@
void VoxelGIEditorPlugin::_bake() {
if (voxel_gi) {
- if (voxel_gi->get_probe_data().is_null()) {
+ Ref<VoxelGIData> voxel_gi_data = voxel_gi->get_probe_data();
+
+ if (voxel_gi_data.is_null()) {
String path = get_tree()->get_edited_scene_root()->get_scene_file_path();
if (path.is_empty()) {
path = "res://" + voxel_gi->get_name() + "_data.res";
@@ -46,7 +48,32 @@ void VoxelGIEditorPlugin::_bake() {
probe_file->set_current_path(path);
probe_file->popup_file_dialog();
return;
+ } else {
+ String path = voxel_gi_data->get_path();
+ if (!path.is_resource_file()) {
+ int srpos = path.find("::");
+ if (srpos != -1) {
+ String base = path.substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ EditorNode::get_singleton()->show_warning(TTR("Voxel GI data is not local to the scene."));
+ return;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ EditorNode::get_singleton()->show_warning(TTR("Voxel GI data is part of an imported resource."));
+ return;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(path + ".import")) {
+ EditorNode::get_singleton()->show_warning(TTR("Voxel GI data is an imported resource."));
+ return;
+ }
+ }
}
+
voxel_gi->bake();
}
}
diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp
index db11f80826..37f941c7b2 100644
--- a/editor/progress_dialog.cpp
+++ b/editor/progress_dialog.cpp
@@ -195,7 +195,7 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p
cancel_hb->hide();
}
cancel_hb->move_to_front();
- cancelled = false;
+ canceled = false;
_popup();
if (p_can_cancel) {
cancel->grab_focus();
@@ -203,12 +203,12 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p
}
bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) {
- ERR_FAIL_COND_V(!tasks.has(p_task), cancelled);
+ ERR_FAIL_COND_V(!tasks.has(p_task), canceled);
if (!p_force_redraw) {
uint64_t tus = OS::get_singleton()->get_ticks_usec();
if (tus - last_progress_tick < 200000) { //200ms
- return cancelled;
+ return canceled;
}
}
@@ -228,7 +228,7 @@ bool ProgressDialog::task_step(const String &p_task, const String &p_state, int
#ifndef ANDROID_ENABLED
Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor
#endif
- return cancelled;
+ return canceled;
}
void ProgressDialog::end_task(const String &p_task) {
@@ -246,7 +246,7 @@ void ProgressDialog::end_task(const String &p_task) {
}
void ProgressDialog::_cancel_pressed() {
- cancelled = true;
+ canceled = true;
}
void ProgressDialog::_bind_methods() {
diff --git a/editor/progress_dialog.h b/editor/progress_dialog.h
index 5346f01a9e..7ac4864c9c 100644
--- a/editor/progress_dialog.h
+++ b/editor/progress_dialog.h
@@ -85,7 +85,7 @@ class ProgressDialog : public PopupPanel {
void _popup();
void _cancel_pressed();
- bool cancelled = false;
+ bool canceled = false;
protected:
void _notification(int p_what);
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index 5f61d10f9d..88fb88de98 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -1285,6 +1285,8 @@ static const char *gdscript_signals_renames[][2] = {
// {"changed","settings_changed"}, // EditorSettings
{ "about_to_show", "about_to_popup" }, // Popup
{ "button_release", "button_released" }, // XRController3D
+ { "cancelled", "canceled" }, // AcceptDialog
+ { "item_double_clicked", "item_icon_double_clicked" }, // Tree
{ "network_peer_connected", "peer_connected" }, // MultiplayerAPI
{ "network_peer_disconnected", "peer_disconnected" }, // MultiplayerAPI
{ "network_peer_packet", "peer_packet" }, // MultiplayerAPI
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index c1dd9e2f6a..86e77fbbbe 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2007,13 +2007,13 @@ void SceneTreeDock::_shader_created(Ref<Shader> p_shader) {
void SceneTreeDock::_script_creation_closed() {
script_create_dialog->disconnect("script_created", callable_mp(this, &SceneTreeDock::_script_created));
script_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed));
- script_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed));
+ script_create_dialog->disconnect("canceled", callable_mp(this, &SceneTreeDock::_script_creation_closed));
}
void SceneTreeDock::_shader_creation_closed() {
shader_create_dialog->disconnect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created));
shader_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
- shader_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
+ shader_create_dialog->disconnect("canceled", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
}
void SceneTreeDock::_toggle_editable_children_from_selection() {
@@ -2191,7 +2191,7 @@ void SceneTreeDock::_do_create(Node *p_parent) {
ERR_FAIL_COND(!child);
String new_name = p_parent->validate_child_name(child);
- if (GLOBAL_GET("editor/node_naming/name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
+ if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
new_name = adjust_name_casing(new_name);
}
child->set_name(new_name);
@@ -3093,7 +3093,7 @@ void SceneTreeDock::attach_script_to_selected(bool p_extend) {
script_create_dialog->connect("script_created", callable_mp(this, &SceneTreeDock::_script_created));
script_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed));
- script_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed));
+ script_create_dialog->connect("canceled", callable_mp(this, &SceneTreeDock::_script_creation_closed));
script_create_dialog->set_inheritance_base_type("Node");
script_create_dialog->config(inherits, path);
script_create_dialog->popup_centered();
@@ -3135,7 +3135,7 @@ void SceneTreeDock::attach_shader_to_selected(int p_preferred_mode) {
shader_create_dialog->connect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created));
shader_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
- shader_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
+ shader_create_dialog->connect("canceled", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
shader_create_dialog->config(path, true, true, -1, p_preferred_mode);
shader_create_dialog->popup_centered();
}
@@ -3613,7 +3613,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
scene_tree->connect("script_dropped", callable_mp(this, &SceneTreeDock::_script_dropped));
scene_tree->connect("nodes_dragged", callable_mp(this, &SceneTreeDock::_nodes_drag_begin));
- scene_tree->get_scene_tree()->connect("item_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node));
+ scene_tree->get_scene_tree()->connect("item_icon_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node));
editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed));
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index e0d748c478..42801cdaf1 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -677,7 +677,7 @@ bool SceneTreeEditor::_item_matches_all_terms(TreeItem *p_item, PackedStringArra
for (int i = 0; i < p_terms.size(); i++) {
String term = p_terms[i];
- // Recognise special filter.
+ // Recognize special filter.
if (term.contains(":") && !term.get_slicec(':', 0).is_empty()) {
String parameter = term.get_slicec(':', 0);
String argument = term.get_slicec(':', 1);
@@ -971,7 +971,7 @@ void SceneTreeEditor::_renamed() {
String raw_new_name = which->get_text(0);
if (raw_new_name.strip_edges().is_empty()) {
// If name is empty, fallback to class name.
- if (GLOBAL_GET("editor/node_naming/name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
+ if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
raw_new_name = Node::adjust_name_casing(n->get_class());
} else {
raw_new_name = n->get_class();