summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2023-05-26 11:00:32 +0200
committerRémi Verschelde <rverschelde@gmail.com>2023-05-26 11:00:32 +0200
commit699b66b62d34d17d72dced139d6691aab64b8180 (patch)
tree5e9f89717fa088cef360c7a14264ea90d19f574d
parentcb711a995061e7a7e097dcb0ed5760bcba3f23d1 (diff)
parent300716321072c719dc5c3f8a19126fe753747a60 (diff)
downloadredot-engine-699b66b62d34d17d72dced139d6691aab64b8180.tar.gz
Merge pull request #77010 from dsnopek/gdextension-editor-plugins
Allow GDExtensions to add editor plugins
-rw-r--r--core/extension/gdextension.cpp22
-rw-r--r--core/extension/gdextension.h24
-rw-r--r--core/extension/gdextension_interface.cpp16
-rw-r--r--core/extension/gdextension_interface.h20
-rw-r--r--editor/editor_data.cpp18
-rw-r--r--editor/editor_data.h6
-rw-r--r--editor/editor_interface.h4
-rw-r--r--editor/editor_node.cpp30
-rw-r--r--editor/editor_node.h3
9 files changed, 143 insertions, 0 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 0cbcf58882..8bdea01ae6 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -678,3 +678,25 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
}
return "";
}
+
+#ifdef TOOLS_ENABLED
+Vector<StringName> GDExtensionEditorPlugins::extension_classes;
+GDExtensionEditorPlugins::EditorPluginRegisterFunc GDExtensionEditorPlugins::editor_node_add_plugin = nullptr;
+GDExtensionEditorPlugins::EditorPluginRegisterFunc GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr;
+
+void GDExtensionEditorPlugins::add_extension_class(const StringName &p_class_name) {
+ if (editor_node_add_plugin) {
+ editor_node_add_plugin(p_class_name);
+ } else {
+ extension_classes.push_back(p_class_name);
+ }
+}
+
+void GDExtensionEditorPlugins::remove_extension_class(const StringName &p_class_name) {
+ if (editor_node_remove_plugin) {
+ editor_node_remove_plugin(p_class_name);
+ } else {
+ extension_classes.erase(p_class_name);
+ }
+}
+#endif // TOOLS_ENABLED
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 95811820cf..49f1cf1d8e 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -107,4 +107,28 @@ public:
virtual String get_resource_type(const String &p_path) const;
};
+#ifdef TOOLS_ENABLED
+class GDExtensionEditorPlugins {
+private:
+ static Vector<StringName> extension_classes;
+
+protected:
+ friend class EditorNode;
+
+ // Since this in core, we can't directly reference EditorNode, so it will
+ // set these function pointers in its constructor.
+ typedef void (*EditorPluginRegisterFunc)(const StringName &p_class_name);
+ static EditorPluginRegisterFunc editor_node_add_plugin;
+ static EditorPluginRegisterFunc editor_node_remove_plugin;
+
+public:
+ static void add_extension_class(const StringName &p_class_name);
+ static void remove_extension_class(const StringName &p_class_name);
+
+ static const Vector<StringName> &get_extension_classes() {
+ return extension_classes;
+ }
+};
+#endif // TOOLS_ENABLED
+
#endif // GDEXTENSION_H
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 12ef1772e3..21d34b6e0c 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1071,6 +1071,20 @@ static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_c
return class_info ? class_info->class_ptr : nullptr;
}
+static void gdextension_editor_add_plugin(GDExtensionConstStringNamePtr p_classname) {
+#ifdef TOOLS_ENABLED
+ const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
+ GDExtensionEditorPlugins::add_extension_class(classname);
+#endif
+}
+
+static void gdextension_editor_remove_plugin(GDExtensionConstStringNamePtr p_classname) {
+#ifdef TOOLS_ENABLED
+ const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
+ GDExtensionEditorPlugins::remove_extension_class(classname);
+#endif
+}
+
#define REGISTER_INTERFACE_FUNC(m_name) GDExtension::register_interface_function(#m_name, (GDExtensionInterfaceFunctionPtr)&gdextension_##m_name)
void gdextension_setup_interface() {
@@ -1199,6 +1213,8 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
+ REGISTER_INTERFACE_FUNC(editor_add_plugin);
+ REGISTER_INTERFACE_FUNC(editor_remove_plugin);
}
#undef REGISTER_INTERFACE_FUNCTION
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index a5ea3918df..3aa41f28da 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -2141,6 +2141,26 @@ typedef void (*GDExtensionInterfaceClassdbUnregisterExtensionClass)(GDExtensionC
*/
typedef void (*GDExtensionInterfaceGetLibraryPath)(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path);
+/**
+ * @name editor_add_plugin
+ *
+ * Adds an editor plugin.
+ *
+ * It's safe to call during initialization.
+ *
+ * @param p_class_name A pointer to a StringName with the name of a class (descending from EditorPlugin) which is already registered with ClassDB.
+ */
+typedef void (*GDExtensionInterfaceEditorAddPlugin)(GDExtensionConstStringNamePtr p_class_name);
+
+/**
+ * @name editor_remove_plugin
+ *
+ * Removes an editor plugin.
+ *
+ * @param p_class_name A pointer to a StringName with the name of a class that was previously added as an editor plugin.
+ */
+typedef void (*GDExtensionInterfaceEditorRemovePlugin)(GDExtensionConstStringNamePtr p_class_name);
+
#ifdef __cplusplus
}
#endif
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 5d3037b4ec..596a2dfac1 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -485,6 +485,24 @@ EditorPlugin *EditorData::get_editor_plugin(int p_idx) {
return editor_plugins[p_idx];
}
+void EditorData::add_extension_editor_plugin(const StringName &p_class_name, EditorPlugin *p_plugin) {
+ ERR_FAIL_COND(extension_editor_plugins.has(p_class_name));
+ extension_editor_plugins.insert(p_class_name, p_plugin);
+}
+
+void EditorData::remove_extension_editor_plugin(const StringName &p_class_name) {
+ extension_editor_plugins.erase(p_class_name);
+}
+
+bool EditorData::has_extension_editor_plugin(const StringName &p_class_name) {
+ return extension_editor_plugins.has(p_class_name);
+}
+
+EditorPlugin *EditorData::get_extension_editor_plugin(const StringName &p_class_name) {
+ EditorPlugin **plugin = extension_editor_plugins.getptr(p_class_name);
+ return plugin == nullptr ? nullptr : *plugin;
+}
+
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.");
CustomType ct;
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 7ca04b5680..28fe13e537 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -124,6 +124,7 @@ public:
private:
Vector<EditorPlugin *> editor_plugins;
+ HashMap<StringName, EditorPlugin *> extension_editor_plugins;
struct PropertyData {
String name;
@@ -170,6 +171,11 @@ public:
int get_editor_plugin_count() const;
EditorPlugin *get_editor_plugin(int p_idx);
+ void add_extension_editor_plugin(const StringName &p_class_name, EditorPlugin *p_plugin);
+ void remove_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);
+
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);
const Vector<Callable> get_undo_redo_inspector_hook_callback();
diff --git a/editor/editor_interface.h b/editor/editor_interface.h
index 1436bf83af..95fa0dd64f 100644
--- a/editor/editor_interface.h
+++ b/editor/editor_interface.h
@@ -41,6 +41,7 @@ class EditorCommandPalette;
class EditorFileSystem;
class EditorInspector;
class EditorPaths;
+class EditorPlugin;
class EditorResourcePreview;
class EditorSelection;
class EditorSettings;
@@ -83,6 +84,9 @@ public:
void set_plugin_enabled(const String &p_plugin, bool p_enabled);
bool is_plugin_enabled(const String &p_plugin) const;
+ void add_editor_plugin(EditorPlugin *p_plugin);
+ void remove_editor_plugin(EditorPlugin *p_plugin);
+
// Editor GUI.
Control *get_base_control() const;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index ce7702d5b0..10c260310f 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3239,6 +3239,30 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_chan
}
}
+void EditorNode::add_extension_editor_plugin(const StringName &p_class_name) {
+ ERR_FAIL_COND_MSG(!ClassDB::class_exists(p_class_name), vformat("No such editor plugin registered: %s", p_class_name));
+ ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_class_name, SNAME("EditorPlugin")), vformat("Class is not an editor plugin: %s", p_class_name));
+ ERR_FAIL_COND_MSG(singleton->editor_data.has_extension_editor_plugin(p_class_name), vformat("Editor plugin already added for class: %s", p_class_name));
+
+ EditorPlugin *plugin = Object::cast_to<EditorPlugin>(ClassDB::instantiate(p_class_name));
+ singleton->editor_data.add_extension_editor_plugin(p_class_name, plugin);
+ add_editor_plugin(plugin);
+}
+
+void EditorNode::remove_extension_editor_plugin(const StringName &p_class_name) {
+ // If we're exiting, the editor plugins will get cleaned up anyway, so don't do anything.
+ if (singleton->exiting) {
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!singleton->editor_data.has_extension_editor_plugin(p_class_name), vformat("No editor plugin added for class: %s", p_class_name));
+
+ EditorPlugin *plugin = singleton->editor_data.get_extension_editor_plugin(p_class_name);
+ remove_editor_plugin(plugin);
+ memfree(plugin);
+ singleton->editor_data.remove_extension_editor_plugin(p_class_name);
+}
+
void EditorNode::_update_addon_config() {
if (_initializing_plugins) {
return;
@@ -7768,6 +7792,12 @@ EditorNode::EditorNode() {
add_editor_plugin(EditorPlugins::create(i));
}
+ for (const StringName &extension_class_name : GDExtensionEditorPlugins::get_extension_classes()) {
+ add_extension_editor_plugin(extension_class_name);
+ }
+ GDExtensionEditorPlugins::editor_node_add_plugin = &EditorNode::add_extension_editor_plugin;
+ GDExtensionEditorPlugins::editor_node_remove_plugin = &EditorNode::remove_extension_editor_plugin;
+
for (int i = 0; i < plugin_init_callback_count; i++) {
plugin_init_callbacks[i]();
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 66da019560..c92601a44a 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -739,6 +739,9 @@ public:
static void add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false);
static void remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false);
+ static void add_extension_editor_plugin(const StringName &p_class_name);
+ static void remove_extension_editor_plugin(const StringName &p_class_name);
+
static void add_plugin_init_callback(EditorPluginInitializeCallback p_callback);
static void add_init_callback(EditorNodeInitCallback p_callback) { _init_callbacks.push_back(p_callback); }
static void add_build_callback(EditorBuildCallback p_callback);