diff --git a/editor/icons/icon_cut_node.svg b/editor/icons/icon_cut_node.svg new file mode 100644 index 000000000000..a355716ebd2c --- /dev/null +++ b/editor/icons/icon_cut_node.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/editor/icons/icon_paste_node.svg b/editor/icons/icon_paste_node.svg new file mode 100644 index 000000000000..c9f69d2cac6e --- /dev/null +++ b/editor/icons/icon_paste_node.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 4d86030e7d6e..4f09e6c9ac54 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -95,6 +95,10 @@ void SceneTreeDock::_unhandled_key_input(Ref p_event) { _tool_selected(TOOL_ERASE, true); } else if (ED_IS_SHORTCUT("scene_tree/copy_node_path", p_event)) { _tool_selected(TOOL_COPY_NODE_PATH); + } else if (ED_IS_SHORTCUT("scene_tree/cut_node", p_event)) { + _tool_selected(TOOL_CUT_NODE); + } else if (ED_IS_SHORTCUT("scene_tree/paste_node", p_event)) { + _tool_selected(TOOL_PASTE_NODE); } else if (ED_IS_SHORTCUT("scene_tree/delete", p_event)) { _tool_selected(TOOL_ERASE); } @@ -480,6 +484,96 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor->push_item(dupsingle); } break; + case TOOL_CUT_NODE: { + if (!edited_scene) + break; + + if (editor_selection->is_selected(edited_scene)) { + + current_option = -1; + //accept->get_cancel()->hide(); + accept->get_ok()->set_text(TTR("I see..")); + accept->set_text(TTR("This operation can't be done on the tree root.")); + accept->popup_centered_minsize(); + break; + } + + if (!_validate_no_foreign()) + break; + + List selection = editor_selection->get_selected_node_list(); + if (selection.size() == 0) + break; + + for (List::Element *E = selection.front(); E; E = E->next()) { + + cutpastecopy_map m; + m.action = NODE_CUT; + Node *node = E->get(); + if (node) { + m.node = node; + + cpc_map->push_back(m); + } + } + } break; + case TOOL_PASTE_NODE: { + if (!edited_scene) + break; + + if (editor->get_edited_scene()->get_tree()->get_node_count() == 0) { + current_option = -1; + //accept->get_cancel()->hide(); + accept->get_ok()->set_text(TTR("I see..")); + accept->set_text(TTR("This operation can't be done without having the tree root.")); + accept->popup_centered_minsize(); + break; + } + + if (!_validate_no_foreign()) + break; + + if (cpc_map->size() == 0) + break; + + List selection = editor_selection->get_selected_node_list(); + // If nothing selected, then lets try to select root node. + if (selection.size() == 0) { + Node *p = editor->get_edited_scene(); + // ...and paste it to there. + if (p) + selection.push_back(p); + } + + // Should be just 1, we do not support multiple selection here. + if (selection.size() != 1) + break; + + // Determine destination node for cut/copy(todo) operation. + Node *to_node = NULL; + List::Element *E = selection.front(); + if (E) { + to_node = E->get(); + + if (!to_node) + break; // something wrong with selected node. + } + + Vector nodes; + while (cpc_map->size() != 0) { + cutpastecopy_map item = cpc_map->get(0); + + // Cut operation. + if (item.action == NODE_CUT) + nodes.push_back(item.node); + + cpc_map->remove(0); + } + + // Perform nodes cut-paste operation via reparenting nodes. + if (nodes.size() > 0) + _do_reparent(to_node, -1, nodes, true); + } break; case TOOL_REPARENT: { if (!scene_tree->get_selected()) @@ -1749,6 +1843,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + menu->add_separator(); + menu->add_icon_shortcut(get_icon("CutNode", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT_NODE); + menu->add_icon_shortcut(get_icon("PasteNode", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE_NODE); + if (selection.size() == 1) { menu->add_separator(); menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); @@ -1928,6 +2026,8 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel 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/cut_node", TTR("Cut Node"), KEY_MASK_CMD | KEY_X); + ED_SHORTCUT("scene_tree/paste_node", TTR("Paste Node"), KEY_MASK_CMD | KEY_V); 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); @@ -2054,5 +2154,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel clear_inherit_confirm->get_ok()->set_text(TTR("Clear!")); add_child(clear_inherit_confirm); + cpc_map = memnew(Vector()); + set_process_input(true); } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 41d5bda180ee..d0150b3eb535 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -76,13 +76,27 @@ class SceneTreeDock : public VBoxContainer { TOOL_SCENE_OPEN, TOOL_SCENE_CLEAR_INHERITANCE, TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM, - TOOL_SCENE_OPEN_INHERITED + TOOL_SCENE_OPEN_INHERITED, + TOOL_CUT_NODE, + TOOL_PASTE_NODE, }; enum { EDIT_SUBRESOURCE_BASE = 100 }; + enum CPC_ACTION { + NODE_CUT, + // TODO: NODE_COPY support? + }; + + struct cutpastecopy_map { + Node *node; + CPC_ACTION action; + }; + + Vector *cpc_map; + Vector subresources; bool restore_script_editor_on_drag;