diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/action_map_editor.cpp | 3 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_node.cpp | 6 | ||||
-rw-r--r-- | editor/debugger/script_editor_debugger.cpp | 1 | ||||
-rw-r--r-- | editor/editor_help.cpp | 6 | ||||
-rw-r--r-- | editor/editor_inspector.cpp | 63 | ||||
-rw-r--r-- | editor/editor_node.cpp | 322 | ||||
-rw-r--r-- | editor/editor_node.h | 26 | ||||
-rw-r--r-- | editor/editor_settings.cpp | 6 | ||||
-rw-r--r-- | editor/filesystem_dock.cpp | 12 | ||||
-rw-r--r-- | editor/input_event_configuration_dialog.cpp | 6 | ||||
-rw-r--r-- | editor/input_event_configuration_dialog.h | 1 | ||||
-rw-r--r-- | editor/plugins/animation_player_editor_plugin.cpp | 33 | ||||
-rw-r--r-- | editor/plugins/animation_player_editor_plugin.h | 1 | ||||
-rw-r--r-- | editor/plugins/script_editor_plugin.cpp | 16 |
14 files changed, 323 insertions, 179 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 3023c5907a..f70730d540 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -428,6 +428,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info // Update Tree... TreeItem *action_item = action_tree->create_item(root); + ERR_FAIL_NULL(action_item); action_item->set_meta("__action", action_info.action); action_item->set_meta("__name", action_info.name); @@ -604,7 +605,7 @@ ActionMapEditor::ActionMapEditor() { action_tree->set_column_custom_minimum_width(1, 80 * EDSCALE); action_tree->set_column_expand(2, false); action_tree->set_column_custom_minimum_width(2, 50 * EDSCALE); - action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited)); + action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited), CONNECT_DEFERRED); action_tree->connect("item_activated", callable_mp(this, &ActionMapEditor::_tree_item_activated)); action_tree->connect("button_clicked", callable_mp(this, &ActionMapEditor::_tree_button_pressed)); main_vbox->add_child(action_tree); diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index 2f7183b883..2a98f50a3a 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -268,11 +268,7 @@ Error EditorDebuggerNode::start(const String &p_uri) { } stop(true); current_uri = p_uri; - if (EDITOR_GET("run/output/always_open_output_on_play")) { - EditorNode::get_bottom_panel()->make_item_visible(EditorNode::get_log()); - } else { - EditorNode::get_bottom_panel()->make_item_visible(this); - } + server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3))); const Error err = server->start(p_uri); if (err != OK) { diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 156e740509..37bb048b19 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1009,7 +1009,6 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { set_process(true); camera_override = CameraOverride::OVERRIDE_NONE; - tabs->set_current_tab(0); _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS); _update_buttons_state(); emit_signal(SNAME("started")); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 48c0c7ac06..fa5cdd185f 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -37,7 +37,7 @@ #include "core/object/script_language.h" #include "core/os/keyboard.h" #include "core/string/string_builder.h" -#include "core/version.h" +#include "core/version_generated.gen.h" #include "editor/doc_data_compressed.gen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" @@ -2340,6 +2340,9 @@ void EditorHelp::_help_callback(const String &p_topic) { if (class_desc->is_ready()) { // call_deferred() is not enough. + if (class_desc->is_connected(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph))) { + class_desc->disconnect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph)); + } class_desc->connect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).bind(line), CONNECT_ONE_SHOT | CONNECT_DEFERRED); } else { scroll_to = line; @@ -3380,6 +3383,7 @@ EditorHelpBit::HelpData EditorHelpBit::_get_theme_item_help_data(const StringNam if (theme_item.name == p_theme_item_name) { result = current; found = true; + if (!is_native) { break; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index f4dcc8bd4a..fafefa7771 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2869,15 +2869,6 @@ void EditorInspector::update_tree() { // Otherwise the category was probably added via `@export_category` or `_get_property_list()`. const bool is_custom_category = p.hint_string.is_empty(); - if ((is_custom_category && !show_custom_categories) || (!is_custom_category && !show_standard_categories)) { - continue; - } - - // Hide the "MultiNodeEdit" category for MultiNodeEdit. - if (Object::cast_to<MultiNodeEdit>(object) && p.name == "MultiNodeEdit") { - continue; - } - // Iterate over remaining properties. If no properties in category, skip the category. List<PropertyInfo>::Element *N = E_property->next(); bool valid = true; @@ -2898,22 +2889,20 @@ void EditorInspector::update_tree() { continue; // Empty, ignore it. } - // Create an EditorInspectorCategory and add it to the inspector. - EditorInspectorCategory *category = memnew(EditorInspectorCategory); - main_vbox->add_child(category); - category_vbox = nullptr; //reset + String category_label; + String category_tooltip; + Ref<Texture> category_icon; // Do not add an icon, do not change the current class (`doc_name`) for custom categories. if (is_custom_category) { - category->label = p.name; - category->set_tooltip_text(p.name); + category_label = p.name; + category_tooltip = p.name; } else { - String type = p.name; - String label = p.name; doc_name = p.name; + category_label = p.name; // Use category's owner script to update some of its information. - if (!EditorNode::get_editor_data().is_type_recognized(type) && ResourceLoader::exists(p.hint_string)) { + if (!EditorNode::get_editor_data().is_type_recognized(p.name) && ResourceLoader::exists(p.hint_string)) { Ref<Script> scr = ResourceLoader::load(p.hint_string, "Script"); if (scr.is_valid()) { StringName script_name = EditorNode::get_editor_data().script_class_get_name(scr->get_path()); @@ -2926,32 +2915,50 @@ void EditorInspector::update_tree() { doc_name = docs[docs.size() - 1].name; } if (script_name != StringName()) { - label = script_name; + category_label = script_name; } // Find the icon corresponding to the script. if (script_name != StringName()) { - category->icon = EditorNode::get_singleton()->get_class_icon(script_name); + category_icon = EditorNode::get_singleton()->get_class_icon(script_name); } else { - category->icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); + category_icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); } } } - if (category->icon.is_null() && !type.is_empty()) { - category->icon = EditorNode::get_singleton()->get_class_icon(type); + if (category_icon.is_null() && !p.name.is_empty()) { + category_icon = EditorNode::get_singleton()->get_class_icon(p.name); } - // Set the category label. - category->label = label; - category->doc_class_name = doc_name; - if (use_doc_hints) { // `|` separators used in `EditorHelpBit`. - category->set_tooltip_text("class|" + doc_name + "|"); + category_tooltip = "class|" + doc_name + "|"; } } + if ((is_custom_category && !show_custom_categories) || (!is_custom_category && !show_standard_categories)) { + continue; + } + + // Hide the "MultiNodeEdit" category for MultiNodeEdit. + if (Object::cast_to<MultiNodeEdit>(object) && p.name == "MultiNodeEdit") { + continue; + } + + // Create an EditorInspectorCategory and add it to the inspector. + EditorInspectorCategory *category = memnew(EditorInspectorCategory); + main_vbox->add_child(category); + category_vbox = nullptr; // Reset. + + // Set the category info. + category->label = category_label; + category->set_tooltip_text(category_tooltip); + category->icon = category_icon; + if (!is_custom_category) { + category->doc_class_name = doc_name; + } + // Add editors at the start of a category. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_category(object, p.name); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 6599a58da4..0df4df36bc 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4107,7 +4107,7 @@ 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> EditorNode::get_modified_properties_for_node(Node *p_node, bool p_node_references_only) { HashMap<StringName, Variant> modified_property_map; List<PropertyInfo> pinfo; @@ -4119,7 +4119,17 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * 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; + // If this property is a direct node reference, save a NodePath instead to prevent corrupted references. + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + Node *target_node = Object::cast_to<Node>(current_value); + if (target_node) { + modified_property_map[E.name] = p_node->get_path_to(target_node); + } + } else { + if (!p_node_references_only) { + modified_property_map[E.name] = current_value; + } + } } } } @@ -4137,10 +4147,118 @@ void EditorNode::update_ownership_table_for_addition_node_ancestors(Node *p_curr } } -void EditorNode::update_diff_data_for_node( - Node *p_edited_scene, +void EditorNode::update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification) { + if (p_node) { + // First, attempt to restore the script property since it may affect the get_property_list method. + Variant *script_property_table_entry = p_node_modification.property_table.getptr(CoreStringName(script)); + if (script_property_table_entry) { + p_node->set_script(*script_property_table_entry); + } + + // Get properties for this node. + List<PropertyInfo> pinfo; + p_node->get_property_list(&pinfo); + + // Get names of all valid property names. + HashMap<StringName, bool> property_node_reference_table; + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + property_node_reference_table[E.name] = true; + } else { + property_node_reference_table[E.name] = false; + } + } + } + + // Restore the modified properties for this node. + for (const KeyValue<StringName, Variant> &E : p_node_modification.property_table) { + bool *property_node_reference_table_entry = property_node_reference_table.getptr(E.key); + if (property_node_reference_table_entry) { + // If the property is a node reference, attempt to restore from the node path instead. + bool is_node_reference = *property_node_reference_table_entry; + if (is_node_reference) { + if (E.value.get_type() == Variant::NODE_PATH) { + p_node->set(E.key, p_node->get_node_or_null(E.value)); + } + } else { + p_node->set(E.key, E.value); + } + } + } + + // Restore the connections to other nodes. + for (const ConnectionWithNodePath &E : p_node_modification.connections_to) { + Connection conn = E.connection; + + // Get the node the callable is targeting. + Node *target_node = Object::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 = p_node->get_node_or_null(E.node_path); + } + + if (target_node) { + // Reconstruct the callable. + Callable new_callable = Callable(target_node, conn.callable.get_method()); + + if (!p_node->is_connected(conn.signal.get_name(), new_callable)) { + ERR_FAIL_COND(p_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK); + } + } + } + + // Restore the connections from other nodes. + for (const Connection &E : p_node_modification.connections_from) { + Connection conn = E; + + bool valid = p_node->has_method(conn.callable.get_method()) || Ref<Script>(p_node->get_script()).is_null() || Ref<Script>(p_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(p_node, conn.callable.get_method()), conn.flags) != OK); + } + } + + // Re-add the groups. + for (const Node::GroupInfo &E : p_node_modification.groups) { + p_node->add_to_group(E.name, E.persistent); + } + } +} + +void EditorNode::update_node_reference_modification_table_for_node( Node *p_root, Node *p_node, + List<Node *> p_excluded_nodes, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table) { + if (!p_excluded_nodes.find(p_node)) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false); + + if (!modified_properties.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; + + p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_node_reference_modification_table_for_node(p_root, child, p_excluded_nodes, p_modification_table); + } + } +} + +void EditorNode::update_reimported_diff_data_for_node( + Node *p_edited_scene, + Node *p_reimported_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 && @@ -4150,14 +4268,14 @@ void EditorNode::update_diff_data_for_node( // 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) { + if (valid_node_owner == p_reimported_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); + if ((valid_node_owner == p_reimported_root && (p_reimported_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_reimported_root) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false); // Find all valid connections to other nodes. List<Connection> connections_to; @@ -4189,7 +4307,7 @@ void EditorNode::update_diff_data_for_node( if (source_node) { valid_source_owner = source_node->get_owner(); while (valid_source_owner) { - if (valid_source_owner == p_root) { + if (valid_source_owner == p_reimported_root) { break; } valid_source_owner = valid_source_owner->get_owner(); @@ -4215,41 +4333,55 @@ void EditorNode::update_diff_data_for_node( 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; + p_modification_table[p_reimported_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(); + // Only save additional nodes which have an owner since this was causing issues transient ownerless nodes + // which get recreated upon scene tree entry. + // For now instead, assume all ownerless nodes are transient and will have to be recreated. + if (p_node->get_owner()) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, true); + + if (p_node->get_parent()->get_owner() != nullptr && p_node->get_parent()->get_owner() != p_edited_scene) { + AdditiveNodeEntry new_additive_node_entry; + new_additive_node_entry.node = p_node; + new_additive_node_entry.parent = p_reimported_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()); + } - 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()); - } + // Gathers the ownership of all ancestor nodes for later use. + HashMap<Node *, Node *> ownership_table; + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, ownership_table); + } - // Gathers the ownership of all ancestor nodes for later use. - HashMap<Node *, Node *> ownership_table; - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *child = p_node->get_child(i); - update_ownership_table_for_addition_node_ancestors(child, ownership_table); - } + new_additive_node_entry.ownership_table = ownership_table; - new_additive_node_entry.ownership_table = ownership_table; + p_addition_list.push_back(new_additive_node_entry); + } - p_addition_list.push_back(new_additive_node_entry); + if (!modified_properties.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; - return; + p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry; + } + } } 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); + update_reimported_diff_data_for_node(p_edited_scene, p_reimported_root, child, p_modification_table, p_addition_list); } } // @@ -4411,17 +4543,19 @@ void EditorNode::_project_run_started() { log->clear(); } - if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) { + int action_on_play = EDITOR_GET("run/bottom_panel/action_on_play"); + if (action_on_play == ACTION_ON_PLAY_OPEN_OUTPUT) { bottom_panel->make_item_visible(log); + } else if (action_on_play == ACTION_ON_PLAY_OPEN_DEBUGGER) { + bottom_panel->make_item_visible(EditorDebuggerNode::get_singleton()); } } void EditorNode::_project_run_stopped() { - if (!bool(EDITOR_GET("run/output/always_close_output_on_stop"))) { - return; + int action_on_stop = EDITOR_GET("run/bottom_panel/action_on_stop"); + if (action_on_stop == ACTION_ON_STOP_CLOSE_BUTTOM_PANEL) { + bottom_panel->hide_bottom_panel(); } - - bottom_panel->make_item_visible(log, false); } void EditorNode::notify_all_debug_sessions_exited() { @@ -5548,12 +5682,11 @@ void EditorNode::_file_access_close_error_notify_impl(const String &p_str) { add_io_error(vformat(TTR("Unable to write to file '%s', file in use, locked or lacking permissions."), p_str)); } -// Since we felt that a bespoke NOTIFICATION might not be desirable, this function -// provides the hardcoded callbacks to address known bugs which occur on certain -// nodes during reimport. -// Ideally, we should probably agree on a standardized method name which could be -// called from here instead. -void EditorNode::_notify_scene_updated(Node *p_node) { +// Recursive function to inform nodes that an array of nodes have had their scene reimported. +// It will attempt to call a method named '_nodes_scene_reimported' on every node in the +// tree so that editor scripts which create transient nodes will have the opportunity +// to recreate them. +void EditorNode::_notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes) { Skeleton3D *skel_3d = Object::cast_to<Skeleton3D>(p_node); if (skel_3d) { skel_3d->reset_bone_poses(); @@ -5564,8 +5697,12 @@ void EditorNode::_notify_scene_updated(Node *p_node) { } } + if (p_node->has_method("_nodes_scene_reimported")) { + p_node->call("_nodes_scene_reimported", p_reimported_nodes); + } + for (int i = 0; i < p_node->get_child_count(); i++) { - _notify_scene_updated(p_node->get_child(i)); + _notify_nodes_scene_reimported(p_node->get_child(i), p_reimported_nodes); } } @@ -5635,6 +5772,7 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node * 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; + Array replaced_nodes; // Walk through each opened scene to get a global list of all instances which match // the current reimported scenes. @@ -5675,12 +5813,16 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins // Update the version editor_data.is_scene_changed(current_scene_idx); + // Contains modifications in the edited scene which reference nodes inside of any nodes we will be reimporting. + HashMap<NodePath, ModificationNodeEntry> edited_scene_global_modification_table; + update_node_reference_modification_table_for_node(current_edited_scene, current_edited_scene, edited_scene_map_elem.value, edited_scene_global_modification_table); + 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); + update_reimported_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) { @@ -5762,7 +5904,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins // be properly updated. for (String path : required_load_paths) { if (!local_scene_cache.find(path)) { - current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); + current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP, &err); local_scene_cache[path] = current_packed_scene; } else { current_packed_scene = local_scene_cache[path]; @@ -5780,6 +5922,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins ERR_FAIL_NULL(instantiated_node); + // For clear instance state for path recaching. + instantiated_node->set_scene_instance_state(Ref<SceneState>()); + 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(); @@ -5885,77 +6030,30 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins 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 efficient). - 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 targeting. - 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(); + update_node_from_node_modification_entry(modifiable_node, E.value); + } + // Add the newly instantiated node to the edited scene's replaced node list. + replaced_nodes.push_back(instantiated_node); + } - if (source_object) { - ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(modifiable_node, conn.callable.get_method()), conn.flags) != OK); - } - } + // Attempt to restore the modified properties and signals for the instantitated node and all its owned children. + for (KeyValue<NodePath, ModificationNodeEntry> &E : edited_scene_global_modification_table) { + NodePath new_current_path = E.key; + Node *modifiable_node = current_edited_scene->get_node_or_null(new_current_path); - // Re-add the groups. - for (const Node::GroupInfo &E2 : E.value.groups) { - modifiable_node->add_to_group(E2.name, E2.persistent); - } - } + if (modifiable_node) { + update_node_from_node_modification_entry(modifiable_node, E.value); } } // Cleanup the history of the changes. editor_history.cleanup_history(); - - _notify_scene_updated(current_edited_scene); } + + // For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes. + // To inform anything that depends on them that they should update as appropriate. + _notify_nodes_scene_reimported(this, replaced_nodes); + edited_scene_map.clear(); } editor_data.set_edited_scene(original_edited_scene_idx); @@ -7228,6 +7326,8 @@ EditorNode::EditorNode() { disk_changed = memnew(ConfirmationDialog); { + disk_changed->set_title(TTR("Files have been modified on disk")); + VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); @@ -7241,9 +7341,9 @@ EditorNode::EditorNode() { disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_modified_scenes)); disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_project_settings)); - disk_changed->set_ok_button_text(TTR("Reload")); + disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); - disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes)); } diff --git a/editor/editor_node.h b/editor/editor_node.h index 6b3359eaee..5d7bd5b4f8 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -140,6 +140,17 @@ public: SCENE_NAME_CASING_KEBAB_CASE, }; + enum ActionOnPlay { + ACTION_ON_PLAY_DO_NOTHING, + ACTION_ON_PLAY_OPEN_OUTPUT, + ACTION_ON_PLAY_OPEN_DEBUGGER, + }; + + enum ActionOnStop { + ACTION_ON_STOP_DO_NOTHING, + ACTION_ON_STOP_CLOSE_BUTTOM_PANEL, + }; + struct ExecuteThreadArgs { String path; List<String> args; @@ -662,7 +673,7 @@ private: void _begin_first_scan(); - void _notify_scene_updated(Node *p_node); + void _notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes); protected: friend class FileSystemDock; @@ -779,7 +790,7 @@ 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); + HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only); struct AdditiveNodeEntry { Node *node = nullptr; @@ -806,11 +817,18 @@ public: }; void update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table); + void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification); - void update_diff_data_for_node( - Node *p_edited_scene, + void update_node_reference_modification_table_for_node( Node *p_root, Node *p_node, + List<Node *> p_excluded_nodes, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table); + + void update_reimported_diff_data_for_node( + Node *p_edited_scene, + Node *p_reimported_root, + Node *p_node, HashMap<NodePath, ModificationNodeEntry> &p_modification_table, List<AdditiveNodeEntry> &p_addition_list); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index d7bc3502ce..737bec352d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -818,11 +818,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Auto save _initial_set("run/auto_save/save_before_running", true); + // Bottom panel + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "run/bottom_panel/action_on_play", EditorNode::ACTION_ON_PLAY_OPEN_OUTPUT, "Do Nothing,Open Output,Open Debugger") + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "run/bottom_panel/action_on_stop", EditorNode::ACTION_ON_STOP_DO_NOTHING, "Do Nothing,Close Bottom Panel") + // Output EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "run/output/font_size", 13, "8,48,1") _initial_set("run/output/always_clear_output_on_play", true); - _initial_set("run/output/always_open_output_on_play", true); - _initial_set("run/output/always_close_output_on_stop", false); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "run/output/max_lines", 10000, "100,100000,1") diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 2e88540fc4..c07667ac12 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -3310,15 +3310,15 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect // Opening the system file manager is not supported on the Android and web editors. const bool is_directory = fpath.ends_with("/"); - p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), 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), is_directory ? TTR("Open in File Manager") : TTR("Show in File Manager")); + p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); + p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal")); if (!is_directory) { p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("ExternalLink")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL); } - p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); - p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal")); + p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), 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), is_directory ? TTR("Open in File Manager") : TTR("Show in File Manager")); #endif current_path = fpath; @@ -3362,8 +3362,8 @@ void FileSystemDock::_tree_empty_click(const Vector2 &p_pos, MouseButton p_butto #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) // Opening the system file manager is not supported on the Android and web editors. tree_popup->add_separator(); - tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); + tree_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); #endif tree_popup->set_position(tree->get_screen_position() + p_pos); @@ -3425,8 +3425,8 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton file_list_popup->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("New Resource..."), FILE_NEW_RESOURCE); file_list_popup->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("New TextFile..."), FILE_NEW_TEXTFILE); file_list_popup->add_separator(); - file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); + file_list_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Filesystem")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); file_list_popup->set_position(files->get_screen_position() + p_pos); file_list_popup->reset_size(); diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index 865729c7c3..2ecce2f739 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -446,6 +446,11 @@ void InputEventConfigurationDialog::_key_location_selected(int p_location) { _set_event(k, original_event); } +void InputEventConfigurationDialog::_input_list_item_activated() { + TreeItem *selected = input_list_tree->get_selected(); + selected->set_collapsed(!selected->is_collapsed()); +} + void InputEventConfigurationDialog::_input_list_item_selected() { TreeItem *selected = input_list_tree->get_selected(); @@ -670,6 +675,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { input_list_tree = memnew(Tree); input_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); input_list_tree->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); // Min height for tree + input_list_tree->connect("item_activated", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_activated)); input_list_tree->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected)); input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); manual_vbox->add_child(input_list_tree); diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h index 1d2cc8ba36..b27f25a5b7 100644 --- a/editor/input_event_configuration_dialog.h +++ b/editor/input_event_configuration_dialog.h @@ -107,6 +107,7 @@ private: void _search_term_updated(const String &p_term); void _update_input_list(); + void _input_list_item_activated(); void _input_list_item_selected(); void _mod_toggled(bool p_checked, int p_index); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 794f6190bb..1cf11f2a43 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -886,6 +886,7 @@ void AnimationPlayerEditor::_update_player() { tool_anim->set_disabled(player == nullptr); pin->set_disabled(player == nullptr); + _set_controls_disabled(player == nullptr); if (!player) { AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying(); @@ -936,17 +937,6 @@ void AnimationPlayerEditor::_update_player() { ITEM_CHECK_DISABLED(TOOL_NEW_ANIM); #undef ITEM_CHECK_DISABLED - stop->set_disabled(no_anims_found); - play->set_disabled(no_anims_found); - play_bw->set_disabled(no_anims_found); - play_bw_from->set_disabled(no_anims_found); - play_from->set_disabled(no_anims_found); - frame->set_editable(!no_anims_found); - animation->set_disabled(no_anims_found); - autoplay->set_disabled(no_anims_found); - onion_toggle->set_disabled(no_anims_found); - onion_skinning->set_disabled(no_anims_found); - _update_animation_list_icons(); updating = false; @@ -963,7 +953,9 @@ void AnimationPlayerEditor::_update_player() { _animation_selected(0); } - if (!no_anims_found) { + if (no_anims_found) { + _set_controls_disabled(true); + } else { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); @@ -979,6 +971,20 @@ void AnimationPlayerEditor::_update_player() { _update_animation(); } +void AnimationPlayerEditor::_set_controls_disabled(bool p_disabled) { + frame->set_editable(!p_disabled); + + stop->set_disabled(p_disabled); + play->set_disabled(p_disabled); + play_bw->set_disabled(p_disabled); + play_bw_from->set_disabled(p_disabled); + play_from->set_disabled(p_disabled); + animation->set_disabled(p_disabled); + autoplay->set_disabled(p_disabled); + onion_toggle->set_disabled(p_disabled); + onion_skinning->set_disabled(p_disabled); +} + void AnimationPlayerEditor::_update_animation_list_icons() { for (int i = 0; i < animation->get_item_count(); i++) { String anim_name = animation->get_item_text(i); @@ -1081,9 +1087,6 @@ void AnimationPlayerEditor::_ensure_dummy_player() { } } - // Make some options disabled. - onion_toggle->set_disabled(dummy_exists); - onion_skinning->set_disabled(dummy_exists); int selected = animation->get_selected(); autoplay->set_disabled(selected != -1 ? (animation->get_item_text(selected).is_empty() ? true : dummy_exists) : true); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 70b31759fc..e624522566 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -206,6 +206,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _current_animation_changed(const String &p_name); void _update_animation(); void _update_player(); + void _set_controls_disabled(bool p_disabled); void _update_animation_list_icons(); void _update_name_dialog_library_dropdown(); void _blend_edited(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 22dbb6e9f2..0a0909ec9f 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -4249,12 +4249,18 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { disk_changed = memnew(ConfirmationDialog); { + disk_changed->set_title(TTR("Files have been modified on disk")); + VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); - Label *dl = memnew(Label); - dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:")); - vbc->add_child(dl); + Label *files_are_newer_label = memnew(Label); + files_are_newer_label->set_text(TTR("The following files are newer on disk.")); + vbc->add_child(files_are_newer_label); + + Label *what_action_label = memnew(Label); + what_action_label->set_text(TTR("What action should be taken?:")); + vbc->add_child(what_action_label); disk_changed_list = memnew(Tree); vbc->add_child(disk_changed_list); @@ -4262,9 +4268,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts).bind(false)); - disk_changed->set_ok_button_text(TTR("Reload")); + disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); - disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &ScriptEditor::_resave_scripts)); } |