diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml index 2bbed84b1ea9..572d5d9d846c 100644 --- a/doc/classes/EditorInspectorPlugin.xml +++ b/doc/classes/EditorInspectorPlugin.xml @@ -77,6 +77,7 @@ + Adds a property editor for an individual property. The [code]editor[/code] control must extend [EditorProperty]. diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 4bc37456d506..9a400eb2bcf0 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1008,12 +1008,11 @@ void EditorInspectorPlugin::add_custom_control(Control *control) { added_editors.push_back(ae); } -void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop) { - ERR_FAIL_COND(Object::cast_to(p_prop) == nullptr); - +void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end) { AddedEditor ae; ae.properties.push_back(p_for_property); ae.property_editor = p_prop; + ae.add_to_end = p_add_to_end; added_editors.push_back(ae); } @@ -1059,7 +1058,7 @@ void EditorInspectorPlugin::parse_end(Object *p_object) { void EditorInspectorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control); - ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor"), &EditorInspectorPlugin::add_property_editor); + ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties); GDVIRTUAL_BIND(_can_handle, "object") @@ -2894,87 +2893,99 @@ void EditorInspector::update_tree() { doc_hint = descr; } + Vector editors; + Vector late_editors; + // Search for the inspector plugin that will handle the properties. Then add the correct property editor to it. for (Ref &ped : valid_plugins) { bool exclusive = ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage, wide_editors); - List editors = ped->added_editors; // Make a copy, since plugins may be used again in a sub-inspector. + for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) { + if (F.add_to_end) { + late_editors.push_back(F); + } else { + editors.push_back(F); + } + } + ped->added_editors.clear(); - for (const EditorInspectorPlugin::AddedEditor &F : editors) { - EditorProperty *ep = Object::cast_to(F.property_editor); + if (exclusive) { + break; + } + } - if (ep) { - // Set all this before the control gets the ENTER_TREE notification. - ep->object = object; + editors.append_array(late_editors); - if (F.properties.size()) { - if (F.properties.size() == 1) { - //since it's one, associate: - ep->property = F.properties[0]; - ep->property_path = property_prefix + F.properties[0]; - ep->property_usage = p.usage; - //and set label? - } + for (int i = 0; i < editors.size(); i++) { + EditorProperty *ep = Object::cast_to(editors[i].property_editor); + const Vector &properties = editors[i].properties; - if (!F.label.is_empty()) { - ep->set_label(F.label); - } else { - // Use the existing one. - ep->set_label(property_label_string); - } - for (int i = 0; i < F.properties.size(); i++) { - String prop = F.properties[i]; + if (ep) { + // Set all this before the control gets the ENTER_TREE notification. + ep->object = object; - if (!editor_property_map.has(prop)) { - editor_property_map[prop] = List(); - } - editor_property_map[prop].push_back(ep); - } + if (properties.size()) { + if (properties.size() == 1) { + //since it's one, associate: + ep->property = properties[0]; + ep->property_path = property_prefix + properties[0]; + ep->property_usage = p.usage; + //and set label? } - ep->set_draw_warning(draw_warning); - ep->set_use_folding(use_folding); - ep->set_checkable(checkable); - ep->set_checked(checked); - ep->set_keying(keying); - ep->set_read_only(property_read_only); - ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); - } - - current_vbox->add_child(F.property_editor); - - if (ep) { - // Eventually, set other properties/signals after the property editor got added to the tree. - bool update_all = (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED); - ep->connect("property_changed", callable_mp(this, &EditorInspector::_property_changed), varray(update_all)); - ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); - ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), CONNECT_DEFERRED); - ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); - ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); - ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); - ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); - ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed)); - ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), varray(), CONNECT_DEFERRED); - ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), varray(), CONNECT_DEFERRED); - if (!doc_hint.is_empty()) { - ep->set_tooltip(property_prefix + p.name + "::" + doc_hint); + + if (!editors[i].label.is_empty()) { + ep->set_label(editors[i].label); } else { - ep->set_tooltip(property_prefix + p.name); + // Use the existing one. + ep->set_label(property_label_string); } - ep->update_property(); - ep->_update_pin_flags(); - ep->update_revert_and_pin_status(); - ep->update_cache(); + for (int j = 0; j < properties.size(); j++) { + String prop = properties[j]; - if (current_selected && ep->property == current_selected) { - ep->select(current_focusable); + if (!editor_property_map.has(prop)) { + editor_property_map[prop] = List(); + } + editor_property_map[prop].push_back(ep); } } - } + ep->set_draw_warning(draw_warning); + ep->set_use_folding(use_folding); + ep->set_checkable(checkable); + ep->set_checked(checked); + ep->set_keying(keying); + ep->set_read_only(property_read_only); + ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); + } + + current_vbox->add_child(editors[i].property_editor); + + if (ep) { + // Eventually, set other properties/signals after the property editor got added to the tree. + bool update_all = (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED); + ep->connect("property_changed", callable_mp(this, &EditorInspector::_property_changed), varray(update_all)); + ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); + ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), CONNECT_DEFERRED); + ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); + ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); + ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); + ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); + ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed)); + ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), varray(), CONNECT_DEFERRED); + ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), varray(), CONNECT_DEFERRED); + if (!doc_hint.is_empty()) { + ep->set_tooltip(property_prefix + p.name + "::" + doc_hint); + } else { + ep->set_tooltip(property_prefix + p.name); + } + ep->update_property(); + ep->_update_pin_flags(); + ep->update_revert_and_pin_status(); + ep->update_cache(); - if (exclusive) { - // If we know the plugin is exclusive, we don't need to go through other plugins. - break; + if (current_selected && ep->property == current_selected) { + ep->select(current_focusable); + } } } } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 30c0cffe4023..2b89fb3f9fce 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -205,11 +205,13 @@ class EditorProperty : public Container { class EditorInspectorPlugin : public RefCounted { GDCLASS(EditorInspectorPlugin, RefCounted); +public: friend class EditorInspector; struct AddedEditor { Control *property_editor = nullptr; Vector properties; String label; + bool add_to_end = false; }; List added_editors; @@ -226,7 +228,7 @@ class EditorInspectorPlugin : public RefCounted { public: void add_custom_control(Control *control); - void add_property_editor(const String &p_for_property, Control *p_prop); + void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false); void add_property_editor_for_multiple_properties(const String &p_label, const Vector &p_properties, Control *p_prop); virtual bool can_handle(Object *p_object); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 74c96e19d056..2b24ebaebd36 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_scale.h" #include "scene/gui/check_box.h" #include "scene/gui/view_panner.h" +#include "scene/resources/texture.h" void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) { Vector2 line = (to - from).normalized() * 10; @@ -228,8 +229,8 @@ void TextureRegionEditor::_region_draw() { Size2 vmin = vscroll->get_combined_minimum_size(); // Avoid scrollbar overlapping. - hscroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, vscroll->is_visible() ? -vmin.width : 0); - vscroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, hscroll->is_visible() ? -hmin.height : 0); + hscroll->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, vscroll->is_visible() ? -vmin.width : 0); + vscroll->set_anchor_and_offset(SIDE_BOTTOM, Control::ANCHOR_END, hscroll->is_visible() ? -hmin.height : 0); updating_scroll = false; @@ -817,8 +818,8 @@ void TextureRegionEditor::_notification(int p_what) { zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons"))); zoom_in->set_icon(get_theme_icon(SNAME("ZoomMore"), SNAME("EditorIcons"))); - vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE); - hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); + vscroll->set_anchors_and_offsets_preset(Control::PRESET_RIGHT_WIDE); + hscroll->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_WIDE); [[fallthrough]]; } case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { @@ -921,6 +922,7 @@ void TextureRegionEditor::edit(Object *p_obj) { atlas_tex = Ref(nullptr); } edit_draw->update(); + popup_centered_ratio(); } void TextureRegionEditor::_texture_changed() { @@ -977,6 +979,9 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const { } TextureRegionEditor::TextureRegionEditor() { + get_ok_button()->set_text(TTR("Close")); + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); node_sprite_2d = nullptr; node_sprite_3d = nullptr; node_ninepatch = nullptr; @@ -992,7 +997,7 @@ TextureRegionEditor::TextureRegionEditor() { drag = false; HBoxContainer *hb_tools = memnew(HBoxContainer); - add_child(hb_tools); + vb->add_child(hb_tools); hb_tools->add_child(memnew(Label(TTR("Snap Mode:")))); snap_mode_button = memnew(OptionButton); @@ -1076,12 +1081,12 @@ TextureRegionEditor::TextureRegionEditor() { panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_scroll_callback), callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); edit_draw = memnew(Panel); - add_child(edit_draw); - edit_draw->set_v_size_flags(SIZE_EXPAND_FILL); + vb->add_child(edit_draw); + edit_draw->set_v_size_flags(Control::SIZE_EXPAND_FILL); edit_draw->connect("draw", callable_mp(this, &TextureRegionEditor::_region_draw)); edit_draw->connect("gui_input", callable_mp(this, &TextureRegionEditor::_region_input)); edit_draw->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key)); - edit_draw->set_focus_mode(FOCUS_CLICK); + edit_draw->set_focus_mode(Control::FOCUS_CLICK); draw_zoom = 1.0; edit_draw->set_clip_contents(true); @@ -1119,88 +1124,40 @@ TextureRegionEditor::TextureRegionEditor() { updating_scroll = false; autoslice_is_dirty = true; -} -void TextureRegionEditorPlugin::edit(Object *p_object) { - region_editor->edit(p_object); + set_title(TTR("Region Editor")); } -bool TextureRegionEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Sprite2D") || p_object->is_class("Sprite3D") || p_object->is_class("NinePatchRect") || p_object->is_class("StyleBoxTexture") || p_object->is_class("AtlasTexture"); -} +//////////////////////// -void TextureRegionEditorPlugin::_editor_visiblity_changed() { - manually_hidden = !region_editor->is_visible_in_tree(); +bool EditorInspectorPluginTextureRegion::can_handle(Object *p_object) { + return Object::cast_to(p_object) || Object::cast_to(p_object) || Object::cast_to(p_object) || Object::cast_to(p_object) || Object::cast_to(p_object); } -void TextureRegionEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - texture_region_button->show(); - bool is_node_configured = region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch(); - is_node_configured |= region_editor->get_sprite_2d() && region_editor->get_sprite_2d()->is_region_enabled(); - is_node_configured |= region_editor->get_sprite_3d() && region_editor->get_sprite_3d()->is_region_enabled(); - if ((is_node_configured && !manually_hidden) || texture_region_button->is_pressed()) { - EditorNode::get_singleton()->make_bottom_panel_item_visible(region_editor); - } - } else { - if (region_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - manually_hidden = false; - } - texture_region_button->hide(); - region_editor->edit(nullptr); - } -} - -Dictionary TextureRegionEditorPlugin::get_state() const { - Dictionary state; - state["snap_offset"] = region_editor->snap_offset; - state["snap_step"] = region_editor->snap_step; - state["snap_separation"] = region_editor->snap_separation; - state["snap_mode"] = region_editor->snap_mode; - return state; +void EditorInspectorPluginTextureRegion::_region_edit(Object *p_object) { + texture_region_editor->edit(p_object); } -void TextureRegionEditorPlugin::set_state(const Dictionary &p_state) { - Dictionary state = p_state; - if (state.has("snap_step")) { - Vector2 s = state["snap_step"]; - region_editor->sb_step_x->set_value(s.x); - region_editor->sb_step_y->set_value(s.y); - region_editor->snap_step = s; - } - - if (state.has("snap_offset")) { - Vector2 ofs = state["snap_offset"]; - region_editor->sb_off_x->set_value(ofs.x); - region_editor->sb_off_y->set_value(ofs.y); - region_editor->snap_offset = ofs; - } - - if (state.has("snap_separation")) { - Vector2 sep = state["snap_separation"]; - region_editor->sb_sep_x->set_value(sep.x); - region_editor->sb_sep_y->set_value(sep.y); - region_editor->snap_separation = sep; - } - - if (state.has("snap_mode")) { - region_editor->_set_snap_mode(state["snap_mode"]); - region_editor->snap_mode_button->select(state["snap_mode"]); +bool EditorInspectorPluginTextureRegion::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { + if ((p_type == Variant::RECT2 || p_type == Variant::RECT2I)) { + if (((Object::cast_to(p_object) || Object::cast_to(p_object) || Object::cast_to(p_object) || Object::cast_to(p_object)) && p_path == "region_rect") || (Object::cast_to(p_object) && p_path == "region")) { + Button *button = memnew(Button); + button->set_text(TTR("Edit Region")); + button->set_icon(texture_region_editor->get_theme_icon(SNAME("RegionEdit"), SNAME("EditorIcons"))); + button->connect("pressed", callable_mp(this, &EditorInspectorPluginTextureRegion::_region_edit), varray(p_object)); + add_property_editor(p_path, button, true); + } } + return false; //not exclusive } -void TextureRegionEditorPlugin::_bind_methods() { +EditorInspectorPluginTextureRegion::EditorInspectorPluginTextureRegion() { + texture_region_editor = memnew(TextureRegionEditor); + EditorNode::get_singleton()->get_gui_base()->add_child(texture_region_editor); } TextureRegionEditorPlugin::TextureRegionEditorPlugin() { - manually_hidden = false; - - region_editor = memnew(TextureRegionEditor); - region_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); - region_editor->hide(); - region_editor->connect("visibility_changed", callable_mp(this, &TextureRegionEditorPlugin::_editor_visiblity_changed)); - - texture_region_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TextureRegion"), region_editor); - texture_region_button->hide(); + Ref inspector_plugin; + inspector_plugin.instantiate(); + add_inspector_plugin(inspector_plugin); } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 2c4ab727430c..dd92f6e3eb63 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -41,8 +41,8 @@ class ViewPanner; -class TextureRegionEditor : public VBoxContainer { - GDCLASS(TextureRegionEditor, VBoxContainer); +class TextureRegionEditor : public AcceptDialog { + GDCLASS(TextureRegionEditor, AcceptDialog); enum SnapMode { SNAP_NONE, @@ -142,26 +142,27 @@ class TextureRegionEditor : public VBoxContainer { TextureRegionEditor(); }; -class TextureRegionEditorPlugin : public EditorPlugin { - GDCLASS(TextureRegionEditorPlugin, EditorPlugin); +// - bool manually_hidden; - Button *texture_region_button = nullptr; - TextureRegionEditor *region_editor = nullptr; +class EditorInspectorPluginTextureRegion : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginTextureRegion, EditorInspectorPlugin); -protected: - static void _bind_methods(); + TextureRegionEditor *texture_region_editor = nullptr; + + void _region_edit(Object *p_object); - void _editor_visiblity_changed(); +public: + virtual bool can_handle(Object *p_object) override; + virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) override; + + EditorInspectorPluginTextureRegion(); +}; + +class TextureRegionEditorPlugin : public EditorPlugin { + GDCLASS(TextureRegionEditorPlugin, EditorPlugin); public: virtual String get_name() const override { return "TextureRegion"; } - bool has_main_screen() const override { return false; } - virtual void edit(Object *p_object) override; - virtual bool handles(Object *p_object) const override; - virtual void make_visible(bool p_visible) override; - void set_state(const Dictionary &p_state) override; - Dictionary get_state() const override; TextureRegionEditorPlugin(); };