diff options
Diffstat (limited to 'editor/scene_tree_dock.cpp')
-rw-r--r-- | editor/scene_tree_dock.cpp | 291 |
1 files changed, 196 insertions, 95 deletions
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d1e2ba0fe0..d882c4652d 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "scene_tree_dock.h" +#include "node_dock.h" #include "core/config/project_settings.h" #include "core/input/input.h" @@ -54,6 +55,7 @@ #include "editor/plugins/script_editor_plugin.h" #include "editor/reparent_dialog.h" #include "editor/shader_create_dialog.h" +#include "scene/animation/animation_tree.h" #include "scene/gui/check_box.h" #include "scene/main/window.h" #include "scene/property_utils.h" @@ -377,6 +379,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } if (editor_selection->get_selected_node_list().size() > 1) { + if (!_validate_no_foreign()) { + break; + } rename_dialog->popup_centered(); } } break; @@ -387,6 +392,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } Tree *tree = scene_tree->get_scene_tree(); if (tree->is_anything_selected()) { + if (!_validate_no_foreign()) { + break; + } tree->grab_focus(); tree->edit_selected(); } @@ -493,16 +501,25 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { // Preserve ownership relations ready for pasting. List<Node *> owned; - node->get_owned_by(node->get_owner() ? node->get_owner() : node, &owned); + Node *owner = node; + while (owner) { + List<Node *> cur_owned; + node->get_owned_by(owner, &cur_owned); + owner = owner->get_owner(); + for (Node *F : cur_owned) { + owned.push_back(F); + } + } for (Node *F : owned) { if (!duplimap.has(F) || F == node) { continue; } Node *d = duplimap[F]; - // Only use this as a marker that ownership needs to be assigned when pasting. - // The actual owner doesn't matter. - d->set_owner(dup); + // Only use nullptr as a marker that ownership may need to be assigned when pasting. + // The ownership is subsequently tracked in the node_clipboard_edited_scene_owned list. + d->set_owner(nullptr); + node_clipboard_edited_scene_owned.insert(d); } node_clipboard.push_back(dup); @@ -680,10 +697,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } - if (!_validate_no_foreign()) { - break; - } - List<Node *> selection = editor_selection->get_selected_node_list(); if (selection.size() == 0) { break; @@ -703,7 +716,15 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *parent = node->get_parent(); List<Node *> owned; - node->get_owned_by(node->get_owner(), &owned); + Node *owner = node; + while (owner) { + List<Node *> cur_owned; + node->get_owned_by(owner, &cur_owned); + owner = owner->get_owner(); + for (Node *F : cur_owned) { + owned.push_back(F); + } + } HashMap<const Node *, Node *> duplimap; Node *dup = node->duplicate_from_editor(duplimap); @@ -723,9 +744,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { continue; } Node *d = duplimap[F]; - undo_redo->add_do_method(d, "set_owner", node->get_owner()); + undo_redo->add_do_method(d, "set_owner", edited_scene); } undo_redo->add_do_method(editor_selection, "add_node", dup); + undo_redo->add_do_method(dup, "set_owner", edited_scene); undo_redo->add_undo_method(parent, "remove_child", dup); undo_redo->add_do_reference(dup); @@ -1040,7 +1062,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { // Fire confirmation dialog when children are editable. if (editable && !placeholder) { - placeholder_editable_instance_remove_dialog->set_text(TTR("Enabling \"Load As Placeholder\" will disable \"Editable Children\" and cause all properties of the node to be reverted to their default.")); + placeholder_editable_instance_remove_dialog->set_text(TTR("Enabling \"Load as Placeholder\" will disable \"Editable Children\" and cause all properties of the node to be reverted to their default.")); placeholder_editable_instance_remove_dialog->popup_centered(); break; } @@ -1081,6 +1103,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { undo_redo->add_do_method(node, "set_scene_file_path", ""); undo_redo->add_undo_method(node, "set_scene_file_path", node->get_scene_file_path()); _node_replace_owner(node, node, root); + _node_strip_signal_inheritance(node); + NodeDock::get_singleton()->set_node(node); // Refresh. undo_redo->add_do_method(scene_tree, "update_tree"); undo_redo->add_undo_method(scene_tree, "update_tree"); undo_redo->commit_action(); @@ -1144,9 +1168,25 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { return; } } - bool enabling = !first_selected->get()->is_unique_name_in_owner(); List<Node *> full_selection = editor_selection->get_full_selected_node_list(); + + // Check if all the nodes for this operation are invalid, and if they are, pop up a dialog and end here. + bool all_nodes_owner_invalid = true; + for (Node *node : full_selection) { + if (node->get_owner() == get_tree()->get_edited_scene_root()) { + all_nodes_owner_invalid = false; + break; + } + } + if (all_nodes_owner_invalid) { + accept->set_text(TTR("Can't toggle unique name for nodes in subscene!")); + accept->popup_centered(); + return; + } + + bool enabling = !first_selected->get()->is_unique_name_in_owner(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (enabling) { @@ -1158,6 +1198,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (node->is_unique_name_in_owner()) { continue; } + if (node->get_owner() != get_tree()->get_edited_scene_root()) { + continue; + } + StringName name = node->get_name(); if (new_unique_names.find(name) != -1 || get_tree()->get_edited_scene_root()->get_node_or_null(UNIQUE_NODE_PREFIX + String(name)) != nullptr) { cant_be_set_unique_names.push_back(name); @@ -1191,6 +1235,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!node->is_unique_name_in_owner()) { continue; } + if (node->get_owner() != get_tree()->get_edited_scene_root()) { + continue; + } undo_redo->add_do_method(node, "set_unique_name_in_owner", false); undo_redo->add_undo_method(node, "set_unique_name_in_owner", true); } @@ -1479,6 +1526,19 @@ void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root } } +void SceneTreeDock::_node_strip_signal_inheritance(Node *p_node) { + List<Object::Connection> conns; + p_node->get_all_signal_connections(&conns); + + for (Object::Connection conn : conns) { + conn.signal.disconnect(conn.callable); + conn.signal.connect(conn.callable, conn.flags & ~CONNECT_INHERITED); + } + for (int i = 0; i < p_node->get_child_count(); i++) { + _node_strip_signal_inheritance(p_node->get_child(i)); + } +} + void SceneTreeDock::_load_request(const String &p_path) { EditorNode::get_singleton()->open_request(p_path); _local_tree_selected(); @@ -1550,6 +1610,14 @@ void SceneTreeDock::_fill_path_renames(Vector<StringName> base_path, Vector<Stri } bool SceneTreeDock::_has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const { + // Skip if this node will be deleted. + for (const Node *F : p_to_delete) { + if (F == p_node || F->is_ancestor_of(p_node)) { + return false; + } + } + + // This is an AnimationPlayer that survives the deletion. AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); if (ap) { Node *root = ap->get_node(ap->get_root_node()); @@ -1578,11 +1646,13 @@ bool SceneTreeDock::_has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delet } } + // Recursively check child nodes. for (int i = 0; i < p_node->get_child_count(); i++) { if (_has_tracks_to_delete(p_node->get_child(i), p_to_delete)) { return true; } } + return false; } @@ -1698,6 +1768,11 @@ bool SceneTreeDock::_check_node_path_recursive(Node *p_root_node, Variant &r_var break; } + if (Object::cast_to<Animation>(resource)) { + // Animation resources are handled by animation editor. + break; + } + List<PropertyInfo> properties; resource->get_property_list(&properties); @@ -1745,95 +1820,93 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath> return; } - // Renaming node paths 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; - if (_check_node_path_recursive(p_base, updated_variant, p_renames)) { - 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); - } - } - bool autorename_animation_tracks = bool(EDITOR_GET("editors/animation/autorename_animation_tracks")); - if (autorename_animation_tracks && Object::cast_to<AnimationPlayer>(p_base)) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_base); - List<StringName> anims; - ap->get_animation_list(&anims); - Node *root = ap->get_node(ap->get_root_node()); + AnimationMixer *mixer = Object::cast_to<AnimationMixer>(p_base); + if (autorename_animation_tracks && mixer) { + // Don't rename if we're an AnimationTree pointing to an AnimationPlayer + bool points_to_other_animation_player = false; + AnimationTree *at = Object::cast_to<AnimationTree>(mixer); + if (at) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(at->get_node_or_null(at->get_animation_player())); + if (ap) { + points_to_other_animation_player = true; + } + } - if (root) { - HashMap<Node *, NodePath>::Iterator found_root_path = p_renames->find(root); - NodePath new_root_path = found_root_path ? found_root_path->value : root->get_path(); - if (!new_root_path.is_empty()) { // No renaming if root node is deleted. - for (const StringName &E : anims) { - Ref<Animation> anim = ap->get_animation(E); - if (!r_rem_anims->has(anim)) { - r_rem_anims->insert(anim, HashSet<int>()); - HashSet<int> &ran = r_rem_anims->find(anim)->value; - for (int i = 0; i < anim->get_track_count(); i++) { - ran.insert(i); + if (!points_to_other_animation_player) { + List<StringName> anims; + mixer->get_animation_list(&anims); + Node *root = mixer->get_node(mixer->get_root_node()); + + if (root) { + HashMap<Node *, NodePath>::Iterator found_root_path = p_renames->find(root); + NodePath new_root_path = found_root_path ? found_root_path->value : root->get_path(); + if (!new_root_path.is_empty()) { // No renaming if root node is deleted. + for (const StringName &E : anims) { + Ref<Animation> anim = mixer->get_animation(E); + if (!r_rem_anims->has(anim)) { + r_rem_anims->insert(anim, HashSet<int>()); + HashSet<int> &ran = r_rem_anims->find(anim)->value; + for (int i = 0; i < anim->get_track_count(); i++) { + ran.insert(i); + } } - } - HashSet<int> &ran = r_rem_anims->find(anim)->value; - - if (anim.is_null()) { - continue; - } - - int tracks_removed = 0; + HashSet<int> &ran = r_rem_anims->find(anim)->value; - for (int i = 0; i < anim->get_track_count(); i++) { - NodePath track_np = anim->track_get_path(i); - Node *n = root->get_node_or_null(track_np); - if (!n) { + if (anim.is_null() || EditorNode::get_singleton()->is_resource_read_only(anim)) { continue; } - if (!ran.has(i)) { - continue; //channel was removed - } + int tracks_removed = 0; - HashMap<Node *, NodePath>::Iterator found_path = p_renames->find(n); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - if (found_path) { - if (found_path->value.is_empty()) { - //will be erased - - int idx = i - tracks_removed; - tracks_removed++; - - undo_redo->add_do_method(anim.ptr(), "remove_track", idx); - undo_redo->add_undo_method(anim.ptr(), "add_track", anim->track_get_type(i), idx); - undo_redo->add_undo_method(anim.ptr(), "track_set_path", idx, track_np); - undo_redo->add_undo_method(anim.ptr(), "track_set_interpolation_type", idx, anim->track_get_interpolation_type(i)); - for (int j = 0; j < anim->track_get_key_count(i); j++) { - undo_redo->add_undo_method(anim.ptr(), "track_insert_key", idx, anim->track_get_key_time(i, j), anim->track_get_key_value(i, j), anim->track_get_key_transition(i, j)); - } + for (int i = 0; i < anim->get_track_count(); i++) { + if (anim->track_is_imported(i)) { + continue; + } - ran.erase(i); //byebye channel + NodePath track_np = anim->track_get_path(i); - } else { - //will be renamed - NodePath rel_path = new_root_path.rel_path_to(found_path->value); + Node *n = root->get_node_or_null(track_np); + if (!n) { + continue; + } + + if (!ran.has(i)) { + continue; //channel was removed + } - NodePath new_path = NodePath(rel_path.get_names(), track_np.get_subnames(), false); - if (new_path == track_np) { - continue; //bleh + HashMap<Node *, NodePath>::Iterator found_path = p_renames->find(n); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + if (found_path) { + if (found_path->value.is_empty()) { + //will be erased + + int idx = i - tracks_removed; + tracks_removed++; + + undo_redo->add_do_method(anim.ptr(), "remove_track", idx); + undo_redo->add_undo_method(anim.ptr(), "add_track", anim->track_get_type(i), idx); + undo_redo->add_undo_method(anim.ptr(), "track_set_path", idx, track_np); + undo_redo->add_undo_method(anim.ptr(), "track_set_interpolation_type", idx, anim->track_get_interpolation_type(i)); + for (int j = 0; j < anim->track_get_key_count(i); j++) { + undo_redo->add_undo_method(anim.ptr(), "track_insert_key", idx, anim->track_get_key_time(i, j), anim->track_get_key_value(i, j), anim->track_get_key_transition(i, j)); + } + + ran.erase(i); //byebye channel + + } else { + //will be renamed + NodePath rel_path = new_root_path.rel_path_to(found_path->value); + + NodePath new_path = NodePath(rel_path.get_names(), track_np.get_subnames(), false); + if (new_path == track_np) { + continue; //bleh + } + undo_redo->add_do_method(anim.ptr(), "track_set_path", i, new_path); + undo_redo->add_undo_method(anim.ptr(), "track_set_path", i, track_np); } - undo_redo->add_do_method(anim.ptr(), "track_set_path", i, new_path); - undo_redo->add_undo_method(anim.ptr(), "track_set_path", i, track_np); } } } @@ -1842,6 +1915,25 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath> } } + // Renaming node paths 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; + if (_check_node_path_recursive(p_base, updated_variant, p_renames)) { + 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); + } + } + for (int i = 0; i < p_base->get_child_count(); i++) { perform_node_renames(p_base->get_child(i), p_renames, r_rem_anims); } @@ -2136,8 +2228,13 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { } undo_redo->commit_action(); + // Avoid changing the currently edited object. + Object *edited_object = InspectorDock::get_inspector_singleton()->get_edited_object(); + _push_item(p_script.ptr()); _update_script_button(); + + InspectorDock::get_inspector_singleton()->edit(edited_object); } void SceneTreeDock::_shader_created(Ref<Shader> p_shader) { @@ -2959,7 +3056,7 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) { Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(obj); if (menu->get_item_count() == 0) { - menu->add_submenu_item(TTR("Sub-Resources"), "Sub-Resources"); + menu->add_submenu_item(TTR("Sub-Resources"), "SubResources"); } menu_subresources->add_icon_item(icon, E.name.capitalize(), EDIT_SUBRESOURCE_BASE + subresources.size()); menu_subresources->set_item_indent(-1, p_depth); @@ -3149,13 +3246,13 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { bool placeholder = selection[0]->get_scene_instance_load_placeholder(); if (profile_allow_editing) { menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); - menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); + menu->add_check_item(TTR("Load as Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); } menu->add_icon_item(get_editor_theme_icon(SNAME("Load")), TTR("Open in Editor"), TOOL_SCENE_OPEN); if (profile_allow_editing) { menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); - menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); + menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load as Placeholder")), placeholder); } } } @@ -3493,14 +3590,17 @@ List<Node *> SceneTreeDock::paste_nodes(bool p_paste_as_sibling) { for (KeyValue<const Node *, Node *> &E2 : duplimap) { Node *d = E2.value; - // When copying, all nodes that should have an owner assigned here were given node as an owner. - if (d != dup && E2.key->get_owner() == node) { - ur->add_do_method(d, "set_owner", owner); + // When copying, all nodes that should have an owner assigned here were given nullptr as an owner + // and added to the node_clipboard_edited_scene_owned list. + if (d != dup && E2.key->get_owner() == nullptr) { + if (node_clipboard_edited_scene_owned.find(const_cast<Node *>(E2.key))) { + ur->add_do_method(d, "set_owner", edited_scene); + } } } if (dup != owner) { - ur->add_do_method(dup, "set_owner", owner); + ur->add_do_method(dup, "set_owner", edited_scene); } ur->add_do_method(editor_selection, "add_node", dup); @@ -3647,6 +3747,7 @@ void SceneTreeDock::_clear_clipboard() { memdelete(E); } node_clipboard.clear(); + node_clipboard_edited_scene_owned.clear(); clipboard_resource_remap.clear(); } @@ -4041,7 +4142,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec menu->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(false)); menu_subresources = memnew(PopupMenu); - menu_subresources->set_name("Sub-Resources"); + menu_subresources->set_name("SubResources"); menu_subresources->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(false)); menu->add_child(menu_subresources); |