diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml index 7eeee3d7fd7..71c4ca0f9b9 100644 --- a/doc/classes/EditorContextMenuPlugin.xml +++ b/doc/classes/EditorContextMenuPlugin.xml @@ -14,7 +14,7 @@ - Called when creating a context menu, custom options can be added by using the [method add_context_menu_item] function. + Called when creating a context menu, custom options can be added by using the [method add_context_menu_item] or [method add_context_menu_item_from_shortcut] functions. [param paths] contains currently selected paths (depending on menu), which can be used to conditionally add options. @@ -22,14 +22,29 @@ - - Add custom options to the context menu of the currently specified slot. - To trigger a [param shortcut] before the context menu is created, please additionally call the [method add_menu_shortcut] function. + Add custom option to the context menu of the plugin's specified slot. When the option is activated, [param callback] will be called. Callback should take single [Array] argument; array contents depend on context menu slot. [codeblock] func _popup_menu(paths): add_context_menu_item("File Custom options", handle, ICON) [/codeblock] + If you want to assign shortcut to the menu item, use [method add_context_menu_item_from_shortcut] instead. + + + + + + + + + Add custom option to the context menu of the plugin's specified slot. The option will have the [param shortcut] assigned and reuse its callback. The shortcut has to be registered beforehand with [method add_menu_shortcut]. + [codeblock] + func _init(): + add_menu_shortcut(SHORTCUT, handle) + + func _popup_menu(paths): + add_context_menu_item_from_shortcut("File Custom options", SHORTCUT, ICON) + [/codeblock] @@ -37,13 +52,26 @@ - To register the shortcut for the context menu, call this function within the [method Object._init] function, even if the context menu has not been created yet. - Note that this method should only be invoked from [method Object._init]; otherwise, the shortcut will not be registered correctly. + Registers a shortcut associated with the plugin's context menu. This method should be called once (e.g. in plugin's [method Object._init]). [param callback] will be called when user presses the specified [param shortcut] while the menu's context is in effect (e.g. FileSystem dock is focused). Callback should take single [Array] argument; array contents depend on context menu slot. [codeblock] func _init(): - add_menu_shortcut(SHORTCUT, handle); + add_menu_shortcut(SHORTCUT, handle) [/codeblock] + + + Context menu of Scene dock. [method _popup_menu] will be called with a list of paths to currently selected nodes, while option callback will receive the list of currently selected nodes. + + + Context menu of FileSystem dock. [method _popup_menu] and option callback will be called with list of paths of the currently selected files. + + + 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. + + diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index b3191e53786..de49764f0d8 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -403,11 +403,11 @@ - + - Adds a plugin to the context menu. [param slot] is the position in the context menu where the plugin will be added. - Context menus are supported for three commonly used areas: the file system, scene tree, and editor script list panel. + Adds a plugin to the context menu. [param slot] is the context menu where the plugin will be added. + See [enum EditorContextMenuPlugin.ContextMenuSlot] for available context menus. A plugin instance can belong only to a single context menu slot. @@ -635,10 +635,9 @@ - - + - Removes a context menu plugin from the specified slot. + Removes the specified context menu plugin. @@ -891,17 +890,5 @@ Pass the [InputEvent] to other editor plugins except the main [Node3D] one. This can be used to prevent node selection changes and work with sub-gizmos instead. - - Context menu slot for the SceneTree. - - - Context menu slot for the FileSystem. - - - Context menu slot for the ScriptEditor file list. - - - Context menu slot for the FileSystem create submenu. - diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index e5caa6a3522..ee16c61c897 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -522,138 +522,6 @@ EditorPlugin *EditorData::get_extension_editor_plugin(const StringName &p_class_ return plugin == nullptr ? nullptr : *plugin; } -void EditorData::add_context_menu_plugin(ContextMenuSlot p_slot, const Ref &p_plugin) { - ContextMenu cm; - cm.p_slot = p_slot; - cm.plugin = p_plugin; - p_plugin->start_idx = context_menu_plugins.size() * EditorContextMenuPlugin::MAX_ITEMS; - context_menu_plugins.push_back(cm); -} - -void EditorData::remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref &p_plugin) { - for (int i = context_menu_plugins.size() - 1; i > -1; i--) { - if (context_menu_plugins[i].p_slot == p_slot && context_menu_plugins[i].plugin == p_plugin) { - context_menu_plugins.remove_at(i); - } - } -} - -int EditorData::match_context_menu_shortcut(ContextMenuSlot p_slot, const Ref &p_event) { - for (ContextMenu &cm : context_menu_plugins) { - if (cm.p_slot != p_slot) { - continue; - } - HashMap, Callable> &cms = cm.plugin->context_menu_shortcuts; - int shortcut_idx = 0; - for (KeyValue, Callable> &E : cms) { - const Ref &p_shortcut = E.key; - if (p_shortcut->matches_event(p_event)) { - return EditorData::CONTEXT_MENU_ITEM_ID_BASE + cm.plugin->start_idx + shortcut_idx; - } - shortcut_idx++; - } - } - return 0; -} - -void EditorData::add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector &p_paths) { - bool add_separator = false; - - for (ContextMenu &cm : context_menu_plugins) { - if (cm.p_slot != p_slot) { - continue; - } - cm.plugin->clear_context_menu_items(); - cm.plugin->add_options(p_paths); - HashMap &items = cm.plugin->context_menu_items; - if (items.size() > 0 && !add_separator) { - add_separator = true; - p_popup->add_separator(); - } - for (KeyValue &E : items) { - EditorContextMenuPlugin::ContextMenuItem &item = E.value; - - if (item.icon.is_valid()) { - p_popup->add_icon_item(item.icon, item.item_name, item.idx); - const int icon_size = p_popup->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); - p_popup->set_item_icon_max_width(-1, icon_size); - } else { - p_popup->add_item(item.item_name, item.idx); - } - if (item.shortcut.is_valid()) { - p_popup->set_item_shortcut(-1, item.shortcut, true); - } - } - } -} - -template -void EditorData::invoke_plugin_callback(ContextMenuSlot p_slot, int p_option, const T &p_arg) { - Variant arg = p_arg; - Variant *argptr = &arg; - - for (int i = 0; i < context_menu_plugins.size(); i++) { - if (context_menu_plugins[i].p_slot != p_slot || context_menu_plugins[i].plugin.is_null()) { - continue; - } - Ref plugin = context_menu_plugins[i].plugin; - - // Shortcut callback. - int shortcut_idx = 0; - int shortcut_base_idx = EditorData::CONTEXT_MENU_ITEM_ID_BASE + plugin->start_idx; - for (KeyValue, Callable> &E : plugin->context_menu_shortcuts) { - if (shortcut_base_idx + shortcut_idx == p_option) { - const Callable &callable = E.value; - Callable::CallError ce; - Variant result; - callable.callp((const Variant **)&argptr, 1, result, ce); - } - shortcut_idx++; - } - if (p_option < shortcut_base_idx + shortcut_idx) { - return; - } - - HashMap &items = plugin->context_menu_items; - for (KeyValue &E : items) { - EditorContextMenuPlugin::ContextMenuItem &item = E.value; - - if (p_option != item.idx || !item.callable.is_valid()) { - continue; - } - - Callable::CallError ce; - Variant result; - item.callable.callp((const Variant **)&argptr, 1, result, ce); - - if (ce.error != Callable::CallError::CALL_OK) { - String err = Variant::get_callable_error_text(item.callable, nullptr, 0, ce); - ERR_PRINT("Error calling function from context menu: " + err); - } - } - } - // Invoke submenu items. - if (p_slot == CONTEXT_SLOT_FILESYSTEM) { - invoke_plugin_callback(CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE, p_option, p_arg); - } -} - -void EditorData::filesystem_options_pressed(ContextMenuSlot p_slot, int p_option, const Vector &p_selected) { - invoke_plugin_callback(p_slot, p_option, p_selected); -} - -void EditorData::scene_tree_options_pressed(ContextMenuSlot p_slot, int p_option, const List &p_selected) { - TypedArray nodes; - for (Node *selected : p_selected) { - nodes.append(selected); - } - invoke_plugin_callback(p_slot, p_option, nodes); -} - -void EditorData::script_editor_options_pressed(ContextMenuSlot p_slot, int p_option, const Ref &p_script) { - invoke_plugin_callback(p_slot, p_option, p_script); -} - void EditorData::add_custom_type(const String &p_type, const String &p_inherits, const Ref