Skip to content

Commit

Permalink
Merge pull request #49812 from nekomatata/node-path-editor-update
Browse files Browse the repository at this point in the history
NodePath properly updated in the editor in more cases when nodes are moved or renamed
  • Loading branch information
akien-mga authored Jun 29, 2021
2 parents 9996cd2 + 3e4e530 commit dfdde2c
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 54 deletions.
1 change: 1 addition & 0 deletions doc/classes/NodePath.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</description>
<tutorials>
<link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link>
Expand Down
163 changes: 109 additions & 54 deletions editor/scene_tree_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,9 +1409,102 @@ void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, List<Pai
_fill_path_renames(base_path, new_base_path, p_node, p_renames);
}

bool SceneTreeDock::_update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List<Pair<NodePath, NodePath>> *p_renames) {
NodePath root_path_new = p_root_path;
for (List<Pair<NodePath, NodePath>>::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<Pair<NodePath, NodePath>>::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<Pair<NodePath, NodePath>> *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<Pair<NodePath, NodePath>> *p_renames, Map<Ref<Animation>, Set<int>> *r_rem_anims) {
Map<Ref<Animation>, Set<int>> rem_anims;

if (!r_rem_anims) {
r_rem_anims = &rem_anims;
}
Expand All @@ -1424,60 +1517,22 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP
return;
}

// Renaming node paths used in script instances
if (p_base->get_script_instance()) {
ScriptInstance *si = p_base->get_script_instance();

if (si) {
List<PropertyInfo> properties;
si->get_property_list(&properties);
NodePath root_path = p_base->get_path();

for (List<PropertyInfo>::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<Pair<NodePath, NodePath>>::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<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) {
NodePath rel_path_old = root_path.rel_path_to(F->get().first);
// Renaming node paths used in node properties.
List<PropertyInfo> properties;
p_base->get_property_list(&properties);
NodePath base_root_path = p_base->get_path();

// 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<PropertyInfo>::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);
}
}

Expand Down
3 changes: 3 additions & 0 deletions editor/scene_tree_dock.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ class SceneTreeDock : public VBoxContainer {
static SceneTreeDock *singleton;
static void _update_configuration_warning();

static bool _update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List<Pair<NodePath, NodePath>> *p_renames);
static bool _check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List<Pair<NodePath, NodePath>> *p_renames);

protected:
void _notification(int p_what);
static void _bind_methods();
Expand Down

0 comments on commit dfdde2c

Please sign in to comment.