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;