From ff7cae4c4cbe55cfe18455509d6d1203afa21399 Mon Sep 17 00:00:00 2001 From: kobewi Date: Wed, 7 Jul 2021 13:19:59 +0200 Subject: [PATCH] Allow to create a node at specific position --- editor/plugins/canvas_item_editor_plugin.cpp | 46 ++++++++++++++++++++ editor/plugins/canvas_item_editor_plugin.h | 10 +++++ editor/scene_tree_dock.cpp | 29 +++++++++++- editor/scene_tree_dock.h | 4 ++ scene/gui/control.cpp | 2 +- 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index d4da3b664c4..9450e2a5502 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1012,6 +1012,32 @@ void CanvasItemEditor::_selection_menu_hide() { selection_menu->set_size(Vector2(0, 0)); } +void CanvasItemEditor::_add_node_pressed(int p_result) { + if (p_result == AddNodeOption::ADD_NODE) { + editor->get_scene_tree_dock()->open_add_child_dialog(); + } else if (p_result == AddNodeOption::ADD_INSTANCE) { + editor->get_scene_tree_dock()->open_instance_child_dialog(); + } +} + +void CanvasItemEditor::_node_created(Node *p_node) { + if (node_create_position == Point2()) { + return; + } + + CanvasItem *c = Object::cast_to(p_node); + if (c) { + Transform2D xform = c->get_global_transform_with_canvas().affine_inverse() * c->get_transform(); + c->_edit_set_position(xform.xform(node_create_position)); + } + + call_deferred("_reset_create_position"); // Defer the call in case more than one node is added. +} + +void CanvasItemEditor::_reset_create_position() { + node_create_position = Point2(); +} + bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref &p_event) { Ref b = p_event; Ref m = p_event; @@ -2397,6 +2423,14 @@ bool CanvasItemEditor::_gui_input_select(const Ref &p_event) { } } + if (b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && b->get_control()) { + add_node_menu->set_position(get_global_transform().xform(get_local_mouse_position())); + add_node_menu->set_size(Vector2(1, 1)); + add_node_menu->popup(); + node_create_position = transform.affine_inverse().xform((get_local_mouse_position())); + return true; + } + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) { // Single item selection Point2 click = transform.affine_inverse().xform(b->get_position()); @@ -5257,6 +5291,9 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed); ClassDB::bind_method("_selection_changed", &CanvasItemEditor::_selection_changed); ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop); + ClassDB::bind_method("_add_node_pressed", &CanvasItemEditor::_add_node_pressed); + ClassDB::bind_method("_node_created", &CanvasItemEditor::_node_created); + ClassDB::bind_method("_reset_create_position", &CanvasItemEditor::_reset_create_position); ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); ClassDB::bind_method(D_METHOD("get_state"), &CanvasItemEditor::get_state); @@ -5574,6 +5611,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { editor_selection->connect("selection_changed", this, "update"); editor_selection->connect("selection_changed", this, "_selection_changed"); + editor->get_scene_tree_dock()->connect("node_created", this, "_node_created"); + editor->get_scene_tree_dock()->connect("add_node_used", this, "_reset_create_position"); + editor->call_deferred("connect", "play_pressed", this, "_update_override_camera_button", make_binds(true)); editor->call_deferred("connect", "stop_pressed", this, "_update_override_camera_button", make_binds(false)); @@ -5950,6 +5990,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { selection_menu->connect("id_pressed", this, "_selection_result_pressed"); selection_menu->connect("popup_hide", this, "_selection_menu_hide"); + add_node_menu = memnew(PopupMenu); + add_child(add_node_menu); + add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_icon("Add", "EditorIcons"), TTR("Add Node Here")); + add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_icon("Instance", "EditorIcons"), TTR("Instance Scene Here")); + add_node_menu->connect("id_pressed", this, "_add_node_pressed"); + multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY); divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE); pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), KEY_SPACE); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index fa74a2a042c..72462681fb2 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -82,6 +82,11 @@ public: TOOL_MAX }; + enum AddNodeOption { + ADD_NODE, + ADD_INSTANCE, + }; + private: EditorNode *editor; @@ -283,6 +288,7 @@ private: bool ruler_tool_active; Point2 ruler_tool_origin; + Point2 node_create_position; MenuOption last_option; @@ -379,6 +385,7 @@ private: Button *key_auto_insert_button; PopupMenu *selection_menu; + PopupMenu *add_node_menu; Control *top_ruler; Control *left_ruler; @@ -439,6 +446,9 @@ private: void _snap_changed(); void _selection_result_pressed(int); void _selection_menu_hide(); + void _add_node_pressed(int p_result); + void _node_created(Node *p_node); + void _reset_create_position(); UndoRedo *undo_redo; bool _build_bones_list(Node *p_node); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 68e2fe39568..eae3dc84e33 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -225,6 +225,9 @@ void SceneTreeDock::_perform_instance_scenes(const Vector &p_files, Node editor_data->get_undo_redo().commit_action(); editor->push_item(instances[instances.size() - 1]); + for (int i = 0; i < instances.size(); i++) { + emit_signal("node_created", instances[i]); + } } void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) { @@ -353,6 +356,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } + if (reset_create_dialog && !p_confirm_override) { + create_dialog->set_base_type("Node"); + reset_create_dialog = false; + } + // Prefer nodes that inherit from the current scene root. Node *current_edited_scene_root = EditorNode::get_singleton()->get_edited_scene(); if (current_edited_scene_root) { @@ -373,6 +381,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } create_dialog->popup_create(true); + if (!p_confirm_override) { + emit_signal("add_node_used"); + } } break; case TOOL_INSTANCE: { if (!profile_allow_editing) { @@ -387,7 +398,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { quick_open->popup_dialog("PackedScene", true); quick_open->set_title(TTR("Instance Child Scene")); - + if (!p_confirm_override) { + emit_signal("add_node_used"); + } } break; case TOOL_EXPAND_COLLAPSE: { if (!scene_tree->get_selected()) { @@ -2168,6 +2181,8 @@ void SceneTreeDock::_do_create(Node *p_parent) { } ct->set_size(ms); } + + emit_signal("node_created", c); } void SceneTreeDock::_create() { @@ -2860,6 +2875,16 @@ void SceneTreeDock::open_script_dialog(Node *p_for_node, bool p_extend) { } } +void SceneTreeDock::open_add_child_dialog() { + create_dialog->set_base_type("CanvasItem"); + _tool_selected(TOOL_NEW, true); + reset_create_dialog = true; +} + +void SceneTreeDock::open_instance_child_dialog() { + _tool_selected(TOOL_INSTANCE, true); +} + void SceneTreeDock::add_remote_tree_editor(Control *p_remote) { ERR_FAIL_COND(remote_tree != nullptr); add_child(p_remote); @@ -3099,6 +3124,8 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node); ADD_SIGNAL(MethodInfo("remote_tree_selected")); + ADD_SIGNAL(MethodInfo("add_node_used")); + ADD_SIGNAL(MethodInfo("node_created", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); } SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) { diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index ae36c52d613..d4566a5dceb 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -104,6 +104,7 @@ class SceneTreeDock : public VBoxContainer { Vector subresources; bool restore_script_editor_on_drag; + bool reset_create_dialog = false; int current_option; CreateDialog *create_dialog; @@ -282,6 +283,9 @@ public: void attach_script_to_selected(bool p_extend); void open_script_dialog(Node *p_for_node, bool p_extend); + void open_add_child_dialog(); + void open_instance_child_dialog(); + ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; } SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index d1df9050913..2eb710316fd 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -97,7 +97,7 @@ void Control::_edit_set_state(const Dictionary &p_state) { void Control::_edit_set_position(const Point2 &p_position) { #ifdef TOOLS_ENABLED ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); - set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled()); + set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to(data.parent)); #else // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED set_position(p_position);