Merge pull request #78300 from ajreckof/fix-replace-node-destroying-Node-properties

Auto-update properties when replacing a node
This commit is contained in:
Yuri Sizov 2023-08-03 22:37:40 +02:00
commit e4b8dc81b8
5 changed files with 114 additions and 31 deletions

View File

@ -116,11 +116,8 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles2D")); ur->create_action(TTR("Convert to CPUParticles2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false); SceneTreeDock::get_singleton()->replace_node(particles, cpu_particles);
ur->add_do_reference(cpu_particles); ur->commit_action(false);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, particles, false, false);
ur->add_undo_reference(particles);
ur->commit_action();
} break; } break;
case MENU_RESTART: { case MENU_RESTART: {

View File

@ -277,11 +277,8 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles3D")); ur->create_action(TTR("Convert to CPUParticles3D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false); SceneTreeDock::get_singleton()->replace_node(node, cpu_particles);
ur->add_do_reference(cpu_particles); ur->commit_action(false);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
} break; } break;
case MENU_OPTION_RESTART: { case MENU_OPTION_RESTART: {

View File

@ -346,11 +346,8 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to MeshInstance2D")); ur->create_action(TTR("Convert to MeshInstance2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false); SceneTreeDock::get_singleton()->replace_node(node, mesh_instance);
ur->add_do_reference(mesh_instance); ur->commit_action(false);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", mesh_instance, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
} }
void Sprite2DEditor::_convert_to_polygon_2d_node() { void Sprite2DEditor::_convert_to_polygon_2d_node() {
@ -404,11 +401,8 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to Polygon2D")); ur->create_action(TTR("Convert to Polygon2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false); SceneTreeDock::get_singleton()->replace_node(node, polygon_2d_instance);
ur->add_do_reference(polygon_2d_instance); ur->commit_action(false);
ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", polygon_2d_instance, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
} }
void Sprite2DEditor::_create_collision_polygon_2d_node() { void Sprite2DEditor::_create_collision_polygon_2d_node() {

View File

@ -2363,16 +2363,12 @@ void SceneTreeDock::_create() {
Variant c = create_dialog->instantiate_selected(); Variant c = create_dialog->instantiate_selected();
ERR_FAIL_COND(!c); ERR_FAIL_COND(!c);
Node *newnode = Object::cast_to<Node>(c); Node *new_node = Object::cast_to<Node>(c);
ERR_FAIL_COND(!newnode); ERR_FAIL_COND(!new_node);
replace_node(n, new_node);
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);
} }
ur->commit_action(); ur->commit_action(false);
} else if (current_option == TOOL_REPARENT_TO_NEW_NODE) { } else if (current_option == TOOL_REPARENT_TO_NEW_NODE) {
List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *> selection = editor_selection->get_selected_node_list();
ERR_FAIL_COND(selection.size() <= 0); ERR_FAIL_COND(selection.size() <= 0);
@ -2426,7 +2422,25 @@ void SceneTreeDock::_create() {
scene_tree->get_scene_tree()->grab_focus(); 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 *n = p_node;
Node *newnode = p_by_node; Node *newnode = p_by_node;
@ -2499,6 +2513,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) { void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene; edited_scene = p_scene;
} }
@ -3635,7 +3727,7 @@ void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("instantiate"), &SceneTreeDock::instantiate); 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("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("remote_tree_selected"));
ADD_SIGNAL(MethodInfo("add_node_used")); ADD_SIGNAL(MethodInfo("add_node_used"));

View File

@ -281,6 +281,8 @@ class SceneTreeDock : public VBoxContainer {
bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const; 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_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: private:
static SceneTreeDock *singleton; static SceneTreeDock *singleton;
@ -306,6 +308,7 @@ public:
void set_selected(Node *p_node, bool p_emit_selected = false); 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 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_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; } SceneTreeEditor *get_tree_editor() { return scene_tree; }
EditorData *get_editor_data() { return editor_data; } EditorData *get_editor_data() { return editor_data; }
@ -315,7 +318,7 @@ public:
void show_tab_buttons(); void show_tab_buttons();
void hide_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 attach_script_to_selected(bool p_extend);
void open_script_dialog(Node *p_for_node, bool p_extend); void open_script_dialog(Node *p_for_node, bool p_extend);