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.