From ba54a2805a2a92fb8efaac5095a8eb9ef9b25b97 Mon Sep 17 00:00:00 2001 From: kobewi Date: Wed, 18 Dec 2024 14:42:10 +0100 Subject: [PATCH] Add more menus support to EditorContextMenuPlugin --- doc/classes/EditorContextMenuPlugin.xml | 23 +++++++++++++-- editor/gui/editor_scene_tabs.cpp | 14 +++++++++ editor/gui/editor_scene_tabs.h | 3 ++ editor/plugins/canvas_item_editor_plugin.cpp | 29 +++++++++++++++++++ editor/plugins/editor_context_menu_plugin.cpp | 14 ++++++++- editor/plugins/editor_context_menu_plugin.h | 4 +++ editor/plugins/script_text_editor.cpp | 9 ++++++ 7 files changed, 93 insertions(+), 3 deletions(-) diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml index fb90a2a5cd6..2717a0bc9a2 100644 --- a/doc/classes/EditorContextMenuPlugin.xml +++ b/doc/classes/EditorContextMenuPlugin.xml @@ -85,11 +85,30 @@ Context menu of FileSystem dock. [method _popup_menu] and option callback will be called with list of paths of the currently selected files. + + Context menu of Script editor's script tabs. [method _popup_menu] will be called with the path to the currently edited script, while option callback will receive reference to that script. + The "Create..." submenu of FileSystem dock's context menu. [method _popup_menu] and option callback will be called with list of paths of the currently selected files. - - Context menu of Scene dock. [method _popup_menu] will be called with the path to the currently edited script, while option callback will receive reference to that script. + + Context menu of Script editor's code editor. [method _popup_menu] will be called with the path to the [CodeEdit] node. You can fetch it using this code: + [codeblock] + func _popup_menu(paths): + var code_edit = Engine.get_main_loop().root.get_node(paths[0]); + [/codeblock] + The option callback will receive reference to that node. You can use [CodeEdit] methods to perform symbol lookups etc. + + + Context menu of scene tabs. [method _popup_menu] will be called with the path of the clicked scene, or empty [PackedStringArray] if the menu was opened on empty space. The option callback will receive the path of the clicked scene, or empty [String] if none was clicked. + + + Context menu of 2D editor's basic right-click menu. [method _popup_menu] will be called with paths to all [CanvasItem] nodes under the cursor. You can fetch them using this code: + [codeblock] + func _popup_menu(paths): + var canvas_item = Engine.get_main_loop().root.get_node(paths[0]); # Replace 0 with the desired index. + [/codeblock] + The paths array is empty if there weren't any nodes under cursor. The option callback will receive a typed array of [CanvasItem] nodes. diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index 5b42afdbe8b..36de87ef24a 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -36,6 +36,7 @@ #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/inspector_dock.h" +#include "editor/plugins/editor_context_menu_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -195,7 +196,13 @@ void EditorSceneTabs::_update_context_menu() { scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), EditorNode::FILE_CLOSE_RIGHT); _disable_menu_option_if(EditorNode::FILE_CLOSE_RIGHT, EditorNode::get_editor_data().get_edited_scene_count() == tab_id + 1); scene_tabs_context_menu->add_item(TTR("Close All Tabs"), EditorNode::FILE_CLOSE_ALL); + + const PackedStringArray paths = { EditorNode::get_editor_data().get_scene_path(tab_id) }; + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(scene_tabs_context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TABS, paths); + } else { + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(scene_tabs_context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TABS, {}); } + last_hovered_tab = tab_id; } void EditorSceneTabs::_disable_menu_option_if(int p_option, bool p_condition) { @@ -204,6 +211,12 @@ void EditorSceneTabs::_disable_menu_option_if(int p_option, bool p_condition) { } } +void EditorSceneTabs::_custom_menu_option(int p_option) { + if (p_option >= EditorContextMenuPlugin::BASE_ID) { + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TABS, p_option, last_hovered_tab >= 0 ? EditorNode::get_editor_data().get_scene_path(last_hovered_tab) : String()); + } +} + void EditorSceneTabs::update_scene_tabs() { static bool menu_initialized = false; tab_preview_panel->hide(); @@ -410,6 +423,7 @@ EditorSceneTabs::EditorSceneTabs() { scene_tabs_context_menu = memnew(PopupMenu); tabbar_container->add_child(scene_tabs_context_menu); scene_tabs_context_menu->connect(SceneStringName(id_pressed), callable_mp(EditorNode::get_singleton(), &EditorNode::trigger_menu_option).bind(false)); + scene_tabs_context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorSceneTabs::_custom_menu_option)); scene_tab_add = memnew(Button); scene_tab_add->set_flat(true); diff --git a/editor/gui/editor_scene_tabs.h b/editor/gui/editor_scene_tabs.h index ac9e6b8c432..f9b94f34102 100644 --- a/editor/gui/editor_scene_tabs.h +++ b/editor/gui/editor_scene_tabs.h @@ -57,6 +57,8 @@ class EditorSceneTabs : public MarginContainer { Panel *tab_preview_panel = nullptr; TextureRect *tab_preview = nullptr; + int last_hovered_tab = -1; + void _scene_tab_changed(int p_tab); void _scene_tab_script_edited(int p_tab); void _scene_tab_closed(int p_tab); @@ -69,6 +71,7 @@ class EditorSceneTabs : public MarginContainer { void _reposition_active_tab(int p_to_index); void _update_context_menu(); void _disable_menu_option_if(int p_option, bool p_condition); + void _custom_menu_option(int p_option); void _tab_preview_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 59775fe266d..17b55ba0fd6 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -43,6 +43,7 @@ #include "editor/gui/editor_toaster.h" #include "editor/gui/editor_zoom_widget.h" #include "editor/plugins/animation_player_editor_plugin.h" +#include "editor/plugins/editor_context_menu_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/scene_tree_dock.h" #include "editor/themes/editor_scale.h" @@ -991,6 +992,19 @@ void CanvasItemEditor::_add_node_pressed(int p_result) { undo_redo->commit_action(); _reset_create_position(); } break; + default: { + if (p_result >= EditorContextMenuPlugin::BASE_ID) { + TypedArray nodes; + nodes.resize(selection_results.size()); + + int i = 0; + for (const _SelectResult &result : selection_results) { + nodes[i] = result.item; + i++; + } + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_2D_EDITOR, p_result, nodes); + } + } } } @@ -2461,6 +2475,21 @@ bool CanvasItemEditor::_gui_input_select(const Ref &p_event) { } } + // Context menu plugin receives paths of nodes under cursor. It's a complex operation, so perform it only when necessary. + if (EditorContextMenuPluginManager::get_singleton()->has_plugins_for_slot(EditorContextMenuPlugin::CONTEXT_SLOT_2D_EDITOR)) { + selection_results.clear(); + _get_canvas_items_at_pos(transform.affine_inverse().xform(viewport->get_local_mouse_position()), selection_results, true); + + PackedStringArray paths; + paths.resize(selection_results.size()); + String *paths_write = paths.ptrw(); + + for (int i = 0; i < paths.size(); i++) { + paths_write[i] = selection_results[i].item->get_path(); + } + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(add_node_menu, EditorContextMenuPlugin::CONTEXT_SLOT_2D_EDITOR, paths); + } + add_node_menu->reset_size(); add_node_menu->set_position(viewport->get_screen_transform().xform(b->get_position())); add_node_menu->popup(); diff --git a/editor/plugins/editor_context_menu_plugin.cpp b/editor/plugins/editor_context_menu_plugin.cpp index b635816bd97..d35e4721ab3 100644 --- a/editor/plugins/editor_context_menu_plugin.cpp +++ b/editor/plugins/editor_context_menu_plugin.cpp @@ -87,8 +87,11 @@ void EditorContextMenuPlugin::_bind_methods() { BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TREE); BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM); - BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM_CREATE); BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM_CREATE); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR_CODE); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TABS); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_2D_EDITOR); } void EditorContextMenuPluginManager::add_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref &p_plugin) { @@ -106,6 +109,15 @@ void EditorContextMenuPluginManager::remove_plugin(const Ref &plugin : plugin_list) { + if (plugin->slot == p_slot) { + return true; + } + } + return false; +} + void EditorContextMenuPluginManager::add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector &p_paths) { bool separator_added = false; const int icon_size = p_popup->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); diff --git a/editor/plugins/editor_context_menu_plugin.h b/editor/plugins/editor_context_menu_plugin.h index 86c67dedda1..8f323239db8 100644 --- a/editor/plugins/editor_context_menu_plugin.h +++ b/editor/plugins/editor_context_menu_plugin.h @@ -52,6 +52,9 @@ public: CONTEXT_SLOT_FILESYSTEM, CONTEXT_SLOT_SCRIPT_EDITOR, CONTEXT_SLOT_FILESYSTEM_CREATE, + CONTEXT_SLOT_SCRIPT_EDITOR_CODE, + CONTEXT_SLOT_SCENE_TABS, + CONTEXT_SLOT_2D_EDITOR, }; inline static constexpr int BASE_ID = 2000; @@ -100,6 +103,7 @@ public: void add_plugin(ContextMenuSlot p_slot, const Ref &p_plugin); void remove_plugin(const Ref &p_plugin); + bool has_plugins_for_slot(ContextMenuSlot p_slot); void add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector &p_paths); Callable match_custom_shortcut(ContextMenuSlot p_slot, const Ref &p_event); bool activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 94e3c8ba7b0..31a815f3332 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -41,6 +41,7 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/gui/editor_toaster.h" +#include "editor/plugins/editor_context_menu_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/gui/menu_button.h" #include "scene/gui/rich_text_label.h" @@ -1721,6 +1722,11 @@ void ScriptTextEditor::_edit_option(int p_op) { _lookup_symbol(text, tx->get_caret_line(0), tx->get_caret_column(0)); } } break; + default: { + if (p_op >= EditorContextMenuPlugin::BASE_ID) { + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, p_op, tx); + } + } } } @@ -2310,6 +2316,9 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p } } + const PackedStringArray paths = { code_editor->get_text_editor()->get_path() }; + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, paths); + const CodeEdit *tx = code_editor->get_text_editor(); context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !tx->has_undo()); context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !tx->has_redo());