Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-update properties when replacing a node #78300

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions editor/plugins/gpu_particles_2d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,8 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
ur->add_do_reference(cpu_particles);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, particles, false, false);
ur->add_undo_reference(particles);
ur->commit_action();
SceneTreeDock::get_singleton()->replace_node(particles, cpu_particles);
ur->commit_action(false);

} break;
case MENU_RESTART: {
Expand Down
7 changes: 2 additions & 5 deletions editor/plugins/gpu_particles_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,8 @@ void GPUParticles3DEditor::_menu_option(int p_option) {

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles3D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
ur->add_do_reference(cpu_particles);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
SceneTreeDock::get_singleton()->replace_node(node, cpu_particles);
ur->commit_action(false);

} break;
case MENU_OPTION_RESTART: {
Expand Down
14 changes: 4 additions & 10 deletions editor/plugins/sprite_2d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,8 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to MeshInstance2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
ur->add_do_reference(mesh_instance);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", mesh_instance, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
SceneTreeDock::get_singleton()->replace_node(node, mesh_instance);
ur->commit_action(false);
}

void Sprite2DEditor::_convert_to_polygon_2d_node() {
Expand Down Expand Up @@ -404,11 +401,8 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to Polygon2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
ur->add_do_reference(polygon_2d_instance);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", polygon_2d_instance, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
SceneTreeDock::get_singleton()->replace_node(node, polygon_2d_instance);
ur->commit_action(false);
}

void Sprite2DEditor::_create_collision_polygon_2d_node() {
Expand Down
112 changes: 102 additions & 10 deletions editor/scene_tree_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2357,16 +2357,12 @@ void SceneTreeDock::_create() {
Variant c = create_dialog->instantiate_selected();

ERR_FAIL_COND(!c);
Node *newnode = Object::cast_to<Node>(c);
ERR_FAIL_COND(!newnode);

ur->add_do_method(this, "replace_node", n, newnode, true, false);
ur->add_do_reference(newnode);
ur->add_undo_method(this, "replace_node", newnode, n, false, false);
ur->add_undo_reference(n);
Node *new_node = Object::cast_to<Node>(c);
ERR_FAIL_COND(!new_node);
replace_node(n, new_node);
}

ur->commit_action();
ur->commit_action(false);
} else if (current_option == TOOL_REPARENT_TO_NEW_NODE) {
List<Node *> selection = editor_selection->get_selected_node_list();
ERR_FAIL_COND(selection.size() <= 0);
Expand Down Expand Up @@ -2420,7 +2416,25 @@ void SceneTreeDock::_create() {
scene_tree->get_scene_tree()->grab_focus();
}

void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) {
void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, p_node);

ur->add_do_method(this, "replace_node", p_node, p_by_node, true, false);
ur->add_do_reference(p_by_node);

_replace_node(p_node, p_by_node, true, false);

ur->add_undo_method(this, "replace_node", p_by_node, p_node, false, false);
ur->add_undo_reference(p_node);

perform_node_replace(nullptr, p_node, p_by_node);

ur->commit_action(false);
}

void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) {
ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "_replace_node() can't be called on a node outside of tree. You might have called it twice.");
Node *n = p_node;
Node *newnode = p_by_node;

Expand Down Expand Up @@ -2493,6 +2507,84 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
}
}

void SceneTreeDock::perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node) {
if (!p_base) {
p_base = edited_scene;
}

if (!p_base) {
return;
}

// Renaming node used in node properties.
List<PropertyInfo> properties;
p_base->get_property_list(&properties);

for (const PropertyInfo &E : properties) {
if (!(E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
continue;
}
String propertyname = E.name;
Variant old_variant = p_base->get(propertyname);
Variant updated_variant = old_variant;
String warn_message;

if (_check_node_recursive(updated_variant, p_node, p_by_node, E.hint_string, warn_message)) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_property(p_base, propertyname, updated_variant);
undo_redo->add_undo_property(p_base, propertyname, old_variant);
p_base->set(propertyname, updated_variant);
if (!warn_message.is_empty()) {
String node_path = (String(edited_scene->get_name()) + "/" + String(edited_scene->get_path_to(p_base))).trim_suffix("/.");
WARN_PRINT(warn_message + vformat(TTR("Removing the node from variable \"%s\" on node \"%s\"."), propertyname, node_path));
}
}
}

for (int i = 0; i < p_base->get_child_count(); i++) {
perform_node_replace(p_base->get_child(i), p_node, p_by_node);
}
}

bool SceneTreeDock::_check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message) {
switch (r_variant.get_type()) {
case Variant::OBJECT: {
if (p_node == r_variant) {
if (p_by_node->is_class(type_hint) || EditorNode::get_singleton()->is_object_of_custom_type(p_by_node, type_hint)) {
r_variant = p_by_node;
} else {
r_variant = memnew(Object);
r_warn_message = vformat(TTR("The node's new type is incompatible with an exported variable (expected %s, but type is %s)."), type_hint, p_by_node->get_class());
}
return true;
}
} break;

case Variant::ARRAY: {
Array a = r_variant;
bool updated = false;
for (int i = 0; i < a.size(); i++) {
Variant value = a[i];
if (_check_node_recursive(value, p_node, p_by_node, type_hint.get_slice(":", 1), r_warn_message)) {
if (!updated) {
a = a.duplicate(); // Need to duplicate for undo-redo to work.
updated = true;
}
a[i] = value;
}
}
if (updated) {
r_variant = a;
return true;
}
} break;
default: {
}
}

return false;
}

void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
Expand Down Expand Up @@ -3636,7 +3728,7 @@ void SceneTreeDock::_bind_methods() {

ClassDB::bind_method(D_METHOD("instantiate"), &SceneTreeDock::instantiate);
ClassDB::bind_method(D_METHOD("get_tree_editor"), &SceneTreeDock::get_tree_editor);
ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node);
ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::_replace_node);

ADD_SIGNAL(MethodInfo("remote_tree_selected"));
ADD_SIGNAL(MethodInfo("add_node_used"));
Expand Down
5 changes: 4 additions & 1 deletion editor/scene_tree_dock.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ class SceneTreeDock : public VBoxContainer {

bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const;
bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames) const;
bool _check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message);
void _replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);

private:
static SceneTreeDock *singleton;
Expand All @@ -307,6 +309,7 @@ class SceneTreeDock : public VBoxContainer {
void set_selected(Node *p_node, bool p_emit_selected = false);
void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames);
void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr);
void perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node);
SceneTreeEditor *get_tree_editor() { return scene_tree; }
EditorData *get_editor_data() { return editor_data; }

Expand All @@ -316,7 +319,7 @@ class SceneTreeDock : public VBoxContainer {
void show_tab_buttons();
void hide_tab_buttons();

void replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
void replace_node(Node *p_node, Node *p_by_node);

void attach_script_to_selected(bool p_extend);
void open_script_dialog(Node *p_for_node, bool p_extend);
Expand Down