From daa0977c68faa7b1be3dc3838c07dcdf23cd790e Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Mon, 21 Jun 2021 12:09:00 -0700 Subject: [PATCH] NodePath properly updated in the editor in more cases Fix more cases of node path needing an update when nodes are renamed or moved in the editor. Built-in node properties: Before, node paths were checked only for script export variables. Now all properties are checked from the node, which includes built-in node properties. Allows proper node path updates for nodes like remote transform, physics joints, etc. Arrays and dictionaries: Node paths nested in array and dictionary properties are now also updated in the editor. Also update the documentation to be clear about node path update in the editor and at runtime. Co-authored-by: latorril (cherry picked from commit 3e4e530523e9ce22ef437af80d1beba83b46afad) --- doc/classes/NodePath.xml | 1 + editor/scene_tree_dock.cpp | 163 +++++++++++++++++++++++++------------ editor/scene_tree_dock.h | 3 + 3 files changed, 113 insertions(+), 54 deletions(-) diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 655fd3cfcd3..d7d19024371 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -20,6 +20,7 @@ @"/root/Main" # If your main scene's root node were named "Main". @"/root/MyAutoload" # If you have an autoloaded node or scene. [/codeblock] + [b]Note:[/b] In the editor, [NodePath] properties are automatically updated when moving, renaming or deleting a node in the scene tree, but they are never updated at runtime. https://godotengine.org/asset-library/asset/520 diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 503b096325d..3972d379fa0 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1397,9 +1397,102 @@ void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, List> *p_renames) { + NodePath root_path_new = p_root_path; + for (List>::Element *F = p_renames->front(); F; F = F->next()) { + if (p_root_path == F->get().first) { + root_path_new = F->get().second; + break; + } + } + + // Goes through all paths to check if it's matching. + for (List>::Element *F = p_renames->front(); F; F = F->next()) { + NodePath rel_path_old = p_root_path.rel_path_to(F->get().first); + + // If old path detected, then it needs to be replaced with the new one. + if (p_node_path == rel_path_old) { + NodePath rel_path_new = F->get().second; + + // If not empty, get new relative path. + if (!rel_path_new.is_empty()) { + rel_path_new = root_path_new.rel_path_to(rel_path_new); + } + + p_node_path = rel_path_new; + return true; + } + + // Update the node itself if it has a valid node path and has not been deleted. + if (p_root_path == F->get().first && p_node_path != NodePath() && F->get().second != NodePath()) { + NodePath abs_path = NodePath(String(root_path_new).plus_file(p_node_path)).simplified(); + NodePath rel_path_new = F->get().second.rel_path_to(abs_path); + + p_node_path = rel_path_new; + return true; + } + } + + return false; +} + +bool SceneTreeDock::_check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List> *p_renames) { + switch (p_variant.get_type()) { + case Variant::NODE_PATH: { + NodePath node_path = p_variant; + if (_update_node_path(p_root_path, node_path, p_renames)) { + p_variant = node_path; + return true; + } + } break; + + case Variant::ARRAY: { + Array a = p_variant; + bool updated = false; + for (int i = 0; i < a.size(); i++) { + Variant value = a[i]; + if (_check_node_path_recursive(p_root_path, value, p_renames)) { + if (!updated) { + a = a.duplicate(); // Need to duplicate for undo-redo to work. + updated = true; + } + a[i] = value; + } + } + if (updated) { + p_variant = a; + return true; + } + } break; + + case Variant::DICTIONARY: { + Dictionary d = p_variant; + bool updated = false; + for (int i = 0; i < d.size(); i++) { + Variant value = d.get_value_at_index(i); + if (_check_node_path_recursive(p_root_path, value, p_renames)) { + if (!updated) { + d = d.duplicate(); // Need to duplicate for undo-redo to work. + updated = true; + } + d[d.get_key_at_index(i)] = value; + } + } + if (updated) { + p_variant = d; + return true; + } + } break; + + default: { + } + } + + return false; +} + void SceneTreeDock::perform_node_renames(Node *p_base, List> *p_renames, Map, Set> *r_rem_anims) { Map, Set> rem_anims; - if (!r_rem_anims) { r_rem_anims = &rem_anims; } @@ -1412,60 +1505,22 @@ void SceneTreeDock::perform_node_renames(Node *p_base, Listget_script_instance()) { - ScriptInstance *si = p_base->get_script_instance(); + // Renaming node paths used in node properties. + List properties; + p_base->get_property_list(&properties); + NodePath base_root_path = p_base->get_path(); - if (si) { - List properties; - si->get_property_list(&properties); - NodePath root_path = p_base->get_path(); - - for (List::Element *E = properties.front(); E; E = E->next()) { - String propertyname = E->get().name; - Variant p = p_base->get(propertyname); - if (p.get_type() == Variant::NODE_PATH) { - NodePath root_path_new = root_path; - for (List>::Element *F = p_renames->front(); F; F = F->next()) { - if (root_path == F->get().first) { - root_path_new = F->get().second; - break; - } - } - - // Goes through all paths to check if its matching - for (List>::Element *F = p_renames->front(); F; F = F->next()) { - NodePath rel_path_old = root_path.rel_path_to(F->get().first); - - // if old path detected, then it needs to be replaced with the new one - if (p == rel_path_old) { - NodePath rel_path_new = F->get().second; - - // if not empty, get new relative path - if (!rel_path_new.is_empty()) { - rel_path_new = root_path_new.rel_path_to(F->get().second); - } - - editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new); - editor_data->get_undo_redo().add_undo_property(p_base, propertyname, rel_path_old); - - p_base->set(propertyname, rel_path_new); - break; - } - - // update the node itself if it has a valid node path and has not been deleted - if (root_path == F->get().first && p != NodePath() && F->get().second != NodePath()) { - NodePath abs_path = NodePath(String(root_path).plus_file(p)).simplified(); - NodePath rel_path_new = F->get().second.rel_path_to(abs_path); - - editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new); - editor_data->get_undo_redo().add_undo_property(p_base, propertyname, p); - - p_base->set(propertyname, rel_path_new); - } - } - } - } + for (List::Element *E = properties.front(); E; E = E->next()) { + if (!(E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) { + continue; + } + String propertyname = E->get().name; + Variant old_variant = p_base->get(propertyname); + Variant updated_variant = old_variant; + if (_check_node_path_recursive(base_root_path, updated_variant, p_renames)) { + editor_data->get_undo_redo().add_do_property(p_base, propertyname, updated_variant); + editor_data->get_undo_redo().add_undo_property(p_base, propertyname, old_variant); + p_base->set(propertyname, updated_variant); } } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 4f638533c4a..ae201cee365 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -248,6 +248,9 @@ class SceneTreeDock : public VBoxContainer { bool profile_allow_editing; bool profile_allow_script_editing; + static bool _update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List> *p_renames); + static bool _check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List> *p_renames); + protected: void _notification(int p_what); static void _bind_methods();