summaryrefslogtreecommitdiff
path: root/editor/scene_tree_dock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/scene_tree_dock.cpp')
-rw-r--r--editor/scene_tree_dock.cpp432
1 files changed, 260 insertions, 172 deletions
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 611038a947..03f65cdf52 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,6 +37,7 @@
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_feature_profile.h"
+#include "editor/editor_file_dialog.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -53,6 +54,9 @@
#include "servers/rendering_server.h"
#include "modules/modules_enabled.gen.h" // For regex.
+#ifdef MODULE_REGEX_ENABLED
+#include "editor/rename_dialog.h"
+#endif // MODULE_REGEX_ENABLED
void SceneTreeDock::_nodes_drag_begin() {
if (restore_script_editor_on_drag) {
@@ -75,10 +79,10 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) {
}
}
-void SceneTreeDock::unhandled_key_input(const Ref<InputEvent> &p_event) {
+void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (get_focus_owner() && get_focus_owner()->is_text_field()) {
+ if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
return;
}
@@ -190,7 +194,7 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
break;
}
- if (edited_scene->get_scene_file_path() != "") {
+ if (!edited_scene->get_scene_file_path().is_empty()) {
if (_cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
accept->set_text(vformat(TTR("Cannot instance the scene '%s' because the current scene exists within one of its nodes."), p_files[i]));
accept->popup_centered();
@@ -233,7 +237,7 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
editor_data->get_undo_redo().commit_action();
- editor->push_item(instances[instances.size() - 1]);
+ _push_item(instances[instances.size() - 1]);
for (int i = 0; i < instances.size(); i++) {
emit_signal(SNAME("node_created"), instances[i]);
}
@@ -254,7 +258,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base)
return;
}
- UndoRedo *undo_redo = editor->get_undo_redo();
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Replace with Branch Scene"));
Node *parent = base->get_parent();
@@ -361,8 +365,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
tree->edit_selected();
}
} break;
- case TOOL_NEW:
- case TOOL_REPARENT_TO_NEW_NODE: {
+ case TOOL_REPARENT_TO_NEW_NODE:
+ if (!_validate_no_foreign()) {
+ break;
+ }
+ [[fallthrough]];
+ case TOOL_NEW: {
if (!profile_allow_editing) {
break;
}
@@ -419,6 +427,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!selected_item) {
selected_item = tree->get_root();
+ if (!selected_item) {
+ break;
+ }
}
bool collapsed = _is_collapsed_recursive(selected_item);
@@ -429,7 +440,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
} break;
case TOOL_CUT:
case TOOL_COPY: {
- if (!edited_scene || !_validate_no_foreign()) {
+ if (!edited_scene || (p_tool == TOOL_CUT && !_validate_no_foreign())) {
break;
}
@@ -438,10 +449,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ bool was_empty = false;
if (!node_clipboard.is_empty()) {
_clear_clipboard();
+ } else {
+ was_empty = true;
}
- clipboard_source_scene = editor->get_edited_scene()->get_scene_file_path();
+ clipboard_source_scene = EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path();
selection.sort_custom<Node::Comparator>();
@@ -457,81 +471,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (p_tool == TOOL_CUT) {
_delete_confirm(true);
}
- } break;
- case TOOL_PASTE: {
- if (node_clipboard.is_empty() || !edited_scene) {
- break;
- }
- bool has_cycle = false;
- if (edited_scene->get_scene_file_path() != String()) {
- for (Node *E : node_clipboard) {
- if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) {
- has_cycle = true;
- break;
- }
- }
+ if (was_empty) {
+ _update_create_root_dialog();
}
-
- if (has_cycle) {
- current_option = -1;
- accept->set_text(TTR("Can't paste root node into the same scene."));
- accept->popup_centered();
- break;
- }
-
- Node *paste_parent = edited_scene;
- List<Node *> selection = editor_selection->get_selected_node_list();
- if (selection.size() > 0) {
- paste_parent = selection.back()->get();
- }
-
- Node *owner = paste_parent->get_owner();
- if (!owner) {
- owner = paste_parent;
- }
-
- editor_data->get_undo_redo().create_action(TTR("Paste Node(s)"));
- editor_data->get_undo_redo().add_do_method(editor_selection, "clear");
-
- Map<RES, RES> resource_remap;
- String target_scene = editor->get_edited_scene()->get_scene_file_path();
- if (target_scene != clipboard_source_scene) {
- if (!clipboard_resource_remap.has(target_scene)) {
- Map<RES, RES> remap;
- for (Node *E : node_clipboard) {
- _create_remap_for_node(E, remap);
- }
- clipboard_resource_remap[target_scene] = remap;
- }
- resource_remap = clipboard_resource_remap[target_scene];
- }
-
- for (Node *node : node_clipboard) {
- Map<const Node *, Node *> duplimap;
-
- Node *dup = node->duplicate_from_editor(duplimap, resource_remap);
-
- ERR_CONTINUE(!dup);
-
- editor_data->get_undo_redo().add_do_method(paste_parent, "add_child", dup, true);
-
- for (KeyValue<const Node *, Node *> &E2 : duplimap) {
- Node *d = E2.value;
- editor_data->get_undo_redo().add_do_method(d, "set_owner", owner);
- }
-
- editor_data->get_undo_redo().add_do_method(dup, "set_owner", owner);
- editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", dup);
- editor_data->get_undo_redo().add_undo_method(paste_parent, "remove_child", dup);
- editor_data->get_undo_redo().add_do_reference(dup);
-
- if (node_clipboard.size() == 1) {
- editor_data->get_undo_redo().add_do_method(editor, "push_item", dup);
- }
- }
-
- editor_data->get_undo_redo().commit_action();
+ } break;
+ case TOOL_PASTE: {
+ paste_nodes();
} break;
case TOOL_REPLACE: {
if (!profile_allow_editing) {
@@ -579,7 +525,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
editor_data->get_undo_redo().create_action(TTR("Detach Script"));
- editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)nullptr);
+ editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "push_item", (Script *)nullptr);
for (int i = 0; i < selection.size(); i++) {
Node *n = Object::cast_to<Node>(selection[i]);
@@ -723,7 +669,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
dup->set_name(parent->validate_child_name(dup));
- editor_data->get_undo_redo().add_do_method(add_below_node, "add_sibling", dup);
+ editor_data->get_undo_redo().add_do_method(add_below_node, "add_sibling", dup, true);
for (Node *F : owned) {
if (!duplimap.has(F)) {
@@ -747,7 +693,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().commit_action();
if (dupsingle) {
- editor->push_item(dupsingle);
+ _push_item(dupsingle);
}
} break;
case TOOL_REPARENT: {
@@ -807,7 +753,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
return;
}
- if (node->get_scene_file_path() != String()) {
+ if (!node->get_scene_file_path().is_empty()) {
accept->set_text(TTR("Instantiated scenes can't become root"));
accept->popup_centered();
return;
@@ -815,7 +761,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().create_action(TTR("Make node as Root"));
editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node);
- editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node);
+ editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", node);
editor_data->get_undo_redo().add_do_method(node, "add_child", root, true);
editor_data->get_undo_redo().add_do_method(node, "set_scene_file_path", root->get_scene_file_path());
editor_data->get_undo_redo().add_do_method(root, "set_scene_file_path", String());
@@ -826,7 +772,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().add_undo_method(root, "set_scene_file_path", root->get_scene_file_path());
editor_data->get_undo_redo().add_undo_method(node, "set_scene_file_path", String());
editor_data->get_undo_redo().add_undo_method(node, "remove_child", root);
- editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root);
+ editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", root);
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node, true);
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "move_child", node, node->get_index());
editor_data->get_undo_redo().add_undo_method(root, "set_owner", (Object *)nullptr);
@@ -851,7 +797,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
mne->add_node(root->get_path_to(E.key));
}
- EditorNode::get_singleton()->push_item(mne.ptr());
+ _push_item(mne.ptr());
} break;
@@ -886,7 +832,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *node = remove_list[0];
if (node == editor_data->get_edited_scene_root()) {
msg = vformat(TTR("Delete the root node \"%s\"?"), node->get_name());
- } else if (node->get_scene_file_path() == "" && node->get_child_count() > 0) {
+ } else if (node->get_scene_file_path().is_empty() && node->get_child_count() > 0) {
// Display this message only for non-instantiated scenes
msg = vformat(TTR("Delete node \"%s\" and its children?"), node->get_name());
} else {
@@ -933,12 +879,24 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- if (tocopy != editor_data->get_edited_scene_root() && tocopy->get_scene_file_path() != "") {
+ if (tocopy != editor_data->get_edited_scene_root() && !tocopy->get_scene_file_path().is_empty()) {
accept->set_text(TTR("Can't save the branch of an already instantiated scene.\nTo create a variation of a scene, you can make an inherited scene based on the instantiated scene using Scene > New Inherited Scene... instead."));
accept->popup_centered();
break;
}
+ if (tocopy->get_owner() != scene) {
+ accept->set_text(TTR("Can't save a branch which is a child of an already instantiated scene.\nTo save this branch into its own scene, open the original scene, right click on this branch, and select \"Save Branch as Scene\"."));
+ accept->popup_centered();
+ break;
+ }
+
+ if (scene->get_scene_inherited_state().is_valid() && scene->get_scene_inherited_state()->find_node_by_path(scene->get_path_to(tocopy)) >= 0) {
+ accept->set_text(TTR("Can't save a branch which is part of an inherited scene.\nTo save this branch into its own scene, open the original scene, right click on this branch, and select \"Save Branch as Scene\"."));
+ accept->popup_centered();
+ break;
+ }
+
new_scene_from_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
List<String> extensions;
@@ -1049,7 +1007,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- ERR_FAIL_COND(node->get_scene_file_path() == String());
+ ERR_FAIL_COND(node->get_scene_file_path().is_empty());
undo_redo->create_action(TTR("Make Local"));
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());
@@ -1089,7 +1047,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (node) {
node->set_scene_inherited_state(Ref<SceneState>());
scene_tree->update_tree();
- EditorNode::get_singleton()->get_inspector()->update_tree();
+ InspectorDock::get_inspector_singleton()->update_tree();
}
}
} break;
@@ -1145,7 +1103,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
add_root_node(new_node);
- editor->edit_node(new_node);
+ EditorNode::get_singleton()->edit_node(new_node);
editor_selection->clear();
editor_selection->add_node(new_node);
@@ -1161,7 +1119,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Object *obj = ObjectDB::get_instance(subresources[idx]);
ERR_FAIL_COND(!obj);
- editor->push_item(obj);
+ _push_item(obj);
}
}
}
@@ -1182,10 +1140,10 @@ void SceneTreeDock::_perform_property_drop(Node *p_node, String p_property, RES
void SceneTreeDock::add_root_node(Node *p_node) {
editor_data->get_undo_redo().create_action(TTR("New Scene Root"));
- editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", p_node);
+ editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_node);
editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
editor_data->get_undo_redo().add_do_reference(p_node);
- editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)nullptr);
+ editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
editor_data->get_undo_redo().commit_action();
}
@@ -1252,8 +1210,16 @@ void SceneTreeDock::_notification(int p_what) {
create_root_dialog->add_child(top_row);
+ ScrollContainer *scroll_container = memnew(ScrollContainer);
+ scroll_container->set_name("NodeShortcutsScrollContainer");
+ create_root_dialog->add_child(scroll_container);
+ scroll_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+
VBoxContainer *node_shortcuts = memnew(VBoxContainer);
node_shortcuts->set_name("NodeShortcuts");
+ scroll_container->add_child(node_shortcuts);
+ node_shortcuts->set_h_size_flags(SIZE_EXPAND_FILL);
VBoxContainer *beginner_node_shortcuts = memnew(VBoxContainer);
beginner_node_shortcuts->set_name("BeginnerNodeShortcuts");
@@ -1287,8 +1253,12 @@ void SceneTreeDock::_notification(int p_what) {
button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_custom->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_NEW, false));
- node_shortcuts->add_spacer();
- create_root_dialog->add_child(node_shortcuts);
+ button_clipboard = memnew(Button);
+ node_shortcuts->add_child(button_clipboard);
+ button_clipboard->set_text(TTR("Paste From Clipboard"));
+ button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
+ button_clipboard->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_PASTE, false));
+
_update_create_root_dialog();
} break;
@@ -1300,20 +1270,24 @@ void SceneTreeDock::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
clear_inherit_confirm->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_tool_selected));
} break;
+
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
scene_tree->set_auto_expand_selected(EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected"), false);
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
+ button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
filter->set_clear_button_enabled(true);
} break;
+
case NOTIFICATION_PROCESS: {
bool show_create_root = bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) && get_tree()->get_edited_scene_root() == nullptr;
@@ -1326,7 +1300,6 @@ void SceneTreeDock::_notification(int p_what) {
scene_tree->show();
}
}
-
} break;
}
}
@@ -1357,11 +1330,17 @@ void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root
}
void SceneTreeDock::_load_request(const String &p_path) {
- editor->open_request(p_path);
+ EditorNode::get_singleton()->open_request(p_path);
}
void SceneTreeDock::_script_open_request(const Ref<Script> &p_script) {
- editor->edit_resource(p_script);
+ EditorNode::get_singleton()->edit_resource(p_script);
+}
+
+void SceneTreeDock::_push_item(Object *p_object) {
+ if (!Input::get_singleton()->is_key_pressed(Key::ALT)) {
+ EditorNode::get_singleton()->push_item(p_object);
+ }
}
void SceneTreeDock::_node_selected() {
@@ -1375,7 +1354,7 @@ void SceneTreeDock::_node_selected() {
restore_script_editor_on_drag = true;
}
- editor->push_item(node);
+ _push_item(node);
}
void SceneTreeDock::_node_renamed() {
@@ -1556,7 +1535,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, Map<Node *, NodePath> *p_
}
}
- bool autorename_animation_tracks = bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true));
+ 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);
@@ -1586,7 +1565,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, Map<Node *, NodePath> *p_
for (int i = 0; i < anim->get_track_count(); i++) {
NodePath track_np = anim->track_get_path(i);
- Node *n = root->get_node(track_np);
+ Node *n = root->get_node_or_null(track_np);
if (!n) {
continue;
}
@@ -1699,7 +1678,7 @@ bool SceneTreeDock::_validate_no_instance() {
List<Node *> selection = editor_selection->get_selected_node_list();
for (Node *E : selection) {
- if (E != edited_scene && E->get_scene_file_path() != "") {
+ if (E != edited_scene && !E->get_scene_file_path().is_empty()) {
accept->set_text(TTR("This operation can't be done on instantiated scenes."));
accept->popup_centered();
return false;
@@ -1755,7 +1734,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
Node *validate = new_parent;
while (validate) {
- ERR_FAIL_COND_MSG(p_nodes.find(validate) != -1, "Selection changed at some point. Can't reparent.");
+ ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
validate = validate->get_parent();
}
@@ -1946,7 +1925,7 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
editor_data->get_undo_redo().commit_action();
- editor->push_item(p_script.operator->());
+ _push_item(p_script.operator->());
_update_script_button();
}
@@ -2023,7 +2002,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
return;
}
- editor->get_editor_plugins_over()->make_visible(false);
+ EditorNode::get_singleton()->get_editor_plugins_over()->make_visible(false);
if (p_cut) {
editor_data->get_undo_redo().create_action(TTR("Cut Node(s)"));
@@ -2033,15 +2012,15 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
bool entire_scene = false;
- for (Node *E : remove_list) {
+ for (const Node *E : remove_list) {
if (E == edited_scene) {
entire_scene = true;
}
}
if (entire_scene) {
- editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", (Object *)nullptr);
- editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", edited_scene);
+ editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
+ editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", edited_scene);
editor_data->get_undo_redo().add_undo_method(edited_scene, "set_owner", edited_scene->get_owner());
editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree");
editor_data->get_undo_redo().add_undo_reference(edited_scene);
@@ -2094,12 +2073,12 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
editor->get_viewport_control()->update();
}
- editor->push_item(nullptr);
+ _push_item(nullptr);
- // Fixes the EditorHistory from still offering deleted notes
- EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+ // Fixes the EditorSelectionHistory from still offering deleted notes
+ EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
editor_history->cleanup_history();
- EditorNode::get_singleton()->get_inspector_dock()->call("_prepare_history");
+ InspectorDock::get_singleton()->call("_prepare_history");
}
void SceneTreeDock::_update_script_button() {
@@ -2138,9 +2117,9 @@ void SceneTreeDock::_selection_changed() {
//automatically turn on multi-edit
_tool_selected(TOOL_MULTI_EDIT);
} else if (selection_size == 1) {
- editor->push_item(editor_selection->get_selection().front()->key());
+ _push_item(editor_selection->get_selection().front()->key());
} else if (selection_size == 0) {
- editor->push_item(nullptr);
+ _push_item(nullptr);
}
_update_script_button();
@@ -2169,14 +2148,14 @@ void SceneTreeDock::_do_create(Node *p_parent) {
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name)));
} else {
- editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", child);
+ editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
editor_data->get_undo_redo().add_do_reference(child);
- editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)nullptr);
+ editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
editor_data->get_undo_redo().commit_action();
- editor->push_item(c);
+ _push_item(c);
editor_selection->clear();
editor_selection->add_node(child);
if (Object::cast_to<Control>(c)) {
@@ -2334,7 +2313,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
memdelete(default_oldnode);
}
- editor->push_item(nullptr);
+ _push_item(nullptr);
//reconnect signals
List<MethodInfo> sl;
@@ -2364,7 +2343,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
if (n == edited_scene) {
edited_scene = newnode;
- editor->set_edited_scene(newnode);
+ EditorNode::get_singleton()->set_edited_scene(newnode);
}
//small hack to make collisionshapes and other kind of nodes to work
@@ -2378,7 +2357,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
}
newnode->set_name(newname);
- editor->push_item(newnode);
+ _push_item(newnode);
if (p_remove_old) {
memdelete(n);
@@ -2568,11 +2547,11 @@ void SceneTreeDock::_files_dropped(Vector<String> p_files, NodePath p_to, int p_
property_drop_node = node;
resource_drop_path = res_path;
- bool capitalize = bool(EDITOR_GET("interface/inspector/capitalize_properties"));
+ const EditorPropertyNameProcessor::Style style = InspectorDock::get_singleton()->get_property_name_style();
menu_properties->clear();
for (const String &p : valid_properties) {
- menu_properties->add_item(capitalize ? p.capitalize() : p);
- menu_properties->set_item_metadata(menu_properties->get_item_count() - 1, p);
+ menu_properties->add_item(EditorPropertyNameProcessor::get_singleton()->process_name(p, style));
+ menu_properties->set_item_metadata(-1, p);
}
menu_properties->reset_size();
@@ -2599,6 +2578,10 @@ void SceneTreeDock::_script_dropped(String p_file, NodePath p_to) {
}
void SceneTreeDock::_nodes_dragged(Array p_nodes, NodePath p_to, int p_type) {
+ if (!_validate_no_foreign()) {
+ return;
+ }
+
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.is_empty()) {
@@ -2699,6 +2682,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW);
menu->add_icon_shortcut(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANTIATE);
}
+ menu->add_icon_shortcut(get_theme_icon(SNAME("Collapse"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/expand_collapse_all"), TOOL_EXPAND_COLLAPSE);
menu->add_separator();
existing_script = selected->get_script();
@@ -2709,10 +2693,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
}
if (profile_allow_editing) {
- menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT);
- menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCut"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY);
if (selection.size() == 1 && !node_clipboard.is_empty()) {
- menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
}
menu->add_separator();
}
@@ -2751,24 +2735,30 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
}
if (profile_allow_editing) {
+ bool add_separator = false;
+
if (full_selection.size() == 1) {
+ add_separator = true;
menu->add_icon_shortcut(get_theme_icon(SNAME("Rename"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME);
}
bool can_replace = true;
for (Node *E : selection) {
- if (E != edited_scene && (E->get_owner() != edited_scene || E->get_scene_file_path() != "")) {
+ if (E != edited_scene && (E->get_owner() != edited_scene || !E->get_scene_file_path().is_empty())) {
can_replace = false;
break;
}
}
if (can_replace) {
+ add_separator = true;
menu->add_icon_shortcut(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE);
}
if (scene_tree->get_selected() != edited_scene) {
- menu->add_separator();
+ if (add_separator) {
+ menu->add_separator();
+ }
menu->add_icon_shortcut(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP);
menu->add_icon_shortcut(get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN);
menu->add_icon_shortcut(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE);
@@ -2789,7 +2779,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node_path"), TOOL_COPY_NODE_PATH);
}
- bool is_external = (selection[0]->get_scene_file_path() != "");
+ bool is_external = (!selection[0]->get_scene_file_path().is_empty());
if (is_external) {
bool is_inherited = selection[0]->get_scene_inherited_state() != nullptr;
bool is_top_level = selection[0]->get_owner() == nullptr;
@@ -2836,16 +2826,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->popup();
}
-void SceneTreeDock::_open_tree_menu() {
- menu->clear();
-
- menu->add_icon_shortcut(get_theme_icon(SNAME("Collapse"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/expand_collapse_all"), TOOL_EXPAND_COLLAPSE);
- menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND);
- menu->set_item_checked(menu->get_item_idx_from_text(TTR("Auto Expand to Selected")), EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected"));
-
- menu->reset_size();
- menu->set_position(get_screen_position() + get_local_mouse_position());
- menu->popup();
+void SceneTreeDock::_update_tree_menu() {
+ PopupMenu *tree_menu = button_tree_menu->get_popup();
+ tree_menu->set_item_checked(tree_menu->get_item_idx_from_text(TTR("Auto Expand to Selected")), EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected"));
}
void SceneTreeDock::_filter_changed(const String &p_filter) {
@@ -2902,9 +2885,9 @@ void SceneTreeDock::attach_script_to_selected(bool p_extend) {
Ref<Script> existing = selected->get_script();
String path = selected->get_scene_file_path();
- if (path == "") {
+ if (path.is_empty()) {
String root_path = editor_data->get_edited_scene_root()->get_scene_file_path();
- if (root_path == "") {
+ if (root_path.is_empty()) {
path = String("res://").plus_file(selected->get_name());
} else {
path = root_path.get_base_dir().plus_file(selected->get_name());
@@ -2952,7 +2935,7 @@ void SceneTreeDock::attach_shader_to_selected(int p_preferred_mode) {
}
String path = selected_shader_material->get_path();
- if (path == "") {
+ if (path.is_empty()) {
String root_path;
if (editor_data->get_edited_scene_root()) {
root_path = editor_data->get_edited_scene_root()->get_scene_file_path();
@@ -2963,7 +2946,7 @@ void SceneTreeDock::attach_shader_to_selected(int p_preferred_mode) {
} else {
shader_name = selected_shader_material->get_name();
}
- if (root_path == "") {
+ if (root_path.is_empty()) {
path = String("res://").plus_file(shader_name);
} else {
path = root_path.get_base_dir().plus_file(shader_name);
@@ -2973,7 +2956,7 @@ void SceneTreeDock::attach_shader_to_selected(int p_preferred_mode) {
shader_create_dialog->connect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created));
shader_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
shader_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed));
- shader_create_dialog->config(path, true, true, p_preferred_mode);
+ shader_create_dialog->config(path, true, true, -1, p_preferred_mode);
shader_create_dialog->popup_centered();
}
@@ -2992,6 +2975,112 @@ void SceneTreeDock::open_instance_child_dialog() {
_tool_selected(TOOL_INSTANTIATE, true);
}
+List<Node *> SceneTreeDock::paste_nodes() {
+ List<Node *> pasted_nodes;
+
+ if (node_clipboard.is_empty()) {
+ return pasted_nodes;
+ }
+
+ bool has_cycle = false;
+ if (edited_scene && !edited_scene->get_scene_file_path().is_empty()) {
+ for (Node *E : node_clipboard) {
+ if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) {
+ has_cycle = true;
+ break;
+ }
+ }
+ }
+
+ if (has_cycle) {
+ current_option = -1;
+ accept->set_text(TTR("Can't paste root node into the same scene."));
+ accept->popup_centered();
+ return pasted_nodes;
+ }
+
+ Node *paste_parent = edited_scene;
+ List<Node *> selection = editor_selection->get_selected_node_list();
+ if (selection.size() > 0) {
+ paste_parent = selection.back()->get();
+ }
+
+ Node *owner = nullptr;
+ if (paste_parent) {
+ owner = paste_parent->get_owner();
+ }
+ if (!owner) {
+ owner = paste_parent;
+ }
+
+ UndoRedo &ur = editor_data->get_undo_redo();
+ ur.create_action(TTR("Paste Node(s)"));
+ ur.add_do_method(editor_selection, "clear");
+
+ Map<RES, RES> resource_remap;
+ String target_scene;
+ if (edited_scene) {
+ target_scene = edited_scene->get_scene_file_path();
+ }
+ if (target_scene != clipboard_source_scene) {
+ if (!clipboard_resource_remap.has(target_scene)) {
+ Map<RES, RES> remap;
+ for (Node *E : node_clipboard) {
+ _create_remap_for_node(E, remap);
+ }
+ clipboard_resource_remap[target_scene] = remap;
+ }
+ resource_remap = clipboard_resource_remap[target_scene];
+ }
+
+ for (Node *node : node_clipboard) {
+ Map<const Node *, Node *> duplimap;
+
+ Node *dup = node->duplicate_from_editor(duplimap, resource_remap);
+ ERR_CONTINUE(!dup);
+
+ pasted_nodes.push_back(dup);
+
+ if (!paste_parent) {
+ paste_parent = dup;
+ owner = dup;
+ ur.add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup);
+ } else {
+ ur.add_do_method(paste_parent, "add_child", dup, true);
+ }
+
+ for (KeyValue<const Node *, Node *> &E2 : duplimap) {
+ Node *d = E2.value;
+ if (d != dup) {
+ ur.add_do_method(d, "set_owner", owner);
+ }
+ }
+
+ if (dup != owner) {
+ ur.add_do_method(dup, "set_owner", owner);
+ }
+ ur.add_do_method(editor_selection, "add_node", dup);
+
+ if (dup == paste_parent) {
+ ur.add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
+ } else {
+ ur.add_undo_method(paste_parent, "remove_child", dup);
+ }
+ ur.add_do_reference(dup);
+
+ if (node_clipboard.size() == 1) {
+ ur.add_do_method(EditorNode::get_singleton(), "push_item", dup);
+ }
+ }
+
+ ur.commit_action();
+ return pasted_nodes;
+}
+
+List<Node *> SceneTreeDock::get_node_clipboard() const {
+ return node_clipboard;
+}
+
void SceneTreeDock::add_remote_tree_editor(Control *p_remote) {
ERR_FAIL_COND(remote_tree != nullptr);
add_child(p_remote);
@@ -3040,7 +3129,7 @@ void SceneTreeDock::_local_tree_selected() {
void SceneTreeDock::_update_create_root_dialog() {
BaseButton *toggle = Object::cast_to<BaseButton>(create_root_dialog->get_node(String("NodeShortcutsTopRow/NodeShortcutsToggle")));
- Node *node_shortcuts = create_root_dialog->get_node(String("NodeShortcuts"));
+ Node *node_shortcuts = create_root_dialog->get_node(String("NodeShortcutsScrollContainer/NodeShortcuts"));
if (!toggle || !node_shortcuts) {
return;
@@ -3060,16 +3149,16 @@ void SceneTreeDock::_update_create_root_dialog() {
favorite_nodes->get_child(i)->queue_delete();
}
- FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites.Node"), FileAccess::READ);
-
- if (f) {
+ Ref<FileAccess> f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites.Node"), FileAccess::READ);
+ if (f.is_valid()) {
while (!f->eof_reached()) {
String l = f->get_line().strip_edges();
- if (l != String()) {
+ if (!l.is_empty()) {
Button *button = memnew(Button);
favorite_nodes->add_child(button);
button->set_text(l);
+ button->set_clip_text(true);
String name = l.get_slicec(' ', 0);
if (ScriptServer::is_global_class(name)) {
name = ScriptServer::get_global_class_native_base(name);
@@ -3078,8 +3167,6 @@ void SceneTreeDock::_update_create_root_dialog() {
button->connect("pressed", callable_mp(this, &SceneTreeDock::_favorite_root_selected), make_binds(l));
}
}
-
- memdelete(f);
}
if (!favorite_nodes->is_visible_in_tree()) {
@@ -3091,6 +3178,7 @@ void SceneTreeDock::_update_create_root_dialog() {
beginner_nodes->show();
favorite_nodes->hide();
}
+ button_clipboard->set_visible(!node_clipboard.is_empty());
}
}
@@ -3145,7 +3233,7 @@ void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap)
}
Variant v = p_node->get(E.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
RES res = v;
if (res.is_valid()) {
if (!states_stack_ready) {
@@ -3153,8 +3241,9 @@ void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap)
states_stack_ready = true;
}
- Variant orig = PropertyUtils::get_property_default_value(p_node, E.name, &states_stack);
- if (!PropertyUtils::is_property_value_different(v, orig)) {
+ bool is_valid_default = false;
+ Variant orig = PropertyUtils::get_property_default_value(p_node, E.name, &is_valid_default, &states_stack);
+ if (is_valid_default && !PropertyUtils::is_property_value_different(v, orig)) {
continue;
}
@@ -3182,7 +3271,7 @@ void SceneTreeDock::_create_remap_for_resource(RES p_resource, Map<RES, RES> &r_
}
Variant v = p_resource->get(E.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
RES res = v;
if (res.is_valid()) {
if (res->is_built_in() && !r_remap.has(res)) {
@@ -3215,10 +3304,9 @@ void SceneTreeDock::_update_configuration_warning() {
}
}
-SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) {
+SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) {
singleton = this;
set_name("Scene");
- editor = p_editor;
edited_scene = nullptr;
editor_data = &p_editor_data;
editor_selection = p_editor_selection;
@@ -3237,7 +3325,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KeyModifierMask::CMD | Key::A);
ED_SHORTCUT("scene_tree/instance_scene", TTR("Instantiate Child Scene"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::A);
- ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse All"));
+ ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse Branch"));
ED_SHORTCUT("scene_tree/cut_node", TTR("Cut"), KeyModifierMask::CMD | Key::X);
ED_SHORTCUT("scene_tree/copy_node", TTR("Copy"), KeyModifierMask::CMD | Key::C);
ED_SHORTCUT("scene_tree/paste_node", TTR("Paste"), KeyModifierMask::CMD | Key::V);
@@ -3294,11 +3382,15 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
filter_hbc->add_child(button_detach_script);
button_detach_script->hide();
- button_tree_menu = memnew(Button);
+ button_tree_menu = memnew(MenuButton);
button_tree_menu->set_flat(true);
- button_tree_menu->connect("pressed", callable_mp(this, &SceneTreeDock::_open_tree_menu));
+ button_tree_menu->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_update_tree_menu));
filter_hbc->add_child(button_tree_menu);
+ PopupMenu *tree_menu = button_tree_menu->get_popup();
+ tree_menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND);
+ tree_menu->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(false));
+
button_hb = memnew(HBoxContainer);
vbc->add_child(button_hb);
@@ -3325,6 +3417,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
create_root_dialog = memnew(VBoxContainer);
vbc->add_child(create_root_dialog);
+ create_root_dialog->set_v_size_flags(SIZE_EXPAND_FILL);
create_root_dialog->hide();
scene_tree = memnew(SceneTreeEditor(false, true, true));
@@ -3380,7 +3473,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
add_child(quick_open);
quick_open->connect("quick_open", callable_mp(this, &SceneTreeDock::_quick_open));
- set_process_unhandled_key_input(true);
+ set_process_shortcut_input(true);
delete_dialog = memnew(ConfirmationDialog);
add_child(delete_dialog);
@@ -3407,8 +3500,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
menu_subresources->set_name("Sub-Resources");
menu_subresources->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(false));
menu->add_child(menu_subresources);
- first_enter = true;
- restore_script_editor_on_drag = false;
menu_properties = memnew(PopupMenu);
add_child(menu_properties);
@@ -3422,9 +3513,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
set_process_input(true);
set_process(true);
- profile_allow_editing = true;
- profile_allow_script_editing = true;
-
EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
EDITOR_DEF("_use_favorites_root_selection", false);