diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index de1af96a370..e8e67001e1f 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -941,6 +941,9 @@ If [code]true[/code], automatically expands property groups in the Inspector dock when opening a scene that hasn't been opened previously. If [code]false[/code], all groups remain collapsed by default. + + If [code]true[/code], embeds the "Add Element" button next to the array header button when unfolded. The size controls are also moved to the bottom of the array inspector. + The default color picker mode to use when opening [ColorPicker]s in the editor. This mode can be temporarily adjusted on the color picker itself. @@ -964,6 +967,11 @@ If [code]true[/code], forces all property groups to be expanded in the Inspector dock and prevents collapsing them. + + Embeds array buttons in the header of each item, increasing the horizontal space available for unfolded items. + If [code]Left Side Buttons[/code] (the first bit) is set, the reorder handle will be embedded. + If [code]Right Side Buttons[/code] (the second bit) is set, the type picker and delete buttons will be embedded. + Base speed for increasing/decreasing float values by dragging them in the inspector. diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 58b02ed2b8a..a4fd5a08eb1 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -276,6 +276,12 @@ void EditorProperty::_notification(int p_what) { if (c == bottom_editor) { continue; } + if (c == left_container) { + continue; + } + if (c == right_container) { + continue; + } Size2 minsize = c->get_combined_minimum_size(); child_room = MAX(child_room, minsize.width); @@ -298,6 +304,13 @@ void EditorProperty::_notification(int p_what) { } } + if (right_container && right_container->get_child_count() > 0) { + rect.size.x -= right_container->get_combined_minimum_size().x + get_theme_constant(SNAME("separation"), SNAME("HBoxContainer")); + if (is_layout_rtl()) { + rect.position.x += right_container->get_combined_minimum_size().x + get_theme_constant(SNAME("separation"), SNAME("HBoxContainer")); + } + } + if (bottom_editor) { int v_offset = label.is_empty() ? 0 : get_theme_constant(SNAME("v_separation")); bottom_rect = Rect2(0, rect.size.height + v_offset, size.width, bottom_editor->get_combined_minimum_size().height); @@ -359,6 +372,12 @@ void EditorProperty::_notification(int p_what) { if (c == bottom_editor) { continue; } + if (c == left_container) { + continue; + } + if (c == right_container) { + continue; + } fit_child_in_rect(c, rect); right_child_rect = rect; @@ -369,6 +388,24 @@ void EditorProperty::_notification(int p_what) { bottom_child_rect = bottom_rect; } + if (left_container) { + Size2 ls = left_container->get_combined_minimum_size(); + if (is_layout_rtl()) { + fit_child_in_rect(left_container, Rect2(size.width - ls.width, 0, ls.width, rect.size.y)); + } else { + fit_child_in_rect(left_container, Rect2(0, 0, ls.width, rect.size.y)); + } + } + + if (right_container) { + Size2 rs = right_container->get_combined_minimum_size(); + if (is_layout_rtl()) { + fit_child_in_rect(right_container, Rect2(0, 0, rs.width, rect.size.y)); + } else { + fit_child_in_rect(right_container, Rect2(size.width - rs.width, 0, rs.width, rect.size.y)); + } + } + queue_redraw(); //need to redraw text } break; @@ -418,6 +455,12 @@ void EditorProperty::_notification(int p_what) { int padding = base_spacing * EDSCALE; int half_padding = padding / 2; + if (left_container) { + int left_ofs = left_container->get_combined_minimum_size().x; + ofs += left_ofs; + text_limit -= left_ofs; + } + if (checkable) { Ref checkbox; if (checked) { @@ -500,6 +543,10 @@ void EditorProperty::_notification(int p_what) { ofs = size.width; + if (right_container) { + ofs -= right_container->get_minimum_size().x; + } + if (keying) { Ref key; @@ -907,6 +954,18 @@ bool EditorProperty::is_selected() const { return selected; } +void EditorProperty::add_inline_control(Control *p_control, bool p_left) { + Node *parent = p_control->get_parent(); + if (parent != nullptr) { + parent->remove_child(p_control); + } + if (p_left) { + left_container->add_child(p_control); + } else { + right_container->add_child(p_control); + } +} + void EditorProperty::gui_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -1351,6 +1410,12 @@ EditorProperty::EditorProperty() { label_reference = nullptr; bottom_editor = nullptr; menu = nullptr; + + left_container = memnew(HBoxContainer); + add_child(left_container); + right_container = memnew(HBoxContainer); + add_child(right_container); + set_process_shortcut_input(true); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 9baf65444f2..e78f1a39e74 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -135,6 +135,8 @@ private: Control *label_reference = nullptr; Control *bottom_editor = nullptr; PopupMenu *menu = nullptr; + HBoxContainer *left_container = nullptr; + HBoxContainer *right_container = nullptr; HashMap cache; @@ -216,6 +218,8 @@ public: void deselect(); bool is_selected() const; + void add_inline_control(Control *p_control, bool p_left); + void set_label_reference(Control *p_control); void set_bottom_editor(Control *p_control); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 93c5a70cf90..e983dc8cdec 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -306,9 +306,14 @@ void EditorPropertyArray::_object_id_selected(const StringName &p_property, Obje } void EditorPropertyArray::_create_new_property_slot() { + int condense_list_buttons = EDITOR_GET("interface/inspector/embedded_list_buttons"); + bool condense_left = (condense_list_buttons & (1 << 0)) != 0; + bool condense_right = (condense_list_buttons & (1 << 1)) != 0; int idx = slots.size(); HBoxContainer *hbox = memnew(HBoxContainer); + EditorProperty *prop = memnew(EditorPropertyNil); + Button *reorder_button = memnew(Button); reorder_button->set_button_icon(get_editor_theme_icon(SNAME("TripleBar"))); reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE); @@ -317,24 +322,38 @@ void EditorPropertyArray::_create_new_property_slot() { reorder_button->connect(SNAME("button_up"), callable_mp(this, &EditorPropertyArray::_reorder_button_up)); reorder_button->connect(SNAME("button_down"), callable_mp(this, &EditorPropertyArray::_reorder_button_down).bind(idx)); - hbox->add_child(reorder_button); - EditorProperty *prop = memnew(EditorPropertyNil); + if (condense_left) { + prop->add_inline_control(reorder_button, true); + } else { + hbox->add_child(reorder_button); + } hbox->add_child(prop); bool is_untyped_array = object->get_array().get_type() == Variant::ARRAY && subtype == Variant::NIL; + Button *edit_btn = nullptr; + Button *remove_btn = nullptr; + if (is_untyped_array) { - Button *edit_btn = memnew(Button); + edit_btn = memnew(Button); edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); edit_btn->set_disabled(is_read_only()); edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_change_type).bind(edit_btn, idx)); - hbox->add_child(edit_btn); + if (condense_right) { + prop->add_inline_control(edit_btn, false); + } else { + hbox->add_child(edit_btn); + } } else { - Button *remove_btn = memnew(Button); + remove_btn = memnew(Button); remove_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); remove_btn->set_disabled(is_read_only()); remove_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_remove_pressed).bind(idx)); - hbox->add_child(remove_btn); + if (condense_right) { + prop->add_inline_control(remove_btn, false); + } else { + hbox->add_child(remove_btn); + } } property_vbox->add_child(hbox); @@ -343,6 +362,8 @@ void EditorPropertyArray::_create_new_property_slot() { slot.object = object; slot.container = hbox; slot.reorder_button = reorder_button; + slot.edit_button = edit_btn; + slot.remove_button = remove_btn; slot.set_index(idx + page_index * page_length); slots.push_back(slot); } @@ -386,6 +407,7 @@ void EditorPropertyArray::update_property() { set_bottom_editor(nullptr); memdelete(container); button_add_item = nullptr; + inline_button_add_item = nullptr; container = nullptr; slots.clear(); } @@ -413,13 +435,18 @@ void EditorPropertyArray::update_property() { edit->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); edit->set_button_icon(get_editor_theme_icon(array_type_name)); edit->set_text(vformat("%s%s", array_sub_type_name, ctr_str)); - edit->set_tooltip_text(vformat(TTR("%s%s (size %d)"), array_type_name, array_sub_type_name, size)); + edit->set_tooltip_text(vformat(TTR("%s%s (%d)"), array_type_name, array_sub_type_name, size)); } else { edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); edit->set_button_icon(Ref()); - edit->set_text(vformat(TTR("%s (size %d)"), array_type_name, size)); + edit->set_text(vformat(TTR("%s (%d)"), array_type_name, size)); } + bool condensed = EDITOR_GET("interface/inspector/condensed_size_controls_layout"); + int embed_list_buttons = EDITOR_GET("interface/inspector/embedded_list_buttons"); + bool embed_left = (embed_list_buttons & (1 << 0)) != 0; + bool embed_right = (embed_list_buttons & (1 << 1)) != 0; + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); if (edit->is_pressed() != unfolded) { edit->set_pressed(unfolded); @@ -438,12 +465,15 @@ void EditorPropertyArray::update_property() { container->add_child(vbox); HBoxContainer *hbox = memnew(HBoxContainer); - vbox->add_child(hbox); - Label *size_label = memnew(Label(TTR("Size:"))); + Label *size_label = memnew(Label(get_label() + " " + TTR("Size:"))); size_label->set_h_size_flags(SIZE_EXPAND_FILL); hbox->add_child(size_label); + HBoxContainer *right_hbox = memnew(HBoxContainer); + right_hbox->set_h_size_flags(SIZE_EXPAND_FILL); + hbox->add_child(right_hbox); + size_slider = memnew(EditorSpinSlider); size_slider->set_step(1); size_slider->set_max(INT32_MAX); @@ -451,20 +481,51 @@ void EditorPropertyArray::update_property() { size_slider->set_h_size_flags(SIZE_EXPAND_FILL); size_slider->set_read_only(is_read_only()); size_slider->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyArray::_length_changed)); - hbox->add_child(size_slider); + right_hbox->add_child(size_slider); property_vbox = memnew(VBoxContainer); property_vbox->set_h_size_flags(SIZE_EXPAND_FILL); - vbox->add_child(property_vbox); - button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element")); - button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); - button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_add_element)); - button_add_item->set_disabled(is_read_only()); - vbox->add_child(button_add_item); + if (condensed) { + if (!inline_button_add_item) { + inline_button_add_item = memnew(Button); + inline_button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + inline_button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_add_element)); + inline_button_add_item->set_disabled(is_read_only()); + add_inline_control(inline_button_add_item, false); + if (embed_right) { + inline_button_add_item->set_flat(true); + } + } else { + inline_button_add_item->set_visible(true); + } + inline_button_add_item->get_parent()->move_child(inline_button_add_item, 0); + + if (!inline_spacer) { + inline_spacer = memnew(Control); + add_inline_control(inline_spacer, false); + } else { + inline_spacer->set_visible(true); + } + } paginator = memnew(EditorPaginator); paginator->connect("page_changed", callable_mp(this, &EditorPropertyArray::_page_changed)); + + if (!condensed) { + vbox->add_child(hbox); + } + vbox->add_child(property_vbox); + if (!condensed) { + button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element")); + button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_add_element)); + button_add_item->set_disabled(is_read_only()); + vbox->add_child(button_add_item); + } + if (condensed) { + vbox->add_child(hbox); + } vbox->add_child(paginator); for (int i = 0; i < page_length; i++) { @@ -474,7 +535,9 @@ void EditorPropertyArray::update_property() { size_slider->set_value(size); property_vbox->set_visible(size > 0); - button_add_item->set_visible(page_index == max_page); + if (!condensed) { + button_add_item->set_visible(page_index == max_page); + } paginator->update(page_index, max_page); paginator->set_visible(max_page > 0); @@ -512,6 +575,32 @@ void EditorPropertyArray::update_property() { new_prop->connect(SNAME("object_id_selected"), callable_mp(this, &EditorPropertyArray::_object_id_selected)); new_prop->set_h_size_flags(SIZE_EXPAND_FILL); new_prop->set_read_only(is_read_only()); + + if (slot.reorder_button) { + if (embed_left) { + new_prop->add_inline_control(slot.reorder_button, true); + slot.reorder_button->set_flat(true); + } else { + slot.reorder_button->set_flat(false); + } + } + if (slot.edit_button) { + if (embed_right) { + new_prop->add_inline_control(slot.edit_button, false); + slot.edit_button->set_flat(true); + } else { + slot.edit_button->set_flat(false); + } + } + if (slot.remove_button) { + if (embed_right) { + new_prop->add_inline_control(slot.remove_button, false); + slot.remove_button->set_flat(true); + } else { + slot.remove_button->set_flat(false); + } + } + slot.prop->add_sibling(new_prop, false); slot.prop->queue_free(); slot.prop = new_prop; @@ -531,6 +620,12 @@ void EditorPropertyArray::update_property() { set_bottom_editor(nullptr); memdelete(container); button_add_item = nullptr; + if (inline_button_add_item) { + inline_button_add_item->set_visible(false); + } + if (inline_spacer) { + inline_spacer->set_visible(false); + } container = nullptr; slots.clear(); } @@ -541,6 +636,12 @@ void EditorPropertyArray::_remove_pressed(int p_slot_index) { Variant array = object->get_array().duplicate(); array.call("remove_at", slots[p_slot_index].index); + // Sync folded state after deletion. + for (int i = p_slot_index; i < (int)array.call("size"); i++) { + bool folded = object->editor_is_section_unfolded("indices/" + itos(i + 1)); + object->editor_set_section_unfold("indices/" + itos(i), folded); + } + emit_changed(get_edited_property(), array); } @@ -722,6 +823,9 @@ void EditorPropertyArray::_notification(int p_what) { if (button_add_item) { button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } + if (inline_button_add_item) { + inline_button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + } } break; case NOTIFICATION_DRAG_BEGIN: { @@ -876,6 +980,24 @@ void EditorPropertyArray::_reorder_button_up() { if (reorder_slot.index != reorder_to_index) { // Move the element. Variant array = object->get_array().duplicate(); + int min_i = MIN(reorder_slot.index, reorder_to_index); + int max_i = MAX(reorder_slot.index, reorder_to_index); + + // Sync folding state after move. + Vector folds; + for (int i = min_i; i <= max_i; i++) { + StringName p = "indices/" + itos(i); + bool folded = object->editor_is_section_unfolded(p); + folds.append(folded); + } + bool f = folds[reorder_slot.index - min_i]; + folds.remove_at(reorder_slot.index - min_i); + folds.insert(reorder_to_index - min_i, f); + + for (int i = 0; i < folds.size(); i++) { + bool folded = folds[i]; + object->editor_set_section_unfold("indices/" + itos(i + min_i), folded); + } property_vbox->move_child(reorder_slot.container, reorder_slot.index % page_length); Variant value_to_move = array.get(reorder_slot.index); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index c0b4453b32c..9ff59d82c33 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -104,6 +104,8 @@ class EditorPropertyArray : public EditorProperty { bool as_id = false; EditorProperty *prop = nullptr; Button *reorder_button = nullptr; + Button *edit_button = nullptr; + Button *remove_button = nullptr; void set_index(int p_idx) { String prop_name = "indices/" + itos(p_idx); @@ -124,6 +126,8 @@ class EditorPropertyArray : public EditorProperty { VBoxContainer *property_vbox = nullptr; EditorSpinSlider *size_slider = nullptr; Button *button_add_item = nullptr; + Button *inline_button_add_item = nullptr; + Control *inline_spacer = nullptr; EditorPaginator *paginator = nullptr; Variant::Type array_type; Variant::Type subtype; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index a246249cfe3..98c48b762ce 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -517,6 +517,8 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/horizontal_vector2_editing", false, "") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/horizontal_vector_types_editing", true, "") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/open_resources_in_current_inspector", true, "") + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_FLAGS, "interface/inspector/embedded_list_buttons", 3, "Left Side Buttons,Right Side Buttons"); + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/condensed_size_controls_layout", true, ""); PackedStringArray open_in_new_inspector_defaults; // Required for the script editor to work.