diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 1aae82f66f6a..3f7fd29e27eb 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -882,10 +882,39 @@ void CanvasItemEditor::_selection_menu_hide() { } void CanvasItemEditor::_add_node_pressed(int p_result) { - if (p_result == AddNodeOption::ADD_NODE) { - SceneTreeDock::get_singleton()->open_add_child_dialog(); - } else if (p_result == AddNodeOption::ADD_INSTANCE) { - SceneTreeDock::get_singleton()->open_instance_child_dialog(); + List nodes_to_move; + + switch (p_result) { + case ADD_NODE: { + SceneTreeDock::get_singleton()->open_add_child_dialog(); + } break; + case ADD_INSTANCE: { + SceneTreeDock::get_singleton()->open_instance_child_dialog(); + } break; + case ADD_PASTE: { + nodes_to_move = SceneTreeDock::get_singleton()->paste_nodes(); + [[fallthrough]]; + } + case ADD_MOVE: { + if (p_result == ADD_MOVE) { + nodes_to_move = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list(); + } + if (nodes_to_move.is_empty()) { + return; + } + + undo_redo->create_action(TTR("Move Node(s) to Position")); + for (Node *node : nodes_to_move) { + CanvasItem *ci = Object::cast_to(node); + if (ci) { + Transform2D xform = ci->get_global_transform_with_canvas().affine_inverse() * ci->get_transform(); + undo_redo->add_do_method(ci, "_edit_set_position", xform.xform(node_create_position)); + undo_redo->add_undo_method(ci, "_edit_set_position", ci->_edit_get_position()); + } + } + undo_redo->commit_action(); + _reset_create_position(); + } break; } } @@ -2194,10 +2223,26 @@ bool CanvasItemEditor::_gui_input_select(const Ref &p_event) { } if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) { + add_node_menu->clear(); + add_node_menu->add_icon_item(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here"), ADD_NODE); + add_node_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instantiate Scene Here"), ADD_INSTANCE); + for (Node *node : SceneTreeDock::get_singleton()->get_node_clipboard()) { + if (Object::cast_to(node)) { + add_node_menu->add_icon_item(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), TTR("Paste Node(s) Here"), ADD_PASTE); + break; + } + } + for (Node *node : EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()) { + if (Object::cast_to(node)) { + add_node_menu->add_icon_item(get_theme_icon(SNAME("ToolMove"), SNAME("EditorIcons")), TTR("Move Node(s) Here"), ADD_MOVE); + break; + } + } + add_node_menu->reset_size(); - add_node_menu->set_position(get_screen_transform().xform(get_local_mouse_position())); + add_node_menu->set_position(get_screen_transform().xform(b->get_position())); add_node_menu->popup(); - node_create_position = transform.affine_inverse().xform((get_local_mouse_position())); + node_create_position = transform.affine_inverse().xform(b->get_position()); return true; } @@ -5548,8 +5593,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { add_node_menu = memnew(PopupMenu); add_child(add_node_menu); - add_node_menu->add_icon_item(SceneTreeDock::get_singleton()->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here")); - add_node_menu->add_icon_item(SceneTreeDock::get_singleton()->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instance Scene Here")); add_node_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_add_node_pressed)); multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), Key::KP_MULTIPLY); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 1e8fc0670d37..9fa44bfb25de 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -84,6 +84,8 @@ class CanvasItemEditor : public VBoxContainer { enum AddNodeOption { ADD_NODE, ADD_INSTANCE, + ADD_PASTE, + ADD_MOVE, }; private: diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 125fcc02dc66..cece787bf348 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3060,6 +3060,10 @@ List SceneTreeDock::paste_nodes() { return pasted_nodes; } +List 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); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index d73038ef36b8..3639e6623376 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -315,6 +315,7 @@ class SceneTreeDock : public VBoxContainer { void open_instance_child_dialog(); List paste_nodes(); + List get_node_clipboard() const; ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; }