Add EditorContextMenuPluginManager and refactor menu plugins
This commit is contained in:
parent
e2dd56bea7
commit
ecc0ab8061
|
@ -14,7 +14,7 @@
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="paths" type="PackedStringArray" />
|
<param index="0" name="paths" type="PackedStringArray" />
|
||||||
<description>
|
<description>
|
||||||
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.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="add_context_menu_item">
|
<method name="add_context_menu_item">
|
||||||
|
@ -22,14 +22,29 @@
|
||||||
<param index="0" name="name" type="String" />
|
<param index="0" name="name" type="String" />
|
||||||
<param index="1" name="callback" type="Callable" />
|
<param index="1" name="callback" type="Callable" />
|
||||||
<param index="2" name="icon" type="Texture2D" default="null" />
|
<param index="2" name="icon" type="Texture2D" default="null" />
|
||||||
<param index="3" name="shortcut" type="Shortcut" default="null" />
|
|
||||||
<description>
|
<description>
|
||||||
Add custom options to the context menu of the currently specified slot.
|
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.
|
||||||
To trigger a [param shortcut] before the context menu is created, please additionally call the [method add_menu_shortcut] function.
|
|
||||||
[codeblock]
|
[codeblock]
|
||||||
func _popup_menu(paths):
|
func _popup_menu(paths):
|
||||||
add_context_menu_item("File Custom options", handle, ICON)
|
add_context_menu_item("File Custom options", handle, ICON)
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
|
If you want to assign shortcut to the menu item, use [method add_context_menu_item_from_shortcut] instead.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="add_context_menu_item_from_shortcut">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="name" type="String" />
|
||||||
|
<param index="1" name="shortcut" type="Shortcut" />
|
||||||
|
<param index="2" name="icon" type="Texture2D" default="null" />
|
||||||
|
<description>
|
||||||
|
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]
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="add_menu_shortcut">
|
<method name="add_menu_shortcut">
|
||||||
|
@ -37,13 +52,26 @@
|
||||||
<param index="0" name="shortcut" type="Shortcut" />
|
<param index="0" name="shortcut" type="Shortcut" />
|
||||||
<param index="1" name="callback" type="Callable" />
|
<param index="1" name="callback" type="Callable" />
|
||||||
<description>
|
<description>
|
||||||
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.
|
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.
|
||||||
Note that this method should only be invoked from [method Object._init]; otherwise, the shortcut will not be registered correctly.
|
|
||||||
[codeblock]
|
[codeblock]
|
||||||
func _init():
|
func _init():
|
||||||
add_menu_shortcut(SHORTCUT, handle);
|
add_menu_shortcut(SHORTCUT, handle)
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
|
<constants>
|
||||||
|
<constant name="CONTEXT_SLOT_SCENE_TREE" value="0" enum="ContextMenuSlot">
|
||||||
|
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.
|
||||||
|
</constant>
|
||||||
|
<constant name="CONTEXT_SLOT_FILESYSTEM" value="1" enum="ContextMenuSlot">
|
||||||
|
Context menu of FileSystem dock. [method _popup_menu] and option callback will be called with list of paths of the currently selected files.
|
||||||
|
</constant>
|
||||||
|
<constant name="CONTEXT_SLOT_FILESYSTEM_CREATE" value="3" enum="ContextMenuSlot">
|
||||||
|
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.
|
||||||
|
</constant>
|
||||||
|
<constant name="CONTEXT_SLOT_SCRIPT_EDITOR" value="2" enum="ContextMenuSlot">
|
||||||
|
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.
|
||||||
|
</constant>
|
||||||
|
</constants>
|
||||||
</class>
|
</class>
|
||||||
|
|
|
@ -403,11 +403,11 @@
|
||||||
</method>
|
</method>
|
||||||
<method name="add_context_menu_plugin">
|
<method name="add_context_menu_plugin">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="slot" type="int" enum="EditorPlugin.ContextMenuSlot" />
|
<param index="0" name="slot" type="int" enum="EditorContextMenuPlugin.ContextMenuSlot" />
|
||||||
<param index="1" name="plugin" type="EditorContextMenuPlugin" />
|
<param index="1" name="plugin" type="EditorContextMenuPlugin" />
|
||||||
<description>
|
<description>
|
||||||
Adds a plugin to the context menu. [param slot] is the position in the context menu where the plugin will be added.
|
Adds a plugin to the context menu. [param slot] is 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.
|
See [enum EditorContextMenuPlugin.ContextMenuSlot] for available context menus. A plugin instance can belong only to a single context menu slot.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="add_control_to_bottom_panel">
|
<method name="add_control_to_bottom_panel">
|
||||||
|
@ -635,10 +635,9 @@
|
||||||
</method>
|
</method>
|
||||||
<method name="remove_context_menu_plugin">
|
<method name="remove_context_menu_plugin">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="slot" type="int" enum="EditorPlugin.ContextMenuSlot" />
|
<param index="0" name="plugin" type="EditorContextMenuPlugin" />
|
||||||
<param index="1" name="plugin" type="EditorContextMenuPlugin" />
|
|
||||||
<description>
|
<description>
|
||||||
Removes a context menu plugin from the specified slot.
|
Removes the specified context menu plugin.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="remove_control_from_bottom_panel">
|
<method name="remove_control_from_bottom_panel">
|
||||||
|
@ -891,17 +890,5 @@
|
||||||
<constant name="AFTER_GUI_INPUT_CUSTOM" value="2" enum="AfterGUIInput">
|
<constant name="AFTER_GUI_INPUT_CUSTOM" value="2" enum="AfterGUIInput">
|
||||||
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.
|
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.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="CONTEXT_SLOT_SCENE_TREE" value="0" enum="ContextMenuSlot">
|
|
||||||
Context menu slot for the SceneTree.
|
|
||||||
</constant>
|
|
||||||
<constant name="CONTEXT_SLOT_FILESYSTEM" value="1" enum="ContextMenuSlot">
|
|
||||||
Context menu slot for the FileSystem.
|
|
||||||
</constant>
|
|
||||||
<constant name="CONTEXT_SLOT_SCRIPT_EDITOR" value="2" enum="ContextMenuSlot">
|
|
||||||
Context menu slot for the ScriptEditor file list.
|
|
||||||
</constant>
|
|
||||||
<constant name="CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE" value="3" enum="ContextMenuSlot">
|
|
||||||
Context menu slot for the FileSystem create submenu.
|
|
||||||
</constant>
|
|
||||||
</constants>
|
</constants>
|
||||||
</class>
|
</class>
|
||||||
|
|
|
@ -522,138 +522,6 @@ EditorPlugin *EditorData::get_extension_editor_plugin(const StringName &p_class_
|
||||||
return plugin == nullptr ? nullptr : *plugin;
|
return plugin == nullptr ? nullptr : *plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorData::add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &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<EditorContextMenuPlugin> &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<InputEvent> &p_event) {
|
|
||||||
for (ContextMenu &cm : context_menu_plugins) {
|
|
||||||
if (cm.p_slot != p_slot) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
HashMap<Ref<Shortcut>, Callable> &cms = cm.plugin->context_menu_shortcuts;
|
|
||||||
int shortcut_idx = 0;
|
|
||||||
for (KeyValue<Ref<Shortcut>, Callable> &E : cms) {
|
|
||||||
const Ref<Shortcut> &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<String> &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<String, EditorContextMenuPlugin::ContextMenuItem> &items = cm.plugin->context_menu_items;
|
|
||||||
if (items.size() > 0 && !add_separator) {
|
|
||||||
add_separator = true;
|
|
||||||
p_popup->add_separator();
|
|
||||||
}
|
|
||||||
for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &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 <typename T>
|
|
||||||
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<EditorContextMenuPlugin> 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<Ref<Shortcut>, 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<String, EditorContextMenuPlugin::ContextMenuItem> &items = plugin->context_menu_items;
|
|
||||||
for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &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<String> &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<Node *> &p_selected) {
|
|
||||||
TypedArray<Node> 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<Resource> &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<Script> &p_script, const Ref<Texture2D> &p_icon) {
|
void EditorData::add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon) {
|
||||||
ERR_FAIL_COND_MSG(p_script.is_null(), "It's not a reference to a valid Script object.");
|
ERR_FAIL_COND_MSG(p_script.is_null(), "It's not a reference to a valid Script object.");
|
||||||
CustomType ct;
|
CustomType ct;
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
class ConfigFile;
|
class ConfigFile;
|
||||||
class EditorPlugin;
|
class EditorPlugin;
|
||||||
class EditorUndoRedoManager;
|
class EditorUndoRedoManager;
|
||||||
class EditorContextMenuPlugin;
|
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,22 +124,6 @@ public:
|
||||||
uint64_t last_checked_version = 0;
|
uint64_t last_checked_version = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ContextMenuSlot {
|
|
||||||
CONTEXT_SLOT_SCENE_TREE,
|
|
||||||
CONTEXT_SLOT_FILESYSTEM,
|
|
||||||
CONTEXT_SLOT_SCRIPT_EDITOR,
|
|
||||||
CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE,
|
|
||||||
};
|
|
||||||
|
|
||||||
inline static constexpr int CONTEXT_MENU_ITEM_ID_BASE = 1000;
|
|
||||||
|
|
||||||
struct ContextMenu {
|
|
||||||
int p_slot;
|
|
||||||
Ref<EditorContextMenuPlugin> plugin;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<ContextMenu> context_menu_plugins;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<EditorPlugin *> editor_plugins;
|
Vector<EditorPlugin *> editor_plugins;
|
||||||
HashMap<StringName, EditorPlugin *> extension_editor_plugins;
|
HashMap<StringName, EditorPlugin *> extension_editor_plugins;
|
||||||
|
@ -195,18 +178,6 @@ public:
|
||||||
bool has_extension_editor_plugin(const StringName &p_class_name);
|
bool has_extension_editor_plugin(const StringName &p_class_name);
|
||||||
EditorPlugin *get_extension_editor_plugin(const StringName &p_class_name);
|
EditorPlugin *get_extension_editor_plugin(const StringName &p_class_name);
|
||||||
|
|
||||||
// Context menu plugin.
|
|
||||||
void add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
|
||||||
void remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
|
||||||
int match_context_menu_shortcut(ContextMenuSlot p_slot, const Ref<InputEvent> &p_event);
|
|
||||||
|
|
||||||
void add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths);
|
|
||||||
void filesystem_options_pressed(ContextMenuSlot p_slot, int p_option, const Vector<String> &p_selected);
|
|
||||||
void scene_tree_options_pressed(ContextMenuSlot p_slot, int p_option, const List<Node *> &p_selected);
|
|
||||||
void script_editor_options_pressed(ContextMenuSlot p_slot, int p_option, const Ref<Resource> &p_script);
|
|
||||||
template <typename T>
|
|
||||||
void invoke_plugin_callback(ContextMenuSlot p_slot, int p_option, const T &p_arg);
|
|
||||||
|
|
||||||
void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value)
|
void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value)
|
||||||
void remove_undo_redo_inspector_hook_callback(Callable p_callable);
|
void remove_undo_redo_inspector_hook_callback(Callable p_callable);
|
||||||
const Vector<Callable> get_undo_redo_inspector_hook_callback();
|
const Vector<Callable> get_undo_redo_inspector_hook_callback();
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "core/string/translation_server.h"
|
#include "core/string/translation_server.h"
|
||||||
#include "core/version.h"
|
#include "core/version.h"
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
|
#include "editor/plugins/editor_context_menu_plugin.h"
|
||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
#include "scene/3d/bone_attachment_3d.h"
|
#include "scene/3d/bone_attachment_3d.h"
|
||||||
#include "scene/animation/animation_tree.h"
|
#include "scene/animation/animation_tree.h"
|
||||||
|
@ -6960,6 +6961,8 @@ EditorNode::EditorNode() {
|
||||||
EditorFileSystem *efs = memnew(EditorFileSystem);
|
EditorFileSystem *efs = memnew(EditorFileSystem);
|
||||||
add_child(efs);
|
add_child(efs);
|
||||||
|
|
||||||
|
EditorContextMenuPluginManager::create();
|
||||||
|
|
||||||
// Used for previews.
|
// Used for previews.
|
||||||
FileDialog::get_icon_func = _file_dialog_get_icon;
|
FileDialog::get_icon_func = _file_dialog_get_icon;
|
||||||
FileDialog::register_func = _file_dialog_register;
|
FileDialog::register_func = _file_dialog_register;
|
||||||
|
@ -7959,6 +7962,7 @@ EditorNode::~EditorNode() {
|
||||||
EditorInspector::cleanup_plugins();
|
EditorInspector::cleanup_plugins();
|
||||||
EditorTranslationParser::get_singleton()->clean_parsers();
|
EditorTranslationParser::get_singleton()->clean_parsers();
|
||||||
ResourceImporterScene::clean_up_importer_plugins();
|
ResourceImporterScene::clean_up_importer_plugins();
|
||||||
|
EditorContextMenuPluginManager::cleanup();
|
||||||
|
|
||||||
remove_print_handler(&print_handler);
|
remove_print_handler(&print_handler);
|
||||||
EditorHelp::cleanup_doc();
|
EditorHelp::cleanup_doc();
|
||||||
|
|
|
@ -2557,9 +2557,12 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
|
||||||
String dir = ProjectSettings::get_singleton()->globalize_path(fpath);
|
String dir = ProjectSettings::get_singleton()->globalize_path(fpath);
|
||||||
ScriptEditor::get_singleton()->open_text_file_create_dialog(dir);
|
ScriptEditor::get_singleton()->open_text_file_create_dialog(dir);
|
||||||
} break;
|
} break;
|
||||||
default:
|
|
||||||
EditorNode::get_editor_data().filesystem_options_pressed(EditorData::CONTEXT_SLOT_FILESYSTEM, p_option, p_selected);
|
default: {
|
||||||
break;
|
if (!EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_option, p_selected)) {
|
||||||
|
EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_option, p_selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3183,7 +3186,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
|
||||||
new_menu->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("Resource..."), FILE_NEW_RESOURCE);
|
new_menu->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("Resource..."), FILE_NEW_RESOURCE);
|
||||||
new_menu->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("TextFile..."), FILE_NEW_TEXTFILE);
|
new_menu->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("TextFile..."), FILE_NEW_TEXTFILE);
|
||||||
|
|
||||||
EditorNode::get_editor_data().add_options_from_plugins(new_menu, EditorData::CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE, p_paths);
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(new_menu, EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_paths);
|
||||||
p_popup->add_separator();
|
p_popup->add_separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3323,8 +3326,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
|
||||||
|
|
||||||
current_path = fpath;
|
current_path = fpath;
|
||||||
}
|
}
|
||||||
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(p_popup, EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_paths);
|
||||||
EditorNode::get_editor_data().add_options_from_plugins(p_popup, EditorData::CONTEXT_SLOT_FILESYSTEM, p_paths);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) {
|
void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) {
|
||||||
|
@ -3554,11 +3556,16 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) {
|
||||||
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
|
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
|
||||||
focus_on_filter();
|
focus_on_filter();
|
||||||
} else {
|
} else {
|
||||||
int match_option = EditorNode::get_editor_data().match_context_menu_shortcut(EditorData::CONTEXT_SLOT_FILESYSTEM, p_event);
|
Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_event);
|
||||||
if (match_option) {
|
if (!custom_callback.is_valid()) {
|
||||||
_tree_rmb_option(match_option);
|
custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custom_callback.is_valid()) {
|
||||||
|
EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, _tree_get_selected(false));
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accept_event();
|
accept_event();
|
||||||
|
@ -3626,7 +3633,16 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) {
|
||||||
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
|
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
|
||||||
focus_on_filter();
|
focus_on_filter();
|
||||||
} else {
|
} else {
|
||||||
return;
|
Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_event);
|
||||||
|
if (!custom_callback.is_valid()) {
|
||||||
|
custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custom_callback.is_valid()) {
|
||||||
|
EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, files->get_selected_items());
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accept_event();
|
accept_event();
|
||||||
|
|
|
@ -32,9 +32,11 @@
|
||||||
|
|
||||||
#include "core/input/shortcut.h"
|
#include "core/input/shortcut.h"
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
|
#include "editor/editor_string_names.h"
|
||||||
|
#include "scene/gui/popup_menu.h"
|
||||||
#include "scene/resources/texture.h"
|
#include "scene/resources/texture.h"
|
||||||
|
|
||||||
void EditorContextMenuPlugin::add_options(const Vector<String> &p_paths) {
|
void EditorContextMenuPlugin::get_options(const Vector<String> &p_paths) {
|
||||||
GDVIRTUAL_CALL(_popup_menu, p_paths);
|
GDVIRTUAL_CALL(_popup_menu, p_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,24 +44,142 @@ void EditorContextMenuPlugin::add_menu_shortcut(const Ref<Shortcut> &p_shortcut,
|
||||||
context_menu_shortcuts.insert(p_shortcut, p_callable);
|
context_menu_shortcuts.insert(p_shortcut, p_callable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorContextMenuPlugin::add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture, const Ref<Shortcut> &p_shortcut) {
|
void EditorContextMenuPlugin::add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture) {
|
||||||
ERR_FAIL_COND_MSG(context_menu_items.has(p_name), "Context menu item already registered.");
|
ERR_FAIL_COND_MSG(context_menu_items.has(p_name), "Context menu item already registered.");
|
||||||
ERR_FAIL_COND_MSG(context_menu_items.size() == MAX_ITEMS, "Maximum number of context menu items reached.");
|
ERR_FAIL_COND_MSG(context_menu_items.size() == MAX_ITEMS, "Maximum number of context menu items reached.");
|
||||||
|
|
||||||
ContextMenuItem item;
|
ContextMenuItem item;
|
||||||
item.item_name = p_name;
|
item.item_name = p_name;
|
||||||
item.callable = p_callable;
|
item.callable = p_callable;
|
||||||
item.icon = p_texture;
|
item.icon = p_texture;
|
||||||
item.shortcut = p_shortcut;
|
|
||||||
item.idx = EditorData::CONTEXT_MENU_ITEM_ID_BASE + start_idx + context_menu_shortcuts.size() + context_menu_items.size();
|
|
||||||
context_menu_items.insert(p_name, item);
|
context_menu_items.insert(p_name, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorContextMenuPlugin::clear_context_menu_items() {
|
void EditorContextMenuPlugin::add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture) {
|
||||||
context_menu_items.clear();
|
Callable *callback = context_menu_shortcuts.getptr(p_shortcut);
|
||||||
|
ERR_FAIL_NULL_MSG(callback, "Shortcut not registered. Use add_menu_shortcut() first.");
|
||||||
|
|
||||||
|
ContextMenuItem item;
|
||||||
|
item.item_name = p_name;
|
||||||
|
item.callable = *callback;
|
||||||
|
item.icon = p_texture;
|
||||||
|
item.shortcut = p_shortcut;
|
||||||
|
context_menu_items.insert(p_name, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorContextMenuPlugin::_bind_methods() {
|
void EditorContextMenuPlugin::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("add_menu_shortcut", "shortcut", "callback"), &EditorContextMenuPlugin::add_menu_shortcut);
|
ClassDB::bind_method(D_METHOD("add_menu_shortcut", "shortcut", "callback"), &EditorContextMenuPlugin::add_menu_shortcut);
|
||||||
ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon", "shortcut"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Shortcut>()));
|
ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>()));
|
||||||
|
ClassDB::bind_method(D_METHOD("add_context_menu_item_from_shortcut", "name", "shortcut", "icon"), &EditorContextMenuPlugin::add_context_menu_item_from_shortcut, DEFVAL(Ref<Texture2D>()));
|
||||||
|
|
||||||
GDVIRTUAL_BIND(_popup_menu, "paths");
|
GDVIRTUAL_BIND(_popup_menu, "paths");
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorContextMenuPluginManager::add_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) {
|
||||||
|
ERR_FAIL_COND(p_plugin.is_null());
|
||||||
|
ERR_FAIL_COND(plugin_list.has(p_plugin));
|
||||||
|
|
||||||
|
p_plugin->slot = p_slot;
|
||||||
|
plugin_list.push_back(p_plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorContextMenuPluginManager::remove_plugin(const Ref<EditorContextMenuPlugin> &p_plugin) {
|
||||||
|
ERR_FAIL_COND(p_plugin.is_null());
|
||||||
|
ERR_FAIL_COND(!plugin_list.has(p_plugin));
|
||||||
|
|
||||||
|
plugin_list.erase(p_plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorContextMenuPluginManager::add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths) {
|
||||||
|
bool separator_added = false;
|
||||||
|
const int icon_size = p_popup->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||||
|
int id = EditorContextMenuPlugin::BASE_ID;
|
||||||
|
|
||||||
|
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||||
|
if (plugin->slot != p_slot) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
plugin->context_menu_items.clear();
|
||||||
|
plugin->get_options(p_paths);
|
||||||
|
|
||||||
|
HashMap<String, EditorContextMenuPlugin::ContextMenuItem> &items = plugin->context_menu_items;
|
||||||
|
if (items.size() > 0 && !separator_added) {
|
||||||
|
separator_added = true;
|
||||||
|
p_popup->add_separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : items) {
|
||||||
|
EditorContextMenuPlugin::ContextMenuItem &item = E.value;
|
||||||
|
item.id = id;
|
||||||
|
|
||||||
|
if (item.icon.is_valid()) {
|
||||||
|
p_popup->add_icon_item(item.icon, item.item_name, id);
|
||||||
|
p_popup->set_item_icon_max_width(-1, icon_size);
|
||||||
|
} else {
|
||||||
|
p_popup->add_item(item.item_name, id);
|
||||||
|
}
|
||||||
|
if (item.shortcut.is_valid()) {
|
||||||
|
p_popup->set_item_shortcut(-1, item.shortcut, true);
|
||||||
|
}
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Callable EditorContextMenuPluginManager::match_custom_shortcut(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<InputEvent> &p_event) {
|
||||||
|
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||||
|
if (plugin->slot != p_slot) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeyValue<Ref<Shortcut>, Callable> &E : plugin->context_menu_shortcuts) {
|
||||||
|
if (E.key->matches_event(p_event)) {
|
||||||
|
return E.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Callable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorContextMenuPluginManager::activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg) {
|
||||||
|
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||||
|
if (plugin->slot != p_slot) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : plugin->context_menu_items) {
|
||||||
|
if (E.value.id == p_option) {
|
||||||
|
invoke_callback(E.value.callable, p_arg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorContextMenuPluginManager::invoke_callback(const Callable &p_callback, const Variant &p_arg) {
|
||||||
|
const Variant *argptr = &p_arg;
|
||||||
|
Callable::CallError ce;
|
||||||
|
Variant result;
|
||||||
|
p_callback.callp(&argptr, 1, result, ce);
|
||||||
|
|
||||||
|
if (ce.error != Callable::CallError::CALL_OK) {
|
||||||
|
ERR_FAIL_MSG("Failed to execute context menu callback: " + Variant::get_callable_error_text(p_callback, &argptr, 1, ce) + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorContextMenuPluginManager::create() {
|
||||||
|
ERR_FAIL_COND(singleton != nullptr);
|
||||||
|
singleton = memnew(EditorContextMenuPluginManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorContextMenuPluginManager::cleanup() {
|
||||||
|
ERR_FAIL_NULL(singleton);
|
||||||
|
memdelete(singleton);
|
||||||
|
singleton = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,19 +34,33 @@
|
||||||
#include "core/object/gdvirtual.gen.inc"
|
#include "core/object/gdvirtual.gen.inc"
|
||||||
#include "core/object/ref_counted.h"
|
#include "core/object/ref_counted.h"
|
||||||
|
|
||||||
class Texture2D;
|
class InputEvent;
|
||||||
|
class PopupMenu;
|
||||||
class Shortcut;
|
class Shortcut;
|
||||||
|
class Texture2D;
|
||||||
|
|
||||||
class EditorContextMenuPlugin : public RefCounted {
|
class EditorContextMenuPlugin : public RefCounted {
|
||||||
GDCLASS(EditorContextMenuPlugin, RefCounted);
|
GDCLASS(EditorContextMenuPlugin, RefCounted);
|
||||||
|
|
||||||
public:
|
friend class EditorContextMenuPluginManager;
|
||||||
int start_idx;
|
|
||||||
|
|
||||||
inline static constexpr int MAX_ITEMS = 100;
|
inline static constexpr int MAX_ITEMS = 100;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ContextMenuSlot {
|
||||||
|
CONTEXT_SLOT_SCENE_TREE,
|
||||||
|
CONTEXT_SLOT_FILESYSTEM,
|
||||||
|
CONTEXT_SLOT_SCRIPT_EDITOR,
|
||||||
|
CONTEXT_SLOT_FILESYSTEM_CREATE,
|
||||||
|
};
|
||||||
|
inline static constexpr int BASE_ID = 2000;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int slot = -1;
|
||||||
|
|
||||||
|
public:
|
||||||
struct ContextMenuItem {
|
struct ContextMenuItem {
|
||||||
int idx = 0;
|
int id = 0;
|
||||||
String item_name;
|
String item_name;
|
||||||
Callable callable;
|
Callable callable;
|
||||||
Ref<Texture2D> icon;
|
Ref<Texture2D> icon;
|
||||||
|
@ -61,10 +75,37 @@ protected:
|
||||||
GDVIRTUAL1(_popup_menu, Vector<String>);
|
GDVIRTUAL1(_popup_menu, Vector<String>);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void add_options(const Vector<String> &p_paths);
|
virtual void get_options(const Vector<String> &p_paths);
|
||||||
|
|
||||||
void add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable);
|
void add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable);
|
||||||
void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture, const Ref<Shortcut> &p_shortcut);
|
void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture);
|
||||||
void clear_context_menu_items();
|
void add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture);
|
||||||
|
};
|
||||||
|
|
||||||
|
VARIANT_ENUM_CAST(EditorContextMenuPlugin::ContextMenuSlot);
|
||||||
|
|
||||||
|
class EditorContextMenuPluginManager : public Object {
|
||||||
|
GDCLASS(EditorContextMenuPluginManager, Object);
|
||||||
|
|
||||||
|
using ContextMenuSlot = EditorContextMenuPlugin::ContextMenuSlot;
|
||||||
|
static inline EditorContextMenuPluginManager *singleton = nullptr;
|
||||||
|
|
||||||
|
LocalVector<Ref<EditorContextMenuPlugin>> plugin_list;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static EditorContextMenuPluginManager *get_singleton() { return singleton; }
|
||||||
|
|
||||||
|
void add_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
||||||
|
void remove_plugin(const Ref<EditorContextMenuPlugin> &p_plugin);
|
||||||
|
|
||||||
|
void add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths);
|
||||||
|
Callable match_custom_shortcut(ContextMenuSlot p_slot, const Ref<InputEvent> &p_event);
|
||||||
|
bool activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg);
|
||||||
|
|
||||||
|
void invoke_callback(const Callable &p_callback, const Variant &p_arg);
|
||||||
|
|
||||||
|
static void create();
|
||||||
|
static void cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // EDITOR_CONTEXT_MENU_PLUGIN_H
|
#endif // EDITOR_CONTEXT_MENU_PLUGIN_H
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
#include "editor/import/editor_import_plugin.h"
|
#include "editor/import/editor_import_plugin.h"
|
||||||
#include "editor/inspector_dock.h"
|
#include "editor/inspector_dock.h"
|
||||||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||||
#include "editor/plugins/editor_context_menu_plugin.h"
|
|
||||||
#include "editor/plugins/editor_debugger_plugin.h"
|
#include "editor/plugins/editor_debugger_plugin.h"
|
||||||
#include "editor/plugins/editor_resource_conversion_plugin.h"
|
#include "editor/plugins/editor_resource_conversion_plugin.h"
|
||||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||||
|
@ -491,14 +490,12 @@ void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImpo
|
||||||
ResourceImporterScene::remove_post_importer_plugin(p_plugin);
|
ResourceImporterScene::remove_post_importer_plugin(p_plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorPlugin::add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) {
|
void EditorPlugin::add_context_menu_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) {
|
||||||
ERR_FAIL_COND(p_plugin.is_null());
|
EditorContextMenuPluginManager::get_singleton()->add_plugin(p_slot, p_plugin);
|
||||||
EditorNode::get_editor_data().add_context_menu_plugin(EditorData::ContextMenuSlot(p_slot), p_plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorPlugin::remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) {
|
void EditorPlugin::remove_context_menu_plugin(const Ref<EditorContextMenuPlugin> &p_plugin) {
|
||||||
ERR_FAIL_COND(p_plugin.is_null());
|
EditorContextMenuPluginManager::get_singleton()->remove_plugin(p_plugin);
|
||||||
EditorNode::get_editor_data().remove_context_menu_plugin(EditorData::ContextMenuSlot(p_slot), p_plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int find(const PackedStringArray &a, const String &v) {
|
int find(const PackedStringArray &a, const String &v) {
|
||||||
|
@ -641,7 +638,7 @@ void EditorPlugin::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled);
|
ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled);
|
||||||
ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled);
|
ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled);
|
||||||
ClassDB::bind_method(D_METHOD("add_context_menu_plugin", "slot", "plugin"), &EditorPlugin::add_context_menu_plugin);
|
ClassDB::bind_method(D_METHOD("add_context_menu_plugin", "slot", "plugin"), &EditorPlugin::add_context_menu_plugin);
|
||||||
ClassDB::bind_method(D_METHOD("remove_context_menu_plugin", "slot", "plugin"), &EditorPlugin::remove_context_menu_plugin);
|
ClassDB::bind_method(D_METHOD("remove_context_menu_plugin", "plugin"), &EditorPlugin::remove_context_menu_plugin);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
|
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
|
||||||
ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
|
ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
|
||||||
|
@ -707,11 +704,6 @@ void EditorPlugin::_bind_methods() {
|
||||||
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS);
|
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS);
|
||||||
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP);
|
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP);
|
||||||
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM);
|
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM);
|
||||||
|
|
||||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TREE);
|
|
||||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM);
|
|
||||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR);
|
|
||||||
BIND_ENUM_CONSTANT(CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorUndoRedoManager *EditorPlugin::get_undo_redo() {
|
EditorUndoRedoManager *EditorPlugin::get_undo_redo() {
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#define EDITOR_PLUGIN_H
|
#define EDITOR_PLUGIN_H
|
||||||
|
|
||||||
#include "core/io/config_file.h"
|
#include "core/io/config_file.h"
|
||||||
|
#include "editor/plugins/editor_context_menu_plugin.h"
|
||||||
#include "scene/3d/camera_3d.h"
|
#include "scene/3d/camera_3d.h"
|
||||||
#include "scene/gui/control.h"
|
#include "scene/gui/control.h"
|
||||||
|
|
||||||
|
@ -53,7 +54,6 @@ class EditorToolAddons;
|
||||||
class EditorTranslationParserPlugin;
|
class EditorTranslationParserPlugin;
|
||||||
class EditorUndoRedoManager;
|
class EditorUndoRedoManager;
|
||||||
class ScriptCreateDialog;
|
class ScriptCreateDialog;
|
||||||
class EditorContextMenuPlugin;
|
|
||||||
|
|
||||||
class EditorPlugin : public Node {
|
class EditorPlugin : public Node {
|
||||||
GDCLASS(EditorPlugin, Node);
|
GDCLASS(EditorPlugin, Node);
|
||||||
|
@ -103,13 +103,6 @@ public:
|
||||||
AFTER_GUI_INPUT_CUSTOM,
|
AFTER_GUI_INPUT_CUSTOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ContextMenuSlot {
|
|
||||||
CONTEXT_SLOT_SCENE_TREE,
|
|
||||||
CONTEXT_SLOT_FILESYSTEM,
|
|
||||||
CONTEXT_SLOT_SCRIPT_EDITOR,
|
|
||||||
CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
|
||||||
|
@ -257,8 +250,8 @@ public:
|
||||||
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
||||||
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
||||||
|
|
||||||
void add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
void add_context_menu_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
||||||
void remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
void remove_context_menu_plugin(const Ref<EditorContextMenuPlugin> &p_plugin);
|
||||||
|
|
||||||
void enable_plugin();
|
void enable_plugin();
|
||||||
void disable_plugin();
|
void disable_plugin();
|
||||||
|
@ -270,7 +263,6 @@ public:
|
||||||
VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer);
|
VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer);
|
||||||
VARIANT_ENUM_CAST(EditorPlugin::DockSlot);
|
VARIANT_ENUM_CAST(EditorPlugin::DockSlot);
|
||||||
VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput);
|
VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput);
|
||||||
VARIANT_ENUM_CAST(EditorPlugin::ContextMenuSlot);
|
|
||||||
|
|
||||||
typedef EditorPlugin *(*EditorPluginCreateFunc)();
|
typedef EditorPlugin *(*EditorPluginCreateFunc)();
|
||||||
|
|
||||||
|
|
|
@ -1399,13 +1399,12 @@ void ScriptEditor::_menu_option(int p_option) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context menu options.
|
if (p_option >= EditorContextMenuPlugin::BASE_ID) {
|
||||||
if (p_option >= EditorData::CONTEXT_MENU_ITEM_ID_BASE) {
|
|
||||||
Ref<Resource> resource;
|
Ref<Resource> resource;
|
||||||
if (current) {
|
if (current) {
|
||||||
resource = current->get_edited_resource();
|
resource = current->get_edited_resource();
|
||||||
}
|
}
|
||||||
EditorNode::get_editor_data().script_editor_options_pressed(EditorData::CONTEXT_SLOT_SCRIPT_EDITOR, p_option, resource);
|
EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR, p_option, resource);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3315,10 +3314,16 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||||
_menu_option(WINDOW_MOVE_DOWN);
|
_menu_option(WINDOW_MOVE_DOWN);
|
||||||
accept_event();
|
accept_event();
|
||||||
}
|
}
|
||||||
// Context menu shortcuts.
|
|
||||||
int match_option = EditorNode::get_editor_data().match_context_menu_shortcut(EditorData::CONTEXT_SLOT_SCRIPT_EDITOR, p_event);
|
Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR, p_event);
|
||||||
if (match_option) {
|
if (custom_callback.is_valid()) {
|
||||||
_menu_option(match_option);
|
Ref<Resource> resource;
|
||||||
|
ScriptEditorBase *current = _get_current_editor();
|
||||||
|
if (current) {
|
||||||
|
resource = current->get_edited_resource();
|
||||||
|
}
|
||||||
|
EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, resource);
|
||||||
|
accept_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3387,7 +3392,7 @@ void ScriptEditor::_make_script_list_context_menu() {
|
||||||
selected_paths.push_back(path);
|
selected_paths.push_back(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EditorNode::get_editor_data().add_options_from_plugins(context_menu, EditorData::CONTEXT_SLOT_SCRIPT_EDITOR, selected_paths);
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR, selected_paths);
|
||||||
|
|
||||||
context_menu->set_position(get_screen_position() + get_local_mouse_position());
|
context_menu->set_position(get_screen_position() + get_local_mouse_position());
|
||||||
context_menu->reset_size();
|
context_menu->reset_size();
|
||||||
|
|
|
@ -214,11 +214,12 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||||
} else if (ED_IS_SHORTCUT("scene_tree/delete", p_event)) {
|
} else if (ED_IS_SHORTCUT("scene_tree/delete", p_event)) {
|
||||||
_tool_selected(TOOL_ERASE);
|
_tool_selected(TOOL_ERASE);
|
||||||
} else {
|
} else {
|
||||||
int match_option = EditorNode::get_editor_data().match_context_menu_shortcut(EditorData::CONTEXT_SLOT_SCENE_TREE, p_event);
|
Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_event);
|
||||||
if (match_option) {
|
if (custom_callback.is_valid()) {
|
||||||
_tool_selected(match_option);
|
EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, _get_selection_array());
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool selection was successful, accept the event to stop propagation.
|
// Tool selection was successful, accept the event to stop propagation.
|
||||||
|
@ -1487,10 +1488,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
// Editor context plugin.
|
if (p_tool >= EditorContextMenuPlugin::BASE_ID) {
|
||||||
if (p_tool >= EditorData::CONTEXT_MENU_ITEM_ID_BASE) {
|
EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_tool, _get_selection_array());
|
||||||
List<Node *> selection = editor_selection->get_selected_node_list();
|
|
||||||
EditorNode::get_editor_data().scene_tree_options_pressed(EditorData::CONTEXT_SLOT_SCENE_TREE, p_tool, selection);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3348,6 +3347,18 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array SceneTreeDock::_get_selection_array() {
|
||||||
|
List<Node *> selection = editor_selection->get_selected_node_list();
|
||||||
|
TypedArray<Node> array;
|
||||||
|
array.resize(selection.size());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const Node *E : selection) {
|
||||||
|
array[i++] = E;
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type) {
|
void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type) {
|
||||||
Node *node = get_node(p_to);
|
Node *node = get_node(p_to);
|
||||||
ERR_FAIL_NULL(node);
|
ERR_FAIL_NULL(node);
|
||||||
|
@ -3748,7 +3759,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
||||||
String node_path = root->get_path().rel_path_to(E->get()->get_path());
|
String node_path = root->get_path().rel_path_to(E->get()->get_path());
|
||||||
p_paths.push_back(node_path);
|
p_paths.push_back(node_path);
|
||||||
}
|
}
|
||||||
EditorNode::get_editor_data().add_options_from_plugins(menu, EditorData::CONTEXT_SLOT_SCENE_TREE, p_paths);
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_paths);
|
||||||
|
|
||||||
menu->reset_size();
|
menu->reset_size();
|
||||||
menu->set_position(p_menu_pos);
|
menu->set_position(p_menu_pos);
|
||||||
|
|
|
@ -261,6 +261,7 @@ class SceneTreeDock : public VBoxContainer {
|
||||||
bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const;
|
bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const;
|
||||||
|
|
||||||
void _normalize_drop(Node *&to_node, int &to_pos, int p_type);
|
void _normalize_drop(Node *&to_node, int &to_pos, int p_type);
|
||||||
|
Array _get_selection_array();
|
||||||
|
|
||||||
void _nodes_dragged(const Array &p_nodes, NodePath p_to, int p_type);
|
void _nodes_dragged(const Array &p_nodes, NodePath p_to, int p_type);
|
||||||
void _files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type);
|
void _files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type);
|
||||||
|
|
Loading…
Reference in New Issue