Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow GDExtensions to add editor plugins #77010

Merged
merged 1 commit into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions core/extension/gdextension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -677,3 +677,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
24 changes: 24 additions & 0 deletions core/extension/gdextension.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,28 @@ class GDExtensionResourceLoader : public ResourceFormatLoader {
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
16 changes: 16 additions & 0 deletions core/extension/gdextension_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,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() {
Expand Down Expand Up @@ -1185,6 +1199,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
Expand Down
20 changes: 20 additions & 0 deletions core/extension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2128,6 +2128,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
Expand Down
18 changes: 18 additions & 0 deletions editor/editor_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions editor/editor_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class EditorData {

private:
Vector<EditorPlugin *> editor_plugins;
HashMap<StringName, EditorPlugin *> extension_editor_plugins;

struct PropertyData {
String name;
Expand Down Expand Up @@ -170,6 +171,11 @@ class EditorData {
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();
Expand Down
4 changes: 4 additions & 0 deletions editor/editor_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class EditorCommandPalette;
class EditorFileSystem;
class EditorInspector;
class EditorPaths;
class EditorPlugin;
class EditorResourcePreview;
class EditorSelection;
class EditorSettings;
Expand Down Expand Up @@ -83,6 +84,9 @@ class EditorInterface : public Object {
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;
Expand Down
30 changes: 30 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3243,6 +3243,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;
Expand Down Expand Up @@ -7769,6 +7793,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]();
}
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,9 @@ class EditorNode : public Node {
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);
Expand Down