Allow contextual plugins to persist temporarily
This commit is contained in:
parent
fba341ce44
commit
a6de7a8a3f
|
@ -3391,11 +3391,16 @@ Object *EditorInspector::get_edited_object() {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object *EditorInspector::get_next_edited_object() {
|
||||||
|
return next_object;
|
||||||
|
}
|
||||||
|
|
||||||
void EditorInspector::edit(Object *p_object) {
|
void EditorInspector::edit(Object *p_object) {
|
||||||
if (object == p_object) {
|
if (object == p_object) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next_object = p_object; // Some plugins need to know the next edited object when clearing the inspector.
|
||||||
if (object) {
|
if (object) {
|
||||||
_clear();
|
_clear();
|
||||||
object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback));
|
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));
|
object->connect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback));
|
||||||
update_tree();
|
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"));
|
emit_signal(SNAME("edited_object_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -473,6 +473,7 @@ class EditorInspector : public ScrollContainer {
|
||||||
|
|
||||||
void _clear(bool p_hide_plugins = true);
|
void _clear(bool p_hide_plugins = true);
|
||||||
Object *object = nullptr;
|
Object *object = nullptr;
|
||||||
|
Object *next_object = nullptr;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -576,6 +577,7 @@ public:
|
||||||
void update_property(const String &p_prop);
|
void update_property(const String &p_prop);
|
||||||
void edit(Object *p_object);
|
void edit(Object *p_object);
|
||||||
Object *get_edited_object();
|
Object *get_edited_object();
|
||||||
|
Object *get_next_edited_object();
|
||||||
|
|
||||||
void set_keying(bool p_active);
|
void set_keying(bool p_active);
|
||||||
void set_read_only(bool p_read_only);
|
void set_read_only(bool p_read_only);
|
||||||
|
|
|
@ -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<String> &p_resources) {
|
void EditorNode::_resources_changed(const Vector<String> &p_resources) {
|
||||||
List<Ref<Resource>> changed;
|
List<Ref<Resource>> changed;
|
||||||
|
|
||||||
|
@ -2135,7 +2139,12 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
|
||||||
for (EditorPlugin *plugin : active_plugins[owner_id]) {
|
for (EditorPlugin *plugin : active_plugins[owner_id]) {
|
||||||
if (!available_plugins.has(plugin)) {
|
if (!available_plugins.has(plugin)) {
|
||||||
to_remove.push_back(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;
|
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.
|
// If plugin is already associated with another owner, remove it from there first.
|
||||||
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
|
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
|
||||||
if (kv.key != owner_id) {
|
if (kv.key != owner_id) {
|
||||||
|
@ -2214,7 +2229,11 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) {
|
||||||
if (p_editing_owner) {
|
if (p_editing_owner) {
|
||||||
const ObjectID id = p_editing_owner->get_instance_id();
|
const ObjectID id = p_editing_owner->get_instance_id();
|
||||||
for (EditorPlugin *plugin : active_plugins[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);
|
active_plugins.erase(id);
|
||||||
} else {
|
} 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.
|
// This is to sweep properties that were removed from the inspector.
|
||||||
List<ObjectID> to_remove;
|
List<ObjectID> to_remove;
|
||||||
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
|
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &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<EditorPlugin>(context);
|
||||||
|
if (self_owning && self_owning->can_auto_hide()) {
|
||||||
|
context = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
to_remove.push_back(kv.key);
|
to_remove.push_back(kv.key);
|
||||||
for (EditorPlugin *plugin : kv.value) {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -550,6 +550,7 @@ private:
|
||||||
|
|
||||||
void _remove_plugin_from_enabled(const String &p_name);
|
void _remove_plugin_from_enabled(const String &p_name);
|
||||||
void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object);
|
void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object);
|
||||||
|
void _plugin_over_self_own(EditorPlugin *p_plugin);
|
||||||
|
|
||||||
void _fs_changed();
|
void _fs_changed();
|
||||||
void _resources_reimported(const Vector<String> &p_resources);
|
void _resources_reimported(const Vector<String> &p_resources);
|
||||||
|
|
|
@ -335,6 +335,10 @@ bool EditorPlugin::handles(Object *p_object) const {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EditorPlugin::can_auto_hide() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Dictionary EditorPlugin::get_state() const {
|
Dictionary EditorPlugin::get_state() const {
|
||||||
Dictionary state;
|
Dictionary state;
|
||||||
GDVIRTUAL_CALL(_get_state, state);
|
GDVIRTUAL_CALL(_get_state, state);
|
||||||
|
|
|
@ -178,6 +178,7 @@ public:
|
||||||
virtual void selected_notify() {} //notify that it was raised by the user, not the editor
|
virtual void selected_notify() {} //notify that it was raised by the user, not the editor
|
||||||
virtual void edit(Object *p_object);
|
virtual void edit(Object *p_object);
|
||||||
virtual bool handles(Object *p_object) const;
|
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 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 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 void clear(); // clear any temporary data in the editor, reset it (likely new scene or load another scene)
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
#include "editor/editor_undo_redo_manager.h"
|
#include "editor/editor_undo_redo_manager.h"
|
||||||
#include "editor/gui/editor_file_dialog.h"
|
#include "editor/gui/editor_file_dialog.h"
|
||||||
|
#include "editor/inspector_dock.h"
|
||||||
#include "editor/progress_dialog.h"
|
#include "editor/progress_dialog.h"
|
||||||
#include "scene/gui/check_button.h"
|
#include "scene/gui/check_button.h"
|
||||||
#include "scene/gui/color_picker.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);
|
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<Theme>();
|
||||||
|
EditorNode::get_singleton()->hide_unused_editors(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ThemeEditor::_add_preview_button_cbk() {
|
void ThemeEditor::_add_preview_button_cbk() {
|
||||||
preview_scene_dialog->popup_file_dialog();
|
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));
|
theme_save_as_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk).bind(true));
|
||||||
top_menu->add_child(theme_save_as_button);
|
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));
|
top_menu->add_child(memnew(VSeparator));
|
||||||
|
|
||||||
Button *theme_edit_button = memnew(Button);
|
Button *theme_edit_button = memnew(Button);
|
||||||
|
@ -3711,20 +3728,12 @@ ThemeEditor::ThemeEditor() {
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
|
||||||
void ThemeEditorPlugin::edit(Object *p_node) {
|
void ThemeEditorPlugin::edit(Object *p_object) {
|
||||||
if (Object::cast_to<Theme>(p_node)) {
|
theme_editor->edit(Ref<Theme>(p_object));
|
||||||
theme_editor->edit(Object::cast_to<Theme>(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<Theme>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ThemeEditorPlugin::handles(Object *p_node) const {
|
bool ThemeEditorPlugin::handles(Object *p_object) const {
|
||||||
return Object::cast_to<Theme>(p_node) != nullptr;
|
return Object::cast_to<Theme>(p_object) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThemeEditorPlugin::make_visible(bool p_visible) {
|
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<Theme> edited_theme = theme_editor->theme;
|
||||||
|
if (edited_theme.is_null()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Resource> edited_resource = Ref<Resource>(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<StyleBox> sbox = edited_resource;
|
||||||
|
if (sbox.is_valid()) {
|
||||||
|
List<StringName> type_list;
|
||||||
|
edited_theme->get_stylebox_type_list(&type_list);
|
||||||
|
|
||||||
|
for (const StringName &E : type_list) {
|
||||||
|
List<StringName> 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<Texture2D> tex = edited_resource;
|
||||||
|
if (tex.is_valid()) {
|
||||||
|
List<StringName> type_list;
|
||||||
|
edited_theme->get_icon_type_list(&type_list);
|
||||||
|
|
||||||
|
for (const StringName &E : type_list) {
|
||||||
|
List<StringName> 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<Font> fnt = edited_resource;
|
||||||
|
if (fnt.is_valid()) {
|
||||||
|
List<StringName> type_list;
|
||||||
|
edited_theme->get_font_type_list(&type_list);
|
||||||
|
|
||||||
|
for (const StringName &E : type_list) {
|
||||||
|
List<StringName> 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() {
|
ThemeEditorPlugin::ThemeEditorPlugin() {
|
||||||
theme_editor = memnew(ThemeEditor);
|
theme_editor = memnew(ThemeEditor);
|
||||||
|
theme_editor->plugin = this;
|
||||||
theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
|
theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
|
||||||
|
|
||||||
button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Theme"), theme_editor);
|
button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Theme"), theme_editor);
|
||||||
|
|
|
@ -47,6 +47,7 @@ class OptionButton;
|
||||||
class PanelContainer;
|
class PanelContainer;
|
||||||
class TabBar;
|
class TabBar;
|
||||||
class TabContainer;
|
class TabContainer;
|
||||||
|
class ThemeEditorPlugin;
|
||||||
class TextureRect;
|
class TextureRect;
|
||||||
|
|
||||||
class ThemeItemImportTree : public VBoxContainer {
|
class ThemeItemImportTree : public VBoxContainer {
|
||||||
|
@ -419,6 +420,9 @@ public:
|
||||||
class ThemeEditor : public VBoxContainer {
|
class ThemeEditor : public VBoxContainer {
|
||||||
GDCLASS(ThemeEditor, VBoxContainer);
|
GDCLASS(ThemeEditor, VBoxContainer);
|
||||||
|
|
||||||
|
friend class ThemeEditorPlugin;
|
||||||
|
ThemeEditorPlugin *plugin = nullptr;
|
||||||
|
|
||||||
Ref<Theme> theme;
|
Ref<Theme> theme;
|
||||||
|
|
||||||
TabBar *preview_tabs = nullptr;
|
TabBar *preview_tabs = nullptr;
|
||||||
|
@ -433,6 +437,7 @@ class ThemeEditor : public VBoxContainer {
|
||||||
|
|
||||||
void _theme_save_button_cbk(bool p_save_as);
|
void _theme_save_button_cbk(bool p_save_as);
|
||||||
void _theme_edit_button_cbk();
|
void _theme_edit_button_cbk();
|
||||||
|
void _theme_close_button_cbk();
|
||||||
|
|
||||||
void _add_preview_button_cbk();
|
void _add_preview_button_cbk();
|
||||||
void _preview_scene_dialog_cbk(const String &p_path);
|
void _preview_scene_dialog_cbk(const String &p_path);
|
||||||
|
@ -462,9 +467,10 @@ class ThemeEditorPlugin : public EditorPlugin {
|
||||||
public:
|
public:
|
||||||
virtual String get_name() const override { return "Theme"; }
|
virtual String get_name() const override { return "Theme"; }
|
||||||
bool has_main_screen() const override { return false; }
|
bool has_main_screen() const override { return false; }
|
||||||
virtual void edit(Object *p_node) override;
|
virtual void edit(Object *p_object) override;
|
||||||
virtual bool handles(Object *p_node) const override;
|
virtual bool handles(Object *p_object) const override;
|
||||||
virtual void make_visible(bool p_visible) override;
|
virtual void make_visible(bool p_visible) override;
|
||||||
|
virtual bool can_auto_hide() const override;
|
||||||
|
|
||||||
ThemeEditorPlugin();
|
ThemeEditorPlugin();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue