From a6de7a8a3f127dcfb4c603adb6cade149c370791 Mon Sep 17 00:00:00 2001 From: kobewi Date: Sun, 10 Sep 2023 21:29:28 +0200 Subject: [PATCH] Allow contextual plugins to persist temporarily --- editor/editor_inspector.cpp | 9 +++ editor/editor_inspector.h | 2 + editor/editor_node.cpp | 40 +++++++++- editor/editor_node.h | 1 + editor/editor_plugin.cpp | 4 + editor/editor_plugin.h | 1 + editor/plugins/theme_editor_plugin.cpp | 102 ++++++++++++++++++++++--- editor/plugins/theme_editor_plugin.h | 10 ++- 8 files changed, 151 insertions(+), 18 deletions(-) diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 395f4faa39b0..233fcd10ce8c 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3391,11 +3391,16 @@ Object *EditorInspector::get_edited_object() { return object; } +Object *EditorInspector::get_next_edited_object() { + return next_object; +} + void EditorInspector::edit(Object *p_object) { if (object == p_object) { return; } + next_object = p_object; // Some plugins need to know the next edited object when clearing the inspector. if (object) { _clear(); object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); @@ -3412,6 +3417,10 @@ void EditorInspector::edit(Object *p_object) { object->connect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); update_tree(); } + + // Keep it available until the end so it works with both main and sub inspectors. + next_object = nullptr; + emit_signal(SNAME("edited_object_changed")); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index b5f0cec80bcc..4defd760dc53 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -473,6 +473,7 @@ class EditorInspector : public ScrollContainer { void _clear(bool p_hide_plugins = true); Object *object = nullptr; + Object *next_object = nullptr; // @@ -576,6 +577,7 @@ class EditorInspector : public ScrollContainer { void update_property(const String &p_prop); void edit(Object *p_object); Object *get_edited_object(); + Object *get_next_edited_object(); void set_keying(bool p_active); void set_read_only(bool p_read_only); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0037b356d05f..8d350638a1f5 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -851,6 +851,10 @@ void EditorNode::_plugin_over_edit(EditorPlugin *p_plugin, Object *p_object) { } } +void EditorNode::_plugin_over_self_own(EditorPlugin *p_plugin) { + active_plugins[p_plugin->get_instance_id()].insert(p_plugin); +} + void EditorNode::_resources_changed(const Vector &p_resources) { List> changed; @@ -2135,7 +2139,12 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { for (EditorPlugin *plugin : active_plugins[owner_id]) { if (!available_plugins.has(plugin)) { to_remove.push_back(plugin); - _plugin_over_edit(plugin, nullptr); + if (plugin->can_auto_hide()) { + _plugin_over_edit(plugin, nullptr); + } else { + // If plugin can't be hidden, make it own itself and become responsible for closing. + _plugin_over_self_own(plugin); + } } } @@ -2151,6 +2160,12 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { continue; } + if (active_plugins.has(plugin->get_instance_id())) { + // Plugin is already active, but as self-owning, so it needs a separate check. + plugin->edit(p_object); + continue; + } + // If plugin is already associated with another owner, remove it from there first. for (KeyValue> &kv : active_plugins) { if (kv.key != owner_id) { @@ -2214,7 +2229,11 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { if (p_editing_owner) { const ObjectID id = p_editing_owner->get_instance_id(); for (EditorPlugin *plugin : active_plugins[id]) { - _plugin_over_edit(plugin, nullptr); + if (plugin->can_auto_hide()) { + _plugin_over_edit(plugin, nullptr); + } else { + _plugin_over_self_own(plugin); + } } active_plugins.erase(id); } else { @@ -2222,10 +2241,23 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { // This is to sweep properties that were removed from the inspector. List to_remove; for (KeyValue> &kv : active_plugins) { - if (!ObjectDB::get_instance(kv.key)) { + const Object *context = ObjectDB::get_instance(kv.key); + if (context) { + // In case of self-owning plugins, they are disabled here if they can auto hide. + const EditorPlugin *self_owning = Object::cast_to(context); + if (self_owning && self_owning->can_auto_hide()) { + context = nullptr; + } + } + + if (!context) { to_remove.push_back(kv.key); for (EditorPlugin *plugin : kv.value) { - _plugin_over_edit(plugin, nullptr); + if (plugin->can_auto_hide()) { + _plugin_over_edit(plugin, nullptr); + } else { + _plugin_over_self_own(plugin); + } } } } diff --git a/editor/editor_node.h b/editor/editor_node.h index 72134e283b0d..4e36c19e96a6 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -550,6 +550,7 @@ class EditorNode : public Node { void _remove_plugin_from_enabled(const String &p_name); void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object); + void _plugin_over_self_own(EditorPlugin *p_plugin); void _fs_changed(); void _resources_reimported(const Vector &p_resources); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 2726a781b425..b43e7eba9c27 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -335,6 +335,10 @@ bool EditorPlugin::handles(Object *p_object) const { return success; } +bool EditorPlugin::can_auto_hide() const { + return true; +} + Dictionary EditorPlugin::get_state() const { Dictionary state; GDVIRTUAL_CALL(_get_state, state); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 62ed432ecc6f..8870ef425ed8 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -178,6 +178,7 @@ class EditorPlugin : public Node { 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) diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 821d8151a4a5..e5244019932c 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/inspector_dock.h" #include "editor/progress_dialog.h" #include "scene/gui/check_button.h" #include "scene/gui/color_picker.h" @@ -3528,6 +3529,16 @@ void ThemeEditor::_theme_edit_button_cbk() { theme_edit_dialog->popup_centered(Size2(850, 700) * EDSCALE); } +void ThemeEditor::_theme_close_button_cbk() { + plugin->make_visible(false); // Enables auto hide. + if (theme.is_valid() && InspectorDock::get_inspector_singleton()->get_edited_object() == theme.ptr()) { + EditorNode::get_singleton()->push_item(nullptr); + } else { + theme = Ref(); + EditorNode::get_singleton()->hide_unused_editors(plugin); + } +} + void ThemeEditor::_add_preview_button_cbk() { preview_scene_dialog->popup_file_dialog(); } @@ -3645,6 +3656,12 @@ ThemeEditor::ThemeEditor() { theme_save_as_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk).bind(true)); top_menu->add_child(theme_save_as_button); + Button *theme_close_button = memnew(Button); + theme_close_button->set_text(TTR("Close")); + theme_close_button->set_flat(true); + theme_close_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_close_button_cbk)); + top_menu->add_child(theme_close_button); + top_menu->add_child(memnew(VSeparator)); Button *theme_edit_button = memnew(Button); @@ -3711,20 +3728,12 @@ ThemeEditor::ThemeEditor() { /////////////////////// -void ThemeEditorPlugin::edit(Object *p_node) { - if (Object::cast_to(p_node)) { - theme_editor->edit(Object::cast_to(p_node)); - } else { - // We intentionally keep a reference to the last used theme to work around - // the the editor being hidden while base resources are edited. Uncomment - // the following line again and remove this comment once that bug has been - // fixed (scheduled for Godot 4.1 in PR 73098): - // theme_editor->edit(Ref()); - } +void ThemeEditorPlugin::edit(Object *p_object) { + theme_editor->edit(Ref(p_object)); } -bool ThemeEditorPlugin::handles(Object *p_node) const { - return Object::cast_to(p_node) != nullptr; +bool ThemeEditorPlugin::handles(Object *p_object) const { + return Object::cast_to(p_object) != nullptr; } void ThemeEditorPlugin::make_visible(bool p_visible) { @@ -3740,8 +3749,77 @@ void ThemeEditorPlugin::make_visible(bool p_visible) { } } +bool ThemeEditorPlugin::can_auto_hide() const { + Ref edited_theme = theme_editor->theme; + if (edited_theme.is_null()) { + return true; + } + + Ref edited_resource = Ref(InspectorDock::get_inspector_singleton()->get_next_edited_object()); + if (edited_resource.is_null()) { + return true; + } + + // Don't hide if edited resource used by this theme. + Ref sbox = edited_resource; + if (sbox.is_valid()) { + List type_list; + edited_theme->get_stylebox_type_list(&type_list); + + for (const StringName &E : type_list) { + List list; + edited_theme->get_stylebox_list(E, &list); + + for (const StringName &F : list) { + if (edited_theme->get_stylebox(F, E) == sbox) { + return false; + } + } + } + return true; + } + + Ref tex = edited_resource; + if (tex.is_valid()) { + List type_list; + edited_theme->get_icon_type_list(&type_list); + + for (const StringName &E : type_list) { + List list; + edited_theme->get_icon_list(E, &list); + + for (const StringName &F : list) { + if (edited_theme->get_icon(F, E) == tex) { + return false; + } + } + } + return true; + } + + Ref fnt = edited_resource; + if (fnt.is_valid()) { + List type_list; + edited_theme->get_font_type_list(&type_list); + + for (const StringName &E : type_list) { + List list; + edited_theme->get_font_list(E, &list); + + for (const StringName &F : list) { + if (edited_theme->get_font(F, E) == fnt) { + return false; + } + } + } + return true; + } + return true; +} + ThemeEditorPlugin::ThemeEditorPlugin() { theme_editor = memnew(ThemeEditor); + theme_editor->plugin = this; theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Theme"), theme_editor); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 077ce8e8f75a..311cab2b0ca9 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -47,6 +47,7 @@ class OptionButton; class PanelContainer; class TabBar; class TabContainer; +class ThemeEditorPlugin; class TextureRect; class ThemeItemImportTree : public VBoxContainer { @@ -419,6 +420,9 @@ class ThemeTypeEditor : public MarginContainer { class ThemeEditor : public VBoxContainer { GDCLASS(ThemeEditor, VBoxContainer); + friend class ThemeEditorPlugin; + ThemeEditorPlugin *plugin = nullptr; + Ref theme; TabBar *preview_tabs = nullptr; @@ -433,6 +437,7 @@ class ThemeEditor : public VBoxContainer { void _theme_save_button_cbk(bool p_save_as); void _theme_edit_button_cbk(); + void _theme_close_button_cbk(); void _add_preview_button_cbk(); void _preview_scene_dialog_cbk(const String &p_path); @@ -462,9 +467,10 @@ class ThemeEditorPlugin : public EditorPlugin { public: virtual String get_name() const override { return "Theme"; } bool has_main_screen() const override { return false; } - virtual void edit(Object *p_node) override; - virtual bool handles(Object *p_node) const override; + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; virtual void make_visible(bool p_visible) override; + virtual bool can_auto_hide() const override; ThemeEditorPlugin(); };