|
@@ -87,6 +87,12 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) {
|
|
|
_tool_selected(TOOL_INSTANCE);
|
|
|
} else if (ED_IS_SHORTCUT("scene_tree/expand_collapse_all", p_event)) {
|
|
|
_tool_selected(TOOL_EXPAND_COLLAPSE);
|
|
|
+ } else if (ED_IS_SHORTCUT("scene_tree/cut_node", p_event)) {
|
|
|
+ _tool_selected(TOOL_CUT);
|
|
|
+ } else if (ED_IS_SHORTCUT("scene_tree/copy_node", p_event)) {
|
|
|
+ _tool_selected(TOOL_COPY);
|
|
|
+ } else if (ED_IS_SHORTCUT("scene_tree/paste_node", p_event)) {
|
|
|
+ _tool_selected(TOOL_PASTE);
|
|
|
} else if (ED_IS_SHORTCUT("scene_tree/change_node_type", p_event)) {
|
|
|
_tool_selected(TOOL_REPLACE);
|
|
|
} else if (ED_IS_SHORTCUT("scene_tree/duplicate", p_event)) {
|
|
@@ -397,6 +403,99 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
|
|
tree->ensure_cursor_is_visible();
|
|
|
|
|
|
} break;
|
|
|
+ case TOOL_CUT:
|
|
|
+ case TOOL_COPY: {
|
|
|
+ if (!edited_scene || !_validate_no_foreign()) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Node *> selection = editor_selection->get_selected_node_list();
|
|
|
+ if (selection.size() == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!node_clipboard.is_empty()) {
|
|
|
+ _clear_clipboard();
|
|
|
+ }
|
|
|
+
|
|
|
+ selection.sort_custom<Node::Comparator>();
|
|
|
+
|
|
|
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
|
|
|
+ Node *node = E->get();
|
|
|
+ Map<const Node *, Node *> duplimap;
|
|
|
+ Node *dup = node->duplicate_from_editor(duplimap);
|
|
|
+
|
|
|
+ ERR_CONTINUE(!dup);
|
|
|
+
|
|
|
+ node_clipboard.push_back(dup);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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_filename() != String()) {
|
|
|
+ for (List<Node *>::Element *E = node_clipboard.front(); E; E = E->next()) {
|
|
|
+ if (edited_scene->get_filename() == E->get()->get_filename()) {
|
|
|
+ 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();
|
|
|
+ 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");
|
|
|
+
|
|
|
+ for (List<Node *>::Element *E = node_clipboard.front(); E; E = E->next()) {
|
|
|
+ Node *node = E->get();
|
|
|
+ Map<const Node *, Node *> duplimap;
|
|
|
+ Node *dup = node->duplicate_from_editor(duplimap);
|
|
|
+
|
|
|
+ ERR_CONTINUE(!dup);
|
|
|
+
|
|
|
+ editor_data->get_undo_redo().add_do_method(paste_parent, "add_child", dup);
|
|
|
+
|
|
|
+ for (Map<const Node *, Node *>::Element *E2 = duplimap.front(); E2; E2 = E2->next()) {
|
|
|
+ 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_REPLACE: {
|
|
|
if (!profile_allow_editing) {
|
|
|
break;
|
|
@@ -1795,7 +1894,7 @@ void SceneTreeDock::_toggle_editable_children(Node *p_node) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void SceneTreeDock::_delete_confirm() {
|
|
|
+void SceneTreeDock::_delete_confirm(bool p_cut) {
|
|
|
List<Node *> remove_list = editor_selection->get_selected_node_list();
|
|
|
|
|
|
if (remove_list.is_empty()) {
|
|
@@ -1804,7 +1903,11 @@ void SceneTreeDock::_delete_confirm() {
|
|
|
|
|
|
editor->get_editor_plugins_over()->make_visible(false);
|
|
|
|
|
|
- editor_data->get_undo_redo().create_action(TTR("Remove Node(s)"));
|
|
|
+ if (p_cut) {
|
|
|
+ editor_data->get_undo_redo().create_action(TTR("Cut Node(s)"));
|
|
|
+ } else {
|
|
|
+ editor_data->get_undo_redo().create_action(TTR("Remove Node(s)"));
|
|
|
+ }
|
|
|
|
|
|
bool entire_scene = false;
|
|
|
|
|
@@ -2444,6 +2547,13 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
|
|
}
|
|
|
|
|
|
if (profile_allow_script_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);
|
|
|
+ if (selection.size() == 1 && !node_clipboard.is_empty()) {
|
|
|
+ menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
|
|
|
+ }
|
|
|
+ menu->add_separator();
|
|
|
+
|
|
|
bool add_separator = false;
|
|
|
|
|
|
if (full_selection.size() == 1) {
|
|
@@ -2775,6 +2885,13 @@ void SceneTreeDock::_feature_profile_changed() {
|
|
|
_update_script_button();
|
|
|
}
|
|
|
|
|
|
+void SceneTreeDock::_clear_clipboard() {
|
|
|
+ for (List<Node *>::Element *E = node_clipboard.front(); E; E = E->next()) {
|
|
|
+ memdelete(E->get());
|
|
|
+ }
|
|
|
+ node_clipboard.clear();
|
|
|
+}
|
|
|
+
|
|
|
void SceneTreeDock::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("_set_owners"), &SceneTreeDock::_set_owners);
|
|
|
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &SceneTreeDock::_unhandled_key_input);
|
|
@@ -2806,6 +2923,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
|
|
|
ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KEY_MASK_CMD | KEY_A);
|
|
|
ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene"));
|
|
|
ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse All"));
|
|
|
+ ED_SHORTCUT("scene_tree/cut_node", TTR("Cut"), KEY_MASK_CMD | KEY_X);
|
|
|
+ ED_SHORTCUT("scene_tree/copy_node", TTR("Copy"), KEY_MASK_CMD | KEY_C);
|
|
|
+ ED_SHORTCUT("scene_tree/paste_node", TTR("Paste"), KEY_MASK_CMD | KEY_V);
|
|
|
ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type"));
|
|
|
ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script"));
|
|
|
ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script"));
|
|
@@ -2818,7 +2938,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
|
|
|
ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root"));
|
|
|
ED_SHORTCUT("scene_tree/merge_from_scene", TTR("Merge From Scene"));
|
|
|
ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene"));
|
|
|
- ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KEY_MASK_CMD | KEY_C);
|
|
|
+ ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_C);
|
|
|
ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KEY_MASK_SHIFT | KEY_DELETE);
|
|
|
ED_SHORTCUT("scene_tree/delete", TTR("Delete"), KEY_DELETE);
|
|
|
|
|
@@ -2936,7 +3056,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
|
|
|
|
|
|
delete_dialog = memnew(ConfirmationDialog);
|
|
|
add_child(delete_dialog);
|
|
|
- delete_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_delete_confirm));
|
|
|
+ delete_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_delete_confirm), varray(false));
|
|
|
|
|
|
editable_instance_remove_dialog = memnew(ConfirmationDialog);
|
|
|
add_child(editable_instance_remove_dialog);
|
|
@@ -2981,3 +3101,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
|
|
|
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
|
|
|
EDITOR_DEF("_use_favorites_root_selection", false);
|
|
|
}
|
|
|
+
|
|
|
+SceneTreeDock::~SceneTreeDock() {
|
|
|
+ if (!node_clipboard.is_empty()) {
|
|
|
+ _clear_clipboard();
|
|
|
+ }
|
|
|
+}
|