initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

6
editor/plugins/SCsub Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* editor_plugin.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
Button *EditorPlugin::_add_control_to_bottom_panel_compat_88081(Control *p_control, const String &p_title) {
return add_control_to_bottom_panel(p_control, p_title, nullptr);
}
void EditorPlugin::_add_control_to_dock_compat_88081(DockSlot p_slot, Control *p_control) {
return add_control_to_dock(p_slot, p_control, nullptr);
}
void EditorPlugin::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("add_control_to_bottom_panel", "control", "title"), &EditorPlugin::_add_control_to_bottom_panel_compat_88081);
ClassDB::bind_compatibility_method(D_METHOD("add_control_to_dock", "slot", "control"), &EditorPlugin::_add_control_to_dock_compat_88081);
}
#endif

View File

@@ -0,0 +1,712 @@
/**************************************************************************/
/* editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "editor_plugin.h"
#include "editor_plugin.compat.inc"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/editor_debugger_plugin.h"
#include "editor/docks/editor_dock_manager.h"
#include "editor/docks/inspector_dock.h"
#include "editor/docks/scene_tree_dock.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/export/editor_export.h"
#include "editor/export/editor_export_platform.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_title_bar.h"
#include "editor/import/3d/resource_importer_scene.h"
#include "editor/import/editor_import_plugin.h"
#include "editor/inspector/editor_inspector.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/scene/canvas_item_editor_plugin.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/settings/project_settings_editor.h"
#include "editor/translations/editor_translation_parser.h"
#include "scene/3d/camera_3d.h"
#include "scene/gui/popup_menu.h"
void EditorPlugin::add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon) {
EditorNode::get_editor_data().add_custom_type(p_type, p_base, p_script, p_icon);
}
void EditorPlugin::remove_custom_type(const String &p_type) {
EditorNode::get_editor_data().remove_custom_type(p_type);
}
void EditorPlugin::add_autoload_singleton(const String &p_name, const String &p_path) {
if (p_path.begins_with("res://")) {
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_add(p_name, p_path);
} else {
const Ref<Script> plugin_script = static_cast<Ref<Script>>(get_script());
ERR_FAIL_COND(plugin_script.is_null());
const String script_base_path = plugin_script->get_path().get_base_dir();
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_add(p_name, script_base_path.path_join(p_path));
}
}
void EditorPlugin::remove_autoload_singleton(const String &p_name) {
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_remove(p_name);
}
Button *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title, const Ref<Shortcut> &p_shortcut) {
ERR_FAIL_NULL_V(p_control, nullptr);
return EditorNode::get_bottom_panel()->add_item(p_title, p_control, p_shortcut);
}
void EditorPlugin::add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut) {
ERR_FAIL_NULL(p_control);
EditorDockManager::get_singleton()->add_dock(p_control, String(), EditorDockManager::DockSlot(p_slot), p_shortcut);
}
void EditorPlugin::remove_control_from_docks(Control *p_control) {
ERR_FAIL_NULL(p_control);
EditorDockManager::get_singleton()->remove_dock(p_control);
}
void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
ERR_FAIL_NULL(p_control);
EditorNode::get_bottom_panel()->remove_item(p_control);
}
void EditorPlugin::set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon) {
ERR_FAIL_NULL(p_control);
EditorDockManager::get_singleton()->set_dock_tab_icon(p_control, p_icon);
}
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
ERR_FAIL_NULL(p_control);
switch (p_location) {
case CONTAINER_TOOLBAR: {
EditorNode::get_title_bar()->add_child(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_MENU: {
Node3DEditor::get_singleton()->add_control_to_menu_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: {
Node3DEditor::get_singleton()->add_control_to_left_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: {
Node3DEditor::get_singleton()->add_control_to_right_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_BOTTOM: {
Node3DEditor::get_singleton()->get_shader_split()->add_child(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_MENU: {
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: {
CanvasItemEditor::get_singleton()->add_control_to_left_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: {
CanvasItemEditor::get_singleton()->add_control_to_right_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_BOTTOM: {
CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control);
} break;
case CONTAINER_INSPECTOR_BOTTOM: {
InspectorDock::get_singleton()->get_addon_area()->add_child(p_control);
} break;
case CONTAINER_PROJECT_SETTING_TAB_LEFT: {
ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(p_control);
ProjectSettingsEditor::get_singleton()->get_tabs()->move_child(p_control, 0);
} break;
case CONTAINER_PROJECT_SETTING_TAB_RIGHT: {
ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(p_control);
} break;
}
}
void EditorPlugin::remove_control_from_container(CustomControlContainer p_location, Control *p_control) {
ERR_FAIL_NULL(p_control);
switch (p_location) {
case CONTAINER_TOOLBAR: {
EditorNode::get_title_bar()->remove_child(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_MENU: {
Node3DEditor::get_singleton()->remove_control_from_menu_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: {
Node3DEditor::get_singleton()->remove_control_from_left_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: {
Node3DEditor::get_singleton()->remove_control_from_right_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_BOTTOM: {
Node3DEditor::get_singleton()->get_shader_split()->remove_child(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_MENU: {
CanvasItemEditor::get_singleton()->remove_control_from_menu_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: {
CanvasItemEditor::get_singleton()->remove_control_from_left_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: {
CanvasItemEditor::get_singleton()->remove_control_from_right_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_BOTTOM: {
CanvasItemEditor::get_singleton()->get_bottom_split()->remove_child(p_control);
} break;
case CONTAINER_INSPECTOR_BOTTOM: {
InspectorDock::get_singleton()->get_addon_area()->remove_child(p_control);
} break;
case CONTAINER_PROJECT_SETTING_TAB_LEFT:
case CONTAINER_PROJECT_SETTING_TAB_RIGHT: {
ProjectSettingsEditor::get_singleton()->get_tabs()->remove_child(p_control);
} break;
}
}
void EditorPlugin::add_tool_menu_item(const String &p_name, const Callable &p_callable) {
EditorNode::get_singleton()->add_tool_menu_item(p_name, p_callable);
}
void EditorPlugin::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
ERR_FAIL_NULL(p_submenu);
EditorNode::get_singleton()->add_tool_submenu_item(p_name, p_submenu);
}
void EditorPlugin::remove_tool_menu_item(const String &p_name) {
EditorNode::get_singleton()->remove_tool_menu_item(p_name);
}
PopupMenu *EditorPlugin::get_export_as_menu() {
return EditorNode::get_singleton()->get_export_as_menu();
}
void EditorPlugin::set_input_event_forwarding_always_enabled() {
input_event_forwarding_always_enabled = true;
EditorPluginList *always_input_forwarding_list = EditorNode::get_singleton()->get_editor_plugins_force_input_forwarding();
always_input_forwarding_list->add_plugin(this);
}
void EditorPlugin::set_force_draw_over_forwarding_enabled() {
force_draw_over_forwarding_enabled = true;
EditorPluginList *always_draw_over_forwarding_list = EditorNode::get_singleton()->get_editor_plugins_force_over();
always_draw_over_forwarding_list->add_plugin(this);
}
void EditorPlugin::notify_scene_changed(const Node *scn_root) {
emit_signal(SNAME("scene_changed"), scn_root);
}
void EditorPlugin::notify_main_screen_changed(const String &screen_name) {
if (screen_name == last_main_screen_name) {
return;
}
emit_signal(SNAME("main_screen_changed"), screen_name);
last_main_screen_name = screen_name;
}
void EditorPlugin::notify_scene_closed(const String &scene_filepath) {
emit_signal(SNAME("scene_closed"), scene_filepath);
}
void EditorPlugin::notify_resource_saved(const Ref<Resource> &p_resource) {
emit_signal(SNAME("resource_saved"), p_resource);
}
void EditorPlugin::notify_scene_saved(const String &p_scene_filepath) {
emit_signal(SNAME("scene_saved"), p_scene_filepath);
}
bool EditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
bool success = false;
GDVIRTUAL_CALL(_forward_canvas_gui_input, p_event, success);
return success;
}
void EditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
GDVIRTUAL_CALL(_forward_canvas_draw_over_viewport, p_overlay);
}
void EditorPlugin::forward_canvas_force_draw_over_viewport(Control *p_overlay) {
GDVIRTUAL_CALL(_forward_canvas_force_draw_over_viewport, p_overlay);
}
// Updates the overlays of the 2D viewport or, if in 3D mode, of every 3D viewport.
int EditorPlugin::update_overlays() const {
if (Node3DEditor::get_singleton()->is_visible()) {
int count = 0;
for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) {
Node3DEditorViewport *vp = Node3DEditor::get_singleton()->get_editor_viewport(i);
if (vp->is_visible()) {
vp->update_surface();
count++;
}
}
return count;
} else {
// This will update the normal viewport itself as well
CanvasItemEditor::get_singleton()->get_viewport_control()->queue_redraw();
return 1;
}
}
EditorPlugin::AfterGUIInput EditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
int success = EditorPlugin::AFTER_GUI_INPUT_PASS;
GDVIRTUAL_CALL(_forward_3d_gui_input, p_camera, p_event, success);
return static_cast<EditorPlugin::AfterGUIInput>(success);
}
void EditorPlugin::forward_3d_draw_over_viewport(Control *p_overlay) {
GDVIRTUAL_CALL(_forward_3d_draw_over_viewport, p_overlay);
}
void EditorPlugin::forward_3d_force_draw_over_viewport(Control *p_overlay) {
GDVIRTUAL_CALL(_forward_3d_force_draw_over_viewport, p_overlay);
}
String EditorPlugin::get_plugin_name() const {
String name;
GDVIRTUAL_CALL(_get_plugin_name, name);
return name;
}
const Ref<Texture2D> EditorPlugin::get_plugin_icon() const {
Ref<Texture2D> icon;
GDVIRTUAL_CALL(_get_plugin_icon, icon);
return icon;
}
String EditorPlugin::get_plugin_version() const {
return plugin_version;
}
void EditorPlugin::set_plugin_version(const String &p_version) {
plugin_version = p_version;
}
bool EditorPlugin::has_main_screen() const {
bool success = false;
GDVIRTUAL_CALL(_has_main_screen, success);
return success;
}
void EditorPlugin::make_visible(bool p_visible) {
GDVIRTUAL_CALL(_make_visible, p_visible);
}
void EditorPlugin::edit(Object *p_object) {
GDVIRTUAL_CALL(_edit, p_object);
}
bool EditorPlugin::handles(Object *p_object) const {
bool success = false;
GDVIRTUAL_CALL(_handles, p_object, success);
return success;
}
bool EditorPlugin::can_auto_hide() const {
return true;
}
Dictionary EditorPlugin::get_state() const {
Dictionary state;
GDVIRTUAL_CALL(_get_state, state);
return state;
}
void EditorPlugin::set_state(const Dictionary &p_state) {
GDVIRTUAL_CALL(_set_state, p_state);
}
void EditorPlugin::clear() {
GDVIRTUAL_CALL(_clear);
}
String EditorPlugin::get_unsaved_status(const String &p_for_scene) const {
String ret;
GDVIRTUAL_CALL(_get_unsaved_status, p_for_scene, ret);
return ret;
}
void EditorPlugin::save_external_data() {
GDVIRTUAL_CALL(_save_external_data);
}
// if changes are pending in editor, apply them
void EditorPlugin::apply_changes() {
GDVIRTUAL_CALL(_apply_changes);
}
void EditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
PackedStringArray arr;
if (GDVIRTUAL_CALL(_get_breakpoints, arr)) {
for (int i = 0; i < arr.size(); i++) {
p_breakpoints->push_back(arr[i]);
}
}
}
bool EditorPlugin::get_remove_list(List<Node *> *p_list) {
return false;
}
void EditorPlugin::add_undo_redo_inspector_hook_callback(Callable p_callable) {
EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(p_callable);
}
void EditorPlugin::remove_undo_redo_inspector_hook_callback(Callable p_callable) {
EditorNode::get_editor_data().remove_undo_redo_inspector_hook_callback(p_callable);
}
void EditorPlugin::add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) {
ERR_FAIL_COND(p_parser.is_null());
EditorTranslationParser::get_singleton()->add_parser(p_parser, EditorTranslationParser::CUSTOM);
}
void EditorPlugin::remove_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) {
ERR_FAIL_COND(p_parser.is_null());
EditorTranslationParser::get_singleton()->remove_parser(p_parser, EditorTranslationParser::CUSTOM);
}
void EditorPlugin::add_import_plugin(const Ref<EditorImportPlugin> &p_importer, bool p_first_priority) {
ERR_FAIL_COND(p_importer.is_null());
ResourceFormatImporter::get_singleton()->add_importer(p_importer, p_first_priority);
// Plugins are now loaded during the first scan. It's important not to start another scan,
// even a deferred one, as it would cause a scan during a scan at the next main thread iteration.
if (!EditorFileSystem::get_singleton()->doing_first_scan()) {
callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan).call_deferred();
}
}
void EditorPlugin::remove_import_plugin(const Ref<EditorImportPlugin> &p_importer) {
ERR_FAIL_COND(p_importer.is_null());
ResourceFormatImporter::get_singleton()->remove_importer(p_importer);
// Plugins are now loaded during the first scan. It's important not to start another scan,
// even a deferred one, as it would cause a scan during a scan at the next main thread iteration.
if (!EditorNode::get_singleton()->is_exiting() && !EditorFileSystem::get_singleton()->doing_first_scan()) {
callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan).call_deferred();
}
}
void EditorPlugin::add_export_plugin(const Ref<EditorExportPlugin> &p_exporter) {
ERR_FAIL_COND(p_exporter.is_null());
EditorExport::get_singleton()->add_export_plugin(p_exporter);
}
void EditorPlugin::remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter) {
ERR_FAIL_COND(p_exporter.is_null());
EditorExport::get_singleton()->remove_export_plugin(p_exporter);
}
void EditorPlugin::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
ERR_FAIL_COND(p_platform.is_null());
EditorExport::get_singleton()->add_export_platform(p_platform);
}
void EditorPlugin::remove_export_platform(const Ref<EditorExportPlatform> &p_platform) {
ERR_FAIL_COND(p_platform.is_null());
EditorExport::get_singleton()->remove_export_platform(p_platform);
}
void EditorPlugin::add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin) {
ERR_FAIL_COND(p_gizmo_plugin.is_null());
Node3DEditor::get_singleton()->add_gizmo_plugin(p_gizmo_plugin);
}
void EditorPlugin::remove_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin) {
ERR_FAIL_COND(p_gizmo_plugin.is_null());
Node3DEditor::get_singleton()->remove_gizmo_plugin(p_gizmo_plugin);
}
void EditorPlugin::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
ERR_FAIL_COND(p_plugin.is_null());
EditorInspector::add_inspector_plugin(p_plugin);
}
void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
ERR_FAIL_COND(p_plugin.is_null());
EditorInspector::remove_inspector_plugin(p_plugin);
}
void EditorPlugin::add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority) {
ERR_FAIL_COND(p_importer.is_null());
ResourceImporterScene::add_scene_importer(p_importer, p_first_priority);
}
void EditorPlugin::remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) {
ERR_FAIL_COND(p_importer.is_null());
ResourceImporterScene::remove_scene_importer(p_importer);
}
void EditorPlugin::add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {
ResourceImporterScene::add_post_importer_plugin(p_plugin, p_first_priority);
}
void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
ResourceImporterScene::remove_post_importer_plugin(p_plugin);
}
void EditorPlugin::add_context_menu_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) {
EditorContextMenuPluginManager::get_singleton()->add_plugin(p_slot, p_plugin);
}
void EditorPlugin::remove_context_menu_plugin(const Ref<EditorContextMenuPlugin> &p_plugin) {
EditorContextMenuPluginManager::get_singleton()->remove_plugin(p_plugin);
}
int find(const PackedStringArray &a, const String &v) {
const String *r = a.ptr();
for (int j = 0; j < a.size(); ++j) {
if (r[j] == v) {
return j;
}
}
return -1;
}
void EditorPlugin::enable_plugin() {
// Called when the plugin gets enabled in project settings, after it's added to the tree.
// You can implement it to register autoloads.
GDVIRTUAL_CALL(_enable_plugin);
}
void EditorPlugin::disable_plugin() {
// Last function called when the plugin gets disabled in project settings.
// Implement it to cleanup things from the project, such as unregister autoloads.
GDVIRTUAL_CALL(_disable_plugin);
}
void EditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
GDVIRTUAL_CALL(_set_window_layout, p_layout);
}
void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
GDVIRTUAL_CALL(_get_window_layout, p_layout);
}
bool EditorPlugin::build() {
bool success = true;
GDVIRTUAL_CALL(_build, success);
return success;
}
void EditorPlugin::queue_save_layout() {
EditorNode::get_singleton()->save_editor_layout_delayed();
}
void EditorPlugin::make_bottom_panel_item_visible(Control *p_item) {
EditorNode::get_bottom_panel()->make_item_visible(p_item);
}
void EditorPlugin::hide_bottom_panel() {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
EditorInterface *EditorPlugin::get_editor_interface() {
return EditorInterface::get_singleton();
}
ScriptCreateDialog *EditorPlugin::get_script_create_dialog() {
return SceneTreeDock::get_singleton()->get_script_create_dialog();
}
void EditorPlugin::add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {
EditorDebuggerNode::get_singleton()->add_debugger_plugin(p_plugin);
}
void EditorPlugin::remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {
EditorDebuggerNode::get_singleton()->remove_debugger_plugin(p_plugin);
}
void EditorPlugin::add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
EditorNode::get_singleton()->add_resource_conversion_plugin(p_plugin);
}
void EditorPlugin::remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
EditorNode::get_singleton()->remove_resource_conversion_plugin(p_plugin);
}
#ifndef DISABLE_DEPRECATED
void EditorPlugin::_editor_project_settings_changed() {
emit_signal(SNAME("project_settings_changed"));
}
#endif
void EditorPlugin::_notification(int p_what) {
#ifndef DISABLE_DEPRECATED
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed));
} break;
case NOTIFICATION_EXIT_TREE: {
ProjectSettings::get_singleton()->disconnect("settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed));
} break;
}
#endif
}
void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
ClassDB::bind_method(D_METHOD("add_control_to_bottom_panel", "control", "title", "shortcut"), &EditorPlugin::add_control_to_bottom_panel, DEFVAL(Ref<Shortcut>()));
ClassDB::bind_method(D_METHOD("add_control_to_dock", "slot", "control", "shortcut"), &EditorPlugin::add_control_to_dock, DEFVAL(Ref<Shortcut>()));
ClassDB::bind_method(D_METHOD("remove_control_from_docks", "control"), &EditorPlugin::remove_control_from_docks);
ClassDB::bind_method(D_METHOD("remove_control_from_bottom_panel", "control"), &EditorPlugin::remove_control_from_bottom_panel);
ClassDB::bind_method(D_METHOD("remove_control_from_container", "container", "control"), &EditorPlugin::remove_control_from_container);
ClassDB::bind_method(D_METHOD("set_dock_tab_icon", "control", "icon"), &EditorPlugin::set_dock_tab_icon);
ClassDB::bind_method(D_METHOD("add_tool_menu_item", "name", "callable"), &EditorPlugin::add_tool_menu_item);
ClassDB::bind_method(D_METHOD("add_tool_submenu_item", "name", "submenu"), &EditorPlugin::add_tool_submenu_item);
ClassDB::bind_method(D_METHOD("remove_tool_menu_item", "name"), &EditorPlugin::remove_tool_menu_item);
ClassDB::bind_method(D_METHOD("get_export_as_menu"), &EditorPlugin::get_export_as_menu);
ClassDB::bind_method(D_METHOD("add_custom_type", "type", "base", "script", "icon"), &EditorPlugin::add_custom_type);
ClassDB::bind_method(D_METHOD("remove_custom_type", "type"), &EditorPlugin::remove_custom_type);
ClassDB::bind_method(D_METHOD("add_autoload_singleton", "name", "path"), &EditorPlugin::add_autoload_singleton);
ClassDB::bind_method(D_METHOD("remove_autoload_singleton", "name"), &EditorPlugin::remove_autoload_singleton);
ClassDB::bind_method(D_METHOD("update_overlays"), &EditorPlugin::update_overlays);
ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible);
ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel);
ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::get_undo_redo);
ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback);
ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback);
ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout);
ClassDB::bind_method(D_METHOD("add_translation_parser_plugin", "parser"), &EditorPlugin::add_translation_parser_plugin);
ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin);
ClassDB::bind_method(D_METHOD("add_import_plugin", "importer", "first_priority"), &EditorPlugin::add_import_plugin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin);
ClassDB::bind_method(D_METHOD("add_scene_format_importer_plugin", "scene_format_importer", "first_priority"), &EditorPlugin::add_scene_format_importer_plugin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::remove_scene_format_importer_plugin);
ClassDB::bind_method(D_METHOD("add_scene_post_import_plugin", "scene_import_plugin", "first_priority"), &EditorPlugin::add_scene_post_import_plugin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::remove_scene_post_import_plugin);
ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin);
ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin);
ClassDB::bind_method(D_METHOD("add_export_platform", "platform"), &EditorPlugin::add_export_platform);
ClassDB::bind_method(D_METHOD("remove_export_platform", "platform"), &EditorPlugin::remove_export_platform);
ClassDB::bind_method(D_METHOD("add_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::add_node_3d_gizmo_plugin);
ClassDB::bind_method(D_METHOD("remove_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::remove_node_3d_gizmo_plugin);
ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin);
ClassDB::bind_method(D_METHOD("remove_inspector_plugin", "plugin"), &EditorPlugin::remove_inspector_plugin);
ClassDB::bind_method(D_METHOD("add_resource_conversion_plugin", "plugin"), &EditorPlugin::add_resource_conversion_plugin);
ClassDB::bind_method(D_METHOD("remove_resource_conversion_plugin", "plugin"), &EditorPlugin::remove_resource_conversion_plugin);
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("add_context_menu_plugin", "slot", "plugin"), &EditorPlugin::add_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_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
ClassDB::bind_method(D_METHOD("add_debugger_plugin", "script"), &EditorPlugin::add_debugger_plugin);
ClassDB::bind_method(D_METHOD("remove_debugger_plugin", "script"), &EditorPlugin::remove_debugger_plugin);
ClassDB::bind_method(D_METHOD("get_plugin_version"), &EditorPlugin::get_plugin_version);
GDVIRTUAL_BIND(_forward_canvas_gui_input, "event");
GDVIRTUAL_BIND(_forward_canvas_draw_over_viewport, "viewport_control");
GDVIRTUAL_BIND(_forward_canvas_force_draw_over_viewport, "viewport_control");
GDVIRTUAL_BIND(_forward_3d_gui_input, "viewport_camera", "event");
GDVIRTUAL_BIND(_forward_3d_draw_over_viewport, "viewport_control");
GDVIRTUAL_BIND(_forward_3d_force_draw_over_viewport, "viewport_control");
GDVIRTUAL_BIND(_get_plugin_name);
GDVIRTUAL_BIND(_get_plugin_icon);
GDVIRTUAL_BIND(_has_main_screen);
GDVIRTUAL_BIND(_make_visible, "visible");
GDVIRTUAL_BIND(_edit, "object");
GDVIRTUAL_BIND(_handles, "object");
GDVIRTUAL_BIND(_get_state);
GDVIRTUAL_BIND(_set_state, "state");
GDVIRTUAL_BIND(_clear);
GDVIRTUAL_BIND(_get_unsaved_status, "for_scene");
GDVIRTUAL_BIND(_save_external_data);
GDVIRTUAL_BIND(_apply_changes);
GDVIRTUAL_BIND(_get_breakpoints);
GDVIRTUAL_BIND(_set_window_layout, "configuration");
GDVIRTUAL_BIND(_get_window_layout, "configuration");
GDVIRTUAL_BIND(_build);
GDVIRTUAL_BIND(_enable_plugin);
GDVIRTUAL_BIND(_disable_plugin);
ADD_SIGNAL(MethodInfo("scene_changed", PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "filepath")));
ADD_SIGNAL(MethodInfo("main_screen_changed", PropertyInfo(Variant::STRING, "screen_name")));
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "filepath")));
ADD_SIGNAL(MethodInfo("project_settings_changed"));
BIND_ENUM_CONSTANT(CONTAINER_TOOLBAR);
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_MENU);
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_SIDE_LEFT);
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT);
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_BOTTOM);
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_MENU);
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_SIDE_LEFT);
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_SIDE_RIGHT);
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_BOTTOM);
BIND_ENUM_CONSTANT(CONTAINER_INSPECTOR_BOTTOM);
BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_LEFT);
BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_RIGHT);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_UL);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_BL);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_UR);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_BR);
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_UL);
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_BL);
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_UR);
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_BR);
BIND_ENUM_CONSTANT(DOCK_SLOT_MAX);
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS);
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP);
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM);
}
EditorUndoRedoManager *EditorPlugin::get_undo_redo() {
return EditorUndoRedoManager::get_singleton();
}
EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS];
int EditorPlugins::creation_func_count = 0;

View File

@@ -0,0 +1,294 @@
/**************************************************************************/
/* editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/config_file.h"
#include "editor/inspector/editor_context_menu_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/gui/control.h"
class Node3D;
class Button;
class PopupMenu;
class EditorDebuggerPlugin;
class EditorExport;
class EditorExportPlugin;
class EditorExportPlatform;
class EditorImportPlugin;
class EditorInspectorPlugin;
class EditorInterface;
class EditorNode3DGizmoPlugin;
class EditorResourceConversionPlugin;
class EditorSceneFormatImporter;
class EditorScenePostImportPlugin;
class EditorToolAddons;
class EditorTranslationParserPlugin;
class EditorUndoRedoManager;
class ScriptCreateDialog;
class EditorPlugin : public Node {
GDCLASS(EditorPlugin, Node);
friend class EditorData;
bool input_event_forwarding_always_enabled = false;
bool force_draw_over_forwarding_enabled = false;
String last_main_screen_name;
String plugin_version;
#ifndef DISABLE_DEPRECATED
void _editor_project_settings_changed();
#endif
public:
enum CustomControlContainer {
CONTAINER_TOOLBAR,
CONTAINER_SPATIAL_EDITOR_MENU,
CONTAINER_SPATIAL_EDITOR_SIDE_LEFT,
CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT,
CONTAINER_SPATIAL_EDITOR_BOTTOM,
CONTAINER_CANVAS_EDITOR_MENU,
CONTAINER_CANVAS_EDITOR_SIDE_LEFT,
CONTAINER_CANVAS_EDITOR_SIDE_RIGHT,
CONTAINER_CANVAS_EDITOR_BOTTOM,
CONTAINER_INSPECTOR_BOTTOM,
CONTAINER_PROJECT_SETTING_TAB_LEFT,
CONTAINER_PROJECT_SETTING_TAB_RIGHT,
};
enum DockSlot {
DOCK_SLOT_LEFT_UL,
DOCK_SLOT_LEFT_BL,
DOCK_SLOT_LEFT_UR,
DOCK_SLOT_LEFT_BR,
DOCK_SLOT_RIGHT_UL,
DOCK_SLOT_RIGHT_BL,
DOCK_SLOT_RIGHT_UR,
DOCK_SLOT_RIGHT_BR,
DOCK_SLOT_MAX
};
enum AfterGUIInput {
AFTER_GUI_INPUT_PASS,
AFTER_GUI_INPUT_STOP,
AFTER_GUI_INPUT_CUSTOM,
};
protected:
void _notification(int p_what);
static void _bind_methods();
EditorUndoRedoManager *get_undo_redo();
void add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon);
void remove_custom_type(const String &p_type);
GDVIRTUAL1R(bool, _forward_canvas_gui_input, Ref<InputEvent>)
GDVIRTUAL1(_forward_canvas_draw_over_viewport, Control *)
GDVIRTUAL1(_forward_canvas_force_draw_over_viewport, Control *)
GDVIRTUAL2R(int, _forward_3d_gui_input, Camera3D *, Ref<InputEvent>)
GDVIRTUAL1(_forward_3d_draw_over_viewport, Control *)
GDVIRTUAL1(_forward_3d_force_draw_over_viewport, Control *)
GDVIRTUAL0RC(String, _get_plugin_name)
GDVIRTUAL0RC(Ref<Texture2D>, _get_plugin_icon)
GDVIRTUAL0RC(bool, _has_main_screen)
GDVIRTUAL1(_make_visible, bool)
GDVIRTUAL1(_edit, Object *)
GDVIRTUAL1RC(bool, _handles, Object *)
GDVIRTUAL0RC(Dictionary, _get_state)
GDVIRTUAL1(_set_state, Dictionary)
GDVIRTUAL0(_clear)
GDVIRTUAL1RC(String, _get_unsaved_status, String)
GDVIRTUAL0(_save_external_data)
GDVIRTUAL0(_apply_changes)
GDVIRTUAL0RC(Vector<String>, _get_breakpoints)
GDVIRTUAL1(_set_window_layout, Ref<ConfigFile>)
GDVIRTUAL1(_get_window_layout, Ref<ConfigFile>)
GDVIRTUAL0R(bool, _build)
GDVIRTUAL0(_enable_plugin)
GDVIRTUAL0(_disable_plugin)
#ifndef DISABLE_DEPRECATED
Button *_add_control_to_bottom_panel_compat_88081(Control *p_control, const String &p_title);
void _add_control_to_dock_compat_88081(DockSlot p_slot, Control *p_control);
static void _bind_compatibility_methods();
#endif
public:
//TODO: send a resource for editing to the editor node?
void add_control_to_container(CustomControlContainer p_location, Control *p_control);
void remove_control_from_container(CustomControlContainer p_location, Control *p_control);
Button *add_control_to_bottom_panel(Control *p_control, const String &p_title, const Ref<Shortcut> &p_shortcut = nullptr);
void add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut = nullptr);
void remove_control_from_docks(Control *p_control);
void remove_control_from_bottom_panel(Control *p_control);
void set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon);
void add_tool_menu_item(const String &p_name, const Callable &p_callable);
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
void remove_tool_menu_item(const String &p_name);
PopupMenu *get_export_as_menu();
void set_input_event_forwarding_always_enabled();
bool is_input_event_forwarding_always_enabled() { return input_event_forwarding_always_enabled; }
void set_force_draw_over_forwarding_enabled();
bool is_force_draw_over_forwarding_enabled() { return force_draw_over_forwarding_enabled; }
void notify_main_screen_changed(const String &screen_name);
void notify_scene_changed(const Node *scn_root);
void notify_scene_closed(const String &scene_filepath);
void notify_resource_saved(const Ref<Resource> &p_resource);
void notify_scene_saved(const String &p_scene_filepath);
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
virtual void forward_canvas_draw_over_viewport(Control *p_overlay);
virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay);
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
virtual void forward_3d_draw_over_viewport(Control *p_overlay);
virtual void forward_3d_force_draw_over_viewport(Control *p_overlay);
virtual String get_plugin_name() const;
virtual const Ref<Texture2D> get_plugin_icon() const;
virtual String get_plugin_version() const;
virtual void set_plugin_version(const String &p_version);
virtual bool has_main_screen() const;
virtual void make_visible(bool p_visible);
virtual void selected_notify() {} //notify that it was raised by the user, not the editor
virtual void edit(Object *p_object);
virtual bool handles(Object *p_object) const;
virtual bool can_auto_hide() const;
virtual Dictionary get_state() const; //save editor state so it can't be reloaded when reloading scene
virtual void set_state(const Dictionary &p_state); //restore editor state (likely was saved with the scene)
virtual void clear(); // clear any temporary data in the editor, reset it (likely new scene or load another scene)
virtual String get_unsaved_status(const String &p_for_scene = "") const;
virtual void save_external_data(); // if editor references external resources/scenes, save them
virtual void apply_changes(); // if changes are pending in editor, apply them
virtual void get_breakpoints(List<String> *p_breakpoints);
virtual bool get_remove_list(List<Node *> *p_list);
virtual void set_window_layout(Ref<ConfigFile> p_layout);
virtual void get_window_layout(Ref<ConfigFile> p_layout);
virtual void edited_scene_changed() {} // if changes are pending in editor, apply them
virtual bool build(); // builds with external tools. Returns true if safe to continue running scene.
EditorInterface *get_editor_interface();
ScriptCreateDialog *get_script_create_dialog();
void add_undo_redo_inspector_hook_callback(Callable p_callable);
void remove_undo_redo_inspector_hook_callback(Callable p_callable);
int update_overlays() const;
void queue_save_layout();
void make_bottom_panel_item_visible(Control *p_item);
void hide_bottom_panel();
void add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
void remove_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
void add_import_plugin(const Ref<EditorImportPlugin> &p_importer, bool p_first_priority = false);
void remove_import_plugin(const Ref<EditorImportPlugin> &p_importer);
void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
void remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
void add_export_platform(const Ref<EditorExportPlatform> &p_platform);
void remove_export_platform(const Ref<EditorExportPlatform> &p_platform);
void add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin);
void remove_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin);
void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
void add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority = false);
void remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer);
void add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer, bool p_first_priority = false);
void remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer);
void add_autoload_singleton(const String &p_name, const String &p_path);
void remove_autoload_singleton(const String &p_name);
void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
void add_context_menu_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
void remove_context_menu_plugin(const Ref<EditorContextMenuPlugin> &p_plugin);
void enable_plugin();
void disable_plugin();
};
VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer);
VARIANT_ENUM_CAST(EditorPlugin::DockSlot);
VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput);
typedef EditorPlugin *(*EditorPluginCreateFunc)();
class EditorPlugins {
enum {
MAX_CREATE_FUNCS = 128
};
static EditorPluginCreateFunc creation_funcs[MAX_CREATE_FUNCS];
static int creation_func_count;
template <typename T>
static EditorPlugin *creator() {
return memnew(T);
}
public:
static int get_plugin_count() { return creation_func_count; }
static EditorPlugin *create(int p_idx) {
ERR_FAIL_INDEX_V(p_idx, creation_func_count, nullptr);
return creation_funcs[p_idx]();
}
template <typename T>
static void add_by_type() {
add_create_func(creator<T>);
}
static void add_create_func(EditorPluginCreateFunc p_func) {
ERR_FAIL_COND(creation_func_count >= MAX_CREATE_FUNCS);
creation_funcs[creation_func_count++] = p_func;
}
};

View File

@@ -0,0 +1,290 @@
/**************************************************************************/
/* editor_plugin_settings.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "editor_plugin_settings.h"
#include "core/config/project_settings.h"
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/separator.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
void EditorPluginSettings::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
update_plugins();
} break;
case NOTIFICATION_READY: {
plugin_config_dialog->connect("plugin_ready", callable_mp(EditorNode::get_singleton(), &EditorNode::_on_plugin_ready));
plugin_list->connect("button_clicked", callable_mp(this, &EditorPluginSettings::_cell_button_pressed));
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
if (plugin_list->get_root()) {
update_plugins();
}
} break;
case NOTIFICATION_THEME_CHANGED: {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
recovery_mode_icon->set_texture(get_editor_theme_icon(SNAME("NodeWarning")));
}
} break;
}
}
void EditorPluginSettings::update_plugins() {
plugin_list->clear();
updating = true;
TreeItem *root = plugin_list->create_item();
Vector<String> plugins = _get_plugins("res://addons");
plugins.sort();
for (int i = 0; i < plugins.size(); i++) {
Ref<ConfigFile> cfg;
cfg.instantiate();
const String &path = plugins[i];
Error err = cfg->load(path);
if (err != OK) {
WARN_PRINT("Can't load plugin config at: " + path);
} else {
Vector<String> missing_keys;
for (const String required_key : { "name", "author", "version", "description", "script" }) {
if (!cfg->has_section_key("plugin", required_key)) {
missing_keys.append("\"plugin/" + required_key + "\"");
}
}
if (!missing_keys.is_empty()) {
WARN_PRINT(vformat("Plugin config at \"%s\" is missing the following keys: %s", path, String(",").join(missing_keys)));
} else {
String name = cfg->get_value("plugin", "name");
String author = cfg->get_value("plugin", "author");
String version = cfg->get_value("plugin", "version");
String description = cfg->get_value("plugin", "description");
String scr = cfg->get_value("plugin", "script");
bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(path);
Color disabled_color = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));
const PackedInt32Array boundaries = TS->string_get_word_breaks(description, "", 80);
String wrapped_description;
for (int j = 0; j < boundaries.size(); j += 2) {
const int start = boundaries[j];
const int end = boundaries[j + 1];
wrapped_description += "\n" + description.substr(start, end - start + 1).rstrip("\n");
}
TreeItem *item = plugin_list->create_item(root);
item->set_text(COLUMN_NAME, name);
if (!is_enabled) {
item->set_custom_color(COLUMN_NAME, disabled_color);
}
item->set_tooltip_text(COLUMN_NAME, vformat(TTR("Name: %s\nPath: %s\nMain Script: %s\n\n%s"), name, path, scr, wrapped_description));
item->set_metadata(COLUMN_NAME, path);
item->set_text(COLUMN_VERSION, version);
item->set_custom_font(COLUMN_VERSION, get_theme_font("source", EditorStringName(EditorFonts)));
item->set_metadata(COLUMN_VERSION, scr);
item->set_text(COLUMN_AUTHOR, author);
item->set_metadata(COLUMN_AUTHOR, description);
item->set_cell_mode(COLUMN_STATUS, TreeItem::CELL_MODE_CHECK);
item->set_text(COLUMN_STATUS, TTRC("On"));
item->set_checked(COLUMN_STATUS, is_enabled);
item->set_editable(COLUMN_STATUS, true);
item->add_button(COLUMN_EDIT, get_editor_theme_icon(SNAME("Edit")), BUTTON_PLUGIN_EDIT, false, TTRC("Edit Plugin"));
}
}
}
updating = false;
}
void EditorPluginSettings::_plugin_activity_changed() {
if (updating) {
return;
}
TreeItem *ti = plugin_list->get_edited();
ERR_FAIL_NULL(ti);
bool checked = ti->is_checked(COLUMN_STATUS);
String name = ti->get_metadata(COLUMN_NAME);
EditorNode::get_singleton()->set_addon_plugin_enabled(name, checked, true);
bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(name);
if (is_enabled != checked) {
updating = true;
ti->set_checked(COLUMN_STATUS, is_enabled);
updating = false;
}
if (is_enabled) {
ti->clear_custom_color(COLUMN_NAME);
} else {
ti->set_custom_color(COLUMN_NAME, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
}
}
void EditorPluginSettings::_create_clicked() {
plugin_config_dialog->config("");
plugin_config_dialog->popup_centered();
}
void EditorPluginSettings::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
if (p_button != MouseButton::LEFT) {
return;
}
TreeItem *item = Object::cast_to<TreeItem>(p_item);
if (!item) {
return;
}
if (p_id == BUTTON_PLUGIN_EDIT) {
if (p_column == COLUMN_EDIT) {
String dir = item->get_metadata(COLUMN_NAME);
plugin_config_dialog->config(dir);
plugin_config_dialog->popup_centered();
}
}
}
Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
Error err = da->change_dir(p_dir);
if (err != OK) {
return Vector<String>();
}
Vector<String> plugins;
da->list_dir_begin();
for (String path = da->get_next(); !path.is_empty(); path = da->get_next()) {
if (path[0] == '.' || !da->current_is_dir()) {
continue;
}
const String full_path = p_dir.path_join(path);
const String plugin_config = full_path.path_join("plugin.cfg");
if (FileAccess::exists(plugin_config)) {
plugins.push_back(plugin_config);
} else {
plugins.append_array(_get_plugins(full_path));
}
}
da->list_dir_end();
return plugins;
}
EditorPluginSettings::EditorPluginSettings() {
ProjectSettings::get_singleton()->add_hidden_prefix("editor_plugins/");
plugin_config_dialog = memnew(PluginConfigDialog);
plugin_config_dialog->config("");
add_child(plugin_config_dialog);
if (Engine::get_singleton()->is_recovery_mode_hint()) {
HBoxContainer *c = memnew(HBoxContainer);
add_child(c);
recovery_mode_icon = memnew(TextureRect);
recovery_mode_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
c->add_child(recovery_mode_icon);
Label *recovery_mode_label = memnew(Label(TTRC("Recovery mode is enabled. Enabled plugins will not run while this mode is active.")));
recovery_mode_label->set_theme_type_variation("HeaderSmall");
recovery_mode_label->set_h_size_flags(SIZE_EXPAND_FILL);
c->add_child(recovery_mode_label);
HSeparator *sep = memnew(HSeparator);
add_child(sep);
}
HBoxContainer *title_hb = memnew(HBoxContainer);
Label *label = memnew(Label(TTRC("Installed Plugins:")));
label->set_theme_type_variation("HeaderSmall");
title_hb->add_child(label);
title_hb->add_spacer();
Button *create_plugin_button = memnew(Button(TTRC("Create New Plugin")));
create_plugin_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPluginSettings::_create_clicked));
title_hb->add_child(create_plugin_button);
add_child(title_hb);
plugin_list = memnew(Tree);
plugin_list->set_v_size_flags(SIZE_EXPAND_FILL);
plugin_list->set_columns(COLUMN_MAX);
plugin_list->set_column_titles_visible(true);
plugin_list->set_column_title(COLUMN_STATUS, TTRC("Enabled"));
plugin_list->set_column_title(COLUMN_NAME, TTRC("Name"));
plugin_list->set_column_title(COLUMN_VERSION, TTRC("Version"));
plugin_list->set_column_title(COLUMN_AUTHOR, TTRC("Author"));
plugin_list->set_column_title(COLUMN_EDIT, TTRC("Edit"));
plugin_list->set_column_title_alignment(COLUMN_STATUS, HORIZONTAL_ALIGNMENT_LEFT);
plugin_list->set_column_title_alignment(COLUMN_NAME, HORIZONTAL_ALIGNMENT_LEFT);
plugin_list->set_column_title_alignment(COLUMN_VERSION, HORIZONTAL_ALIGNMENT_LEFT);
plugin_list->set_column_title_alignment(COLUMN_AUTHOR, HORIZONTAL_ALIGNMENT_LEFT);
plugin_list->set_column_title_alignment(COLUMN_EDIT, HORIZONTAL_ALIGNMENT_LEFT);
plugin_list->set_column_expand(COLUMN_PADDING_LEFT, false);
plugin_list->set_column_expand(COLUMN_STATUS, false);
plugin_list->set_column_expand(COLUMN_NAME, true);
plugin_list->set_column_expand(COLUMN_VERSION, false);
plugin_list->set_column_expand(COLUMN_AUTHOR, false);
plugin_list->set_column_expand(COLUMN_EDIT, false);
plugin_list->set_column_expand(COLUMN_PADDING_RIGHT, false);
plugin_list->set_column_clip_content(COLUMN_STATUS, true);
plugin_list->set_column_clip_content(COLUMN_NAME, true);
plugin_list->set_column_clip_content(COLUMN_VERSION, true);
plugin_list->set_column_clip_content(COLUMN_AUTHOR, true);
plugin_list->set_column_clip_content(COLUMN_EDIT, true);
plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_LEFT, 10 * EDSCALE);
plugin_list->set_column_custom_minimum_width(COLUMN_STATUS, 80 * EDSCALE);
plugin_list->set_column_custom_minimum_width(COLUMN_VERSION, 100 * EDSCALE);
plugin_list->set_column_custom_minimum_width(COLUMN_AUTHOR, 250 * EDSCALE);
plugin_list->set_column_custom_minimum_width(COLUMN_EDIT, 40 * EDSCALE);
plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_RIGHT, 10 * EDSCALE);
plugin_list->set_hide_root(true);
plugin_list->connect("item_edited", callable_mp(this, &EditorPluginSettings::_plugin_activity_changed), CONNECT_DEFERRED);
VBoxContainer *mc = memnew(VBoxContainer);
mc->add_child(plugin_list);
mc->set_v_size_flags(SIZE_EXPAND_FILL);
mc->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(mc);
}

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* editor_plugin_settings.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/plugin_config_dialog.h"
class TextureRect;
class Tree;
class EditorPluginSettings : public VBoxContainer {
GDCLASS(EditorPluginSettings, VBoxContainer);
enum {
BUTTON_PLUGIN_EDIT
};
enum {
COLUMN_PADDING_LEFT,
COLUMN_STATUS,
COLUMN_NAME,
COLUMN_VERSION,
COLUMN_AUTHOR,
COLUMN_EDIT,
COLUMN_PADDING_RIGHT,
COLUMN_MAX,
};
PluginConfigDialog *plugin_config_dialog = nullptr;
TextureRect *recovery_mode_icon = nullptr;
Tree *plugin_list = nullptr;
bool updating = false;
void _plugin_activity_changed();
void _create_clicked();
void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
static Vector<String> _get_plugins(const String &p_dir);
protected:
void _notification(int p_what);
public:
void update_plugins();
EditorPluginSettings();
};

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* editor_resource_conversion_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "editor_resource_conversion_plugin.h"
void EditorResourceConversionPlugin::_bind_methods() {
GDVIRTUAL_BIND(_converts_to);
GDVIRTUAL_BIND(_handles, "resource");
GDVIRTUAL_BIND(_convert, "resource");
}
String EditorResourceConversionPlugin::converts_to() const {
String ret;
GDVIRTUAL_CALL(_converts_to, ret);
return ret;
}
bool EditorResourceConversionPlugin::handles(const Ref<Resource> &p_resource) const {
bool ret = false;
GDVIRTUAL_CALL(_handles, p_resource, ret);
return ret;
}
Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) const {
Ref<Resource> ret;
GDVIRTUAL_CALL(_convert, p_resource, ret);
return ret;
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* editor_resource_conversion_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/io/resource.h"
#include "core/object/gdvirtual.gen.inc"
class EditorResourceConversionPlugin : public RefCounted {
GDCLASS(EditorResourceConversionPlugin, RefCounted);
protected:
static void _bind_methods();
GDVIRTUAL0RC(String, _converts_to)
GDVIRTUAL1RC(bool, _handles, Ref<Resource>)
GDVIRTUAL1RC(Ref<Resource>, _convert, Ref<Resource>)
public:
virtual String converts_to() const;
virtual bool handles(const Ref<Resource> &p_resource) const;
virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const;
};

View File

@@ -0,0 +1,350 @@
/**************************************************************************/
/* plugin_config_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "plugin_config_dialog.h"
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/object/script_language.h"
#include "editor/editor_node.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/gui/editor_validation_panel.h"
#include "editor/plugins/editor_plugin.h"
#include "editor/settings/project_settings_editor.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/grid_container.h"
void PluginConfigDialog::_clear_fields() {
name_edit->set_text("");
subfolder_edit->set_text("");
desc_edit->set_text("");
author_edit->set_text("");
version_edit->set_text("");
script_edit->set_text("");
}
void PluginConfigDialog::_on_confirmed() {
String path = "res://addons/" + _get_subfolder();
if (!_edit_mode) {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (d.is_null() || d->make_dir_recursive(path) != OK) {
return;
}
}
// Create the plugin.cfg file.
Ref<ConfigFile> cf = memnew(ConfigFile);
cf->load(path.path_join("plugin.cfg"));
cf->set_value("plugin", "name", name_edit->get_text());
cf->set_value("plugin", "description", desc_edit->get_text());
cf->set_value("plugin", "author", author_edit->get_text());
cf->set_value("plugin", "version", version_edit->get_text());
// Language-specific settings.
int lang_index = script_option_edit->get_selected();
_create_script_for_plugin(path, cf, lang_index);
// Save and inform the editor.
cf->save(path.path_join("plugin.cfg"));
EditorNode::get_singleton()->get_project_settings()->update_plugins();
EditorFileSystem::get_singleton()->scan();
_clear_fields();
}
void PluginConfigDialog::_create_script_for_plugin(const String &p_plugin_path, Ref<ConfigFile> p_config_file, int p_script_lang_index) {
ScriptLanguage *language = ScriptServer::get_language(p_script_lang_index);
ERR_FAIL_COND(language == nullptr);
String ext = language->get_extension();
String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
if (script_name.get_extension() != ext) {
script_name += "." + ext;
}
String script_path = p_plugin_path.path_join(script_name);
p_config_file->set_value("plugin", "script", script_name);
// If the requested script does not exist, create it.
if (!_edit_mode && !FileAccess::exists(script_path)) {
String class_name = script_name.get_basename();
String template_content = "";
Vector<ScriptLanguage::ScriptTemplate> templates = language->get_built_in_templates("EditorPlugin");
if (!templates.is_empty()) {
template_content = templates[0].content;
}
Ref<Script> scr = language->make_template(template_content, class_name, "EditorPlugin");
scr->set_path(script_path, true);
ResourceSaver::save(scr);
p_config_file->save(p_plugin_path.path_join("plugin.cfg"));
emit_signal(SNAME("plugin_ready"), scr.ptr(), active_edit->is_pressed() ? _to_absolute_plugin_path(_get_subfolder()) : "");
}
}
void PluginConfigDialog::_on_canceled() {
_clear_fields();
}
void PluginConfigDialog::_on_required_text_changed() {
if (name_edit->get_text().is_empty()) {
validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR);
}
if (subfolder_edit->is_visible()) {
if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder name is not a valid folder name."), EditorValidationPanel::MSG_ERROR);
} else {
String path = "res://addons/" + _get_subfolder();
if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode.
validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder cannot be one which already exists."), EditorValidationPanel::MSG_ERROR);
}
}
} else {
validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK);
}
// Language and script validation.
int lang_idx = script_option_edit->get_selected();
ScriptLanguage *language = ScriptServer::get_language(lang_idx);
if (language == nullptr) {
return;
}
String ext = language->get_extension();
if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
}
if (active_edit->is_visible()) {
if (language->get_name() == "C#") {
active_edit->set_pressed(false);
active_edit->set_disabled(true);
validation_panel->set_message(MSG_ID_ACTIVE, TTR("C# doesn't support activating the plugin on creation because the project must be built first."), EditorValidationPanel::MSG_WARNING);
} else {
active_edit->set_disabled(false);
}
}
}
String PluginConfigDialog::_get_subfolder() {
return subfolder_edit->get_text().is_empty() ? name_edit->get_text().replace_char(' ', '_').to_lower() : subfolder_edit->get_text();
}
String PluginConfigDialog::_to_absolute_plugin_path(const String &p_plugin_name) {
return "res://addons/" + p_plugin_name + "/plugin.cfg";
}
void PluginConfigDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
name_edit->grab_focus();
}
} break;
case NOTIFICATION_READY: {
connect(SceneStringName(confirmed), callable_mp(this, &PluginConfigDialog::_on_confirmed));
get_cancel_button()->connect(SceneStringName(pressed), callable_mp(this, &PluginConfigDialog::_on_canceled));
} break;
}
}
void PluginConfigDialog::config(const String &p_config_path) {
if (!p_config_path.is_empty()) {
Ref<ConfigFile> cf = memnew(ConfigFile);
Error err = cf->load(p_config_path);
ERR_FAIL_COND_MSG(err != OK, "Cannot load config file from path '" + p_config_path + "'.");
name_edit->set_text(cf->get_value("plugin", "name", ""));
subfolder_edit->set_text(p_config_path.get_base_dir().get_file());
desc_edit->set_text(cf->get_value("plugin", "description", ""));
author_edit->set_text(cf->get_value("plugin", "author", ""));
version_edit->set_text(cf->get_value("plugin", "version", ""));
script_edit->set_text(cf->get_value("plugin", "script", ""));
_edit_mode = true;
set_title(TTR("Edit a Plugin"));
} else {
_clear_fields();
_edit_mode = false;
set_title(TTR("Create a Plugin"));
}
for (Control *control : plugin_edit_hidden_controls) {
control->set_visible(!_edit_mode);
}
validation_panel->update();
get_ok_button()->set_disabled(!_edit_mode);
set_ok_button_text(_edit_mode ? TTR("Update") : TTR("Create"));
}
void PluginConfigDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("plugin_ready", PropertyInfo(Variant::STRING, "script_path", PROPERTY_HINT_NONE, ""), PropertyInfo(Variant::STRING, "activate_name")));
}
PluginConfigDialog::PluginConfigDialog() {
get_ok_button()->set_disabled(true);
set_hide_on_ok(true);
VBoxContainer *vbox = memnew(VBoxContainer);
vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
add_child(vbox);
GridContainer *grid = memnew(GridContainer);
grid->set_columns(2);
grid->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbox->add_child(grid);
// Plugin Name
Label *name_lb = memnew(Label);
name_lb->set_text(TTR("Plugin Name:"));
name_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(name_lb);
name_edit = memnew(LineEdit);
name_edit->set_placeholder("MyPlugin");
name_edit->set_tooltip_text(TTR("Required. This name will be displayed in the list of plugins."));
name_edit->set_accessibility_name(TTRC("Plugin Name:"));
name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(name_edit);
// Subfolder
Label *subfolder_lb = memnew(Label);
subfolder_lb->set_text(TTR("Subfolder:"));
subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(subfolder_lb);
plugin_edit_hidden_controls.push_back(subfolder_lb);
subfolder_edit = memnew(LineEdit);
subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin");
subfolder_edit->set_tooltip_text(TTR("Optional. The folder name should generally use `snake_case` naming (avoid spaces and special characters).\nIf left empty, the folder will be named after the plugin name converted to `snake_case`."));
subfolder_edit->set_accessibility_name(TTRC("Subfolder:"));
subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(subfolder_edit);
plugin_edit_hidden_controls.push_back(subfolder_edit);
// Description
Label *desc_lb = memnew(Label);
desc_lb->set_text(TTR("Description:"));
desc_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(desc_lb);
desc_edit = memnew(TextEdit);
desc_edit->set_tooltip_text(TTR("Optional. This description should be kept relatively short (up to 5 lines).\nIt will display when hovering the plugin in the list of plugins."));
desc_edit->set_accessibility_name(TTRC("Description:"));
desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
desc_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
desc_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(desc_edit);
// Author
Label *author_lb = memnew(Label);
author_lb->set_text(TTR("Author:"));
author_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(author_lb);
author_edit = memnew(LineEdit);
author_edit->set_placeholder("Godette");
author_edit->set_accessibility_name(TTRC("Author:"));
author_edit->set_tooltip_text(TTR("Optional. The author's username, full name, or organization name."));
author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(author_edit);
// Version
Label *version_lb = memnew(Label);
version_lb->set_text(TTR("Version:"));
version_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(version_lb);
version_edit = memnew(LineEdit);
version_edit->set_tooltip_text(TTR("Optional. A human-readable version identifier used for informational purposes only."));
version_edit->set_placeholder("1.0");
version_edit->set_accessibility_name(TTRC("Version:"));
version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(version_edit);
// Language dropdown
Label *script_option_lb = memnew(Label);
script_option_lb->set_text(TTR("Language:"));
script_option_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_option_lb);
script_option_edit = memnew(OptionButton);
script_option_edit->set_tooltip_text(TTR("Required. The scripting language to use for the script.\nNote that a plugin may use several languages at once by adding more scripts to the plugin."));
script_option_edit->set_accessibility_name(TTRC("Language:"));
int default_lang = 0;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *lang = ScriptServer::get_language(i);
script_option_edit->add_item(lang->get_name());
if (lang->get_name() == "GDScript") {
default_lang = i;
}
}
script_option_edit->select(default_lang);
grid->add_child(script_option_edit);
// Plugin Script Name
Label *script_name_label = memnew(Label);
script_name_label->set_text(TTR("Script Name:"));
script_name_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_name_label);
script_edit = memnew(LineEdit);
script_edit->set_tooltip_text(TTR("Optional. The name of the script file. If left empty, will default to the subfolder name."));
script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd");
script_edit->set_accessibility_name(TTRC("Script Name:"));
script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(script_edit);
// Activate now checkbox
Label *active_label = memnew(Label);
active_label->set_text(TTR("Activate now?"));
active_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(active_label);
plugin_edit_hidden_controls.push_back(active_label);
active_edit = memnew(CheckBox);
active_edit->set_pressed(true);
active_edit->set_accessibility_name(TTRC("Activate now?"));
grid->add_child(active_edit);
plugin_edit_hidden_controls.push_back(active_edit);
Control *spacing = memnew(Control);
vbox->add_child(spacing);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
validation_panel = memnew(EditorValidationPanel);
vbox->add_child(validation_panel);
validation_panel->add_line(MSG_ID_PLUGIN, TTR("Plugin name is valid."));
validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script extension is valid."));
validation_panel->add_line(MSG_ID_SUBFOLDER, TTR("Subfolder name is valid."));
validation_panel->add_line(MSG_ID_ACTIVE, "");
validation_panel->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
validation_panel->set_accept_button(get_ok_button());
script_option_edit->connect(SceneStringName(item_selected), callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
name_edit->connect(SceneStringName(text_changed), callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
subfolder_edit->connect(SceneStringName(text_changed), callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
script_edit->connect(SceneStringName(text_changed), callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* plugin_config_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "scene/gui/check_box.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
#include "scene/gui/text_edit.h"
class ConfigFile;
class EditorValidationPanel;
class PluginConfigDialog : public ConfirmationDialog {
GDCLASS(PluginConfigDialog, ConfirmationDialog);
enum {
MSG_ID_PLUGIN,
MSG_ID_SUBFOLDER,
MSG_ID_SCRIPT,
MSG_ID_ACTIVE,
};
LineEdit *name_edit = nullptr;
LineEdit *subfolder_edit = nullptr;
TextEdit *desc_edit = nullptr;
LineEdit *author_edit = nullptr;
LineEdit *version_edit = nullptr;
OptionButton *script_option_edit = nullptr;
LineEdit *script_edit = nullptr;
CheckBox *active_edit = nullptr;
LocalVector<Control *> plugin_edit_hidden_controls;
EditorValidationPanel *validation_panel = nullptr;
bool _edit_mode = false;
void _clear_fields();
void _on_confirmed();
void _on_canceled();
void _on_required_text_changed();
void _create_script_for_plugin(const String &p_plugin_path, Ref<ConfigFile> p_config_file, int p_script_lang_index);
String _get_subfolder();
static String _to_absolute_plugin_path(const String &p_plugin_name);
protected:
virtual void _notification(int p_what);
static void _bind_methods();
public:
void config(const String &p_config_path);
PluginConfigDialog();
};