Add "desired size" to Control

A `Control` can define `get_desired_size()` which allows it to grow based on its content without increasing its minimum size.
This new size will be respected by parent `Container`s.
Currently used by `Label` and `TabBar`.
This commit is contained in:
StarryWorm authored and Enzo Novoselic committed 2026-05-07 13:01:02 -04:00
1 parent 6d6e822c68
commit 05737cd449
33 files changed
+454 -72

No files matched your search

+10 -2
View File
@@ -288,7 +288,7 @@ void BoxContainer::_resort() {
}
}
Size2 BoxContainer::get_minimum_size() const {
Size2 BoxContainer::_get_minimum_size(bool p_use_desired_sizes) const {
/* Calculate MINIMUM SIZE */
Size2i minimum;
@@ -301,7 +301,7 @@ Size2 BoxContainer::get_minimum_size() const {
continue;
}
Size2i size = c->get_bound_minimum_size();
Size2i size = p_use_desired_sizes ? c->get_bound_desired_size() : c->get_bound_minimum_size();
if (vertical) { /* VERTICAL */
@@ -326,6 +326,14 @@ Size2 BoxContainer::get_minimum_size() const {
return minimum;
}
Size2 BoxContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 BoxContainer::get_desired_size() const {
return _get_minimum_size(true);
}
void BoxContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
+2
View File
@@ -51,6 +51,7 @@ private:
} theme_cache;
void _resort();
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
bool is_fixed = false;
@@ -69,6 +70,7 @@ public:
bool is_vertical() const;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
+17
View File
@@ -49,6 +49,23 @@ Size2 CenterContainer::get_minimum_size() const {
return ms;
}
Size2 CenterContainer::get_desired_size() const {
if (use_top_left) {
return Size2();
}
Size2 ds;
for (int i = 0; i < get_child_count(); i++) {
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
if (!c) {
continue;
}
Size2 minsize = c->get_bound_desired_size();
ds = ds.max(minsize);
}
return ds;
}
void CenterContainer::set_use_top_left(bool p_enable) {
if (use_top_left == p_enable) {
return;
+1
View File
@@ -46,6 +46,7 @@ public:
bool is_using_top_left() const;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
+10
View File
@@ -39,6 +39,11 @@ void Container::_child_minsize_changed() {
queue_sort();
}
void Container::_child_desired_size_changed() {
update_desired_size();
queue_sort();
}
void Container::add_child_notify(Node *p_child) {
Control::add_child_notify(p_child);
@@ -50,6 +55,7 @@ void Container::add_child_notify(Node *p_child) {
control->connect(SceneStringName(size_flags_changed), callable_mp(this, &Container::queue_sort));
control->connect(SceneStringName(minimum_size_changed), callable_mp(this, &Container::_child_minsize_changed));
control->connect(SceneStringName(maximum_size_changed), callable_mp(this, &Container::_child_minsize_changed));
control->connect("_desired_size_changed", callable_mp(this, &Container::_child_desired_size_changed));
control->connect(SceneStringName(visibility_changed), callable_mp(this, &Container::_child_minsize_changed));
update_minimum_size();
@@ -78,6 +84,7 @@ void Container::remove_child_notify(Node *p_child) {
control->disconnect(SceneStringName(size_flags_changed), callable_mp(this, &Container::queue_sort));
control->disconnect(SceneStringName(minimum_size_changed), callable_mp(this, &Container::_child_minsize_changed));
control->disconnect(SceneStringName(maximum_size_changed), callable_mp(this, &Container::_child_minsize_changed));
control->disconnect("_desired_size_changed", callable_mp(this, &Container::_child_desired_size_changed));
control->disconnect(SceneStringName(visibility_changed), callable_mp(this, &Container::_child_minsize_changed));
update_minimum_size();
@@ -105,6 +112,7 @@ void Container::fit_child_in_rect(RequiredParam<Control> rp_child, const Rect2 &
bool rtl = is_layout_rtl();
Size2 minsize = p_child->get_combined_minimum_size();
Size2 desired_size = p_child->get_bound_desired_size();
Size2 maxsize = p_child->get_combined_maximum_size();
Rect2 r = p_rect;
BitField<SizeFlags> h_size_flags = p_child->get_h_size_flags();
@@ -112,6 +120,7 @@ void Container::fit_child_in_rect(RequiredParam<Control> rp_child, const Rect2 &
if (!h_size_flags.has_flag(SIZE_FILL)) {
float final_width = minsize.width;
final_width = MAX(MIN(desired_size.width, r.size.width), final_width);
if (maxsize.width >= 0) {
final_width = MIN(final_width, maxsize.width);
}
@@ -127,6 +136,7 @@ void Container::fit_child_in_rect(RequiredParam<Control> rp_child, const Rect2 &
if (!v_size_flags.has_flag(SIZE_FILL)) {
float final_height = minsize.y;
final_height = MAX(MIN(desired_size.y, r.size.y), final_height);
if (maxsize.height >= 0) {
final_height = MIN(final_height, maxsize.height);
}
+1
View File
@@ -39,6 +39,7 @@ class Container : public Control {
bool accessibility_region = false;
void _sort_children();
void _child_minsize_changed();
void _child_desired_size_changed();
protected:
enum class SortableVisibilityMode {
+112
View File
@@ -1550,6 +1550,8 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
}
#endif // TOOLS_ENABLED
data.expanded_by_desired_size = false;
if (p_keep_offsets) {
_compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor);
} else {
@@ -1761,6 +1763,11 @@ void Control::update_maximum_size() {
data.updating_last_minimum_size = true;
callable_mp(this, &Control::_update_minimum_size).call_deferred();
}
// Same with desired size.
if (!data.updating_last_desired_size) {
data.updating_last_desired_size = true;
callable_mp(this, &Control::_update_desired_size).call_deferred();
}
callable_mp(this, &Control::_update_maximum_size).call_deferred();
}
@@ -1948,6 +1955,107 @@ Size2 Control::get_custom_minimum_size() const {
return data.custom_minimum_size;
}
void Control::_update_desired_size_cache() const {
Size2 desired_size = get_desired_size();
data.desired_size_cache = desired_size;
data.desired_size_valid = true;
}
void Control::_update_desired_size() {
if (!is_inside_tree()) {
data.updating_last_desired_size = false;
return;
}
Size2 desired_size = get_desired_size();
data.updating_last_desired_size = false;
if (desired_size != data.last_desired_size) {
data.last_desired_size = desired_size;
grow_to_desired_size();
emit_signal("_desired_size_changed");
}
}
void Control::update_desired_size() {
ERR_MAIN_THREAD_GUARD;
if (!is_inside_tree()) {
return;
}
// Invalidate cache upwards.
Control *invalidate = this;
while (invalidate && invalidate->data.desired_size_valid) {
invalidate->data.desired_size_valid = false;
if (invalidate->is_set_as_top_level()) {
break; // Do not go further up.
}
Window *parent_window = invalidate->get_parent_window();
if (parent_window && parent_window->is_wrapping_controls()) {
parent_window->child_controls_changed();
break; // Stop on a window as well.
}
invalidate = invalidate->get_parent_control();
}
if (!is_visible_in_tree()) {
// Invalidate the last desired size so it will update when made visible.
data.last_desired_size = Size2(-1, -1);
return;
}
if (data.updating_last_desired_size) {
return;
}
data.updating_last_desired_size = true;
callable_mp(this, &Control::_update_desired_size).call_deferred();
}
Size2 Control::get_bound_desired_size() const {
ERR_READ_THREAD_GUARD_V(Size2());
if (!data.desired_size_valid) {
_update_desired_size_cache();
}
Size2 desired_size = data.desired_size_cache;
desired_size = desired_size.max(get_combined_minimum_size());
Size2 max_size = get_combined_maximum_size();
if (max_size.x >= 0) {
desired_size.x = MIN(desired_size.x, max_size.x);
}
if (max_size.y >= 0) {
desired_size.y = MIN(desired_size.y, max_size.y);
}
return desired_size;
}
Size2 Control::get_desired_size() const {
return Size2();
}
void Control::grow_to_desired_size() {
ERR_MAIN_THREAD_GUARD;
if (!is_inside_tree()) {
return;
}
Size2 desired_size = get_bound_desired_size();
if (desired_size <= get_combined_minimum_size()) {
return;
}
if (data.expanded_by_desired_size) {
set_size(desired_size);
data.expanded_by_desired_size = true;
} else if (desired_size.x > get_size().x || desired_size.y > get_size().y) {
set_size(desired_size);
data.expanded_by_desired_size = true;
}
}
void Control::add_child_notify(Node *p_child) {
CanvasItem::add_child_notify(p_child);
@@ -2021,6 +2129,9 @@ void Control::_update_minimum_size_cache() const {
data.minimum_size_cache = minsize;
data.minimum_size_valid = true;
// Keep the desired size cache in sync so it will update if needed when the minimum size changes. This is needed for get_bound_desired_size to work correctly.
data.desired_size_valid = false;
}
Size2 Control::get_combined_minimum_size() const {
@@ -5024,6 +5135,7 @@ void Control::_bind_methods() {
ADD_SIGNAL(MethodInfo("size_flags_changed"));
ADD_SIGNAL(MethodInfo("maximum_size_changed"));
ADD_SIGNAL(MethodInfo("minimum_size_changed"));
ADD_SIGNAL(MethodInfo("_desired_size_changed"));
ADD_SIGNAL(MethodInfo("theme_changed"));
GDVIRTUAL_BIND(_has_point, "point");
+17
View File
@@ -256,6 +256,14 @@ private:
bool updating_last_minimum_size = false;
bool block_minimum_size_adjust = false;
mutable Size2 desired_size_cache;
mutable bool desired_size_valid = false;
Size2 last_desired_size;
bool updating_last_desired_size = false;
bool expanded_by_desired_size = false;
bool layout_pending = false;
bool size_warning = true;
@@ -371,6 +379,9 @@ private:
void _update_maximum_size();
void _update_minimum_size_cache() const;
void _update_minimum_size();
void _update_desired_size_cache() const;
void _update_desired_size();
void _grow_to_desired_size();
void _size_changed();
void _top_level_changed() override {} // Controls don't need to do anything, only other CanvasItems.
@@ -603,6 +614,9 @@ public:
void update_maximum_size();
void update_minimum_size();
void update_desired_size();
void grow_to_desired_size();
void set_block_maximum_size_adjust(bool p_block);
void set_block_minimum_size_adjust(bool p_block);
@@ -624,6 +638,9 @@ public:
virtual Size2 get_bound_minimum_size() const;
Size2 get_bound_desired_size() const;
virtual Size2 get_desired_size() const;
bool is_layout_pending() const;
bool is_layout_pending_in_tree() const;
void layout_pending_start();
+10 -2
View File
@@ -347,7 +347,7 @@ void FlowContainer::_resort() {
cached_line_max_child_count = lines_data.size() > 0 ? lines_data[0].child_count : 0;
}
Size2 FlowContainer::get_minimum_size() const {
Size2 FlowContainer::_get_minimum_size(bool p_use_desired_sizes) const {
Size2i minimum;
for (int i = 0; i < get_child_count(); i++) {
@@ -356,7 +356,7 @@ Size2 FlowContainer::get_minimum_size() const {
continue;
}
Size2i size = c->get_bound_minimum_size();
Size2i size = p_use_desired_sizes ? c->get_bound_desired_size() : c->get_bound_minimum_size();
if (vertical) { /* VERTICAL */
minimum.height = MAX(minimum.height, size.height);
@@ -371,6 +371,14 @@ Size2 FlowContainer::get_minimum_size() const {
return minimum;
}
Size2 FlowContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 FlowContainer::get_desired_size() const {
return _get_minimum_size(true);
}
Vector<int> FlowContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
+2
View File
@@ -64,6 +64,7 @@ private:
} theme_cache;
void _resort();
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
bool is_fixed = false;
@@ -89,6 +90,7 @@ public:
bool is_reverse_fill() const;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
+20
View File
@@ -54,6 +54,26 @@ Size2 FoldableContainer::get_minimum_size() const {
return Size2(MAX(ms.width, title_minimum_size.width), ms.height + title_minimum_size.height);
}
Size2 FoldableContainer::get_desired_size() const {
_update_title_min_size();
if (folded) {
return title_minimum_size;
}
Size2 ds;
for (int i = 0; i < get_child_count(); i++) {
Control *c = as_sortable_control(get_child(i));
if (!c) {
continue;
}
ds = ds.max(c->get_bound_desired_size());
}
ds += theme_cache.panel_style->get_minimum_size();
return Size2(MAX(ds.width, title_minimum_size.width), ds.height + title_minimum_size.height);
}
Size2 FoldableContainer::get_inner_combined_maximum_size() const {
Size2 ms = Container::get_inner_combined_maximum_size();
+1
View File
@@ -138,6 +138,7 @@ public:
void remove_title_bar_control(Control *p_control);
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Size2 get_inner_combined_maximum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override { return { SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END }; }
+11 -3
View File
@@ -323,11 +323,11 @@ bool GraphFrame::has_point(const Point2 &p_point) const {
return false;
}
Size2 GraphFrame::get_minimum_size() const {
Size2 GraphFrame::_get_minimum_size(bool p_use_desired_sizes) const {
Ref<StyleBox> sb_panel = theme_cache.panel;
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
Size2 minsize = titlebar_hbox->get_minimum_size() + sb_titlebar->get_minimum_size();
Size2 minsize = (p_use_desired_sizes ? titlebar_hbox->get_bound_desired_size() : titlebar_hbox->get_minimum_size()) + sb_titlebar->get_minimum_size();
for (int i = 0; i < get_child_count(false); i++) {
Control *child = as_sortable_control(get_child(i, false));
@@ -335,7 +335,7 @@ Size2 GraphFrame::get_minimum_size() const {
continue;
}
Size2i size = child->get_bound_minimum_size();
Size2i size = p_use_desired_sizes ? child->get_bound_desired_size() : child->get_bound_minimum_size();
size.width += sb_panel->get_minimum_size().width;
minsize.x = MAX(minsize.x, size.x);
@@ -347,6 +347,14 @@ Size2 GraphFrame::get_minimum_size() const {
return minsize;
}
Size2 GraphFrame::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 GraphFrame::get_desired_size() const {
return _get_minimum_size(true);
}
GraphFrame::GraphFrame() {
titlebar_hbox = memnew(HBoxContainer);
titlebar_hbox->set_h_size_flags(SIZE_EXPAND_FILL);
+3
View File
@@ -60,6 +60,8 @@ private:
bool tint_color_enabled = false;
Color tint_color = Color(0.3, 0.3, 0.3, 0.75);
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
@@ -95,6 +97,7 @@ public:
virtual bool has_point(const Point2 &p_point) const override;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
GraphFrame();
};
+11 -3
View File
@@ -996,13 +996,13 @@ bool GraphNode::is_ignoring_valid_connection_type() const {
return ignore_invalid_connection_type;
}
Size2 GraphNode::get_minimum_size() const {
Size2 GraphNode::_get_minimum_size(bool p_use_desired_sizes) const {
Ref<StyleBox> sb_panel = theme_cache.panel;
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
Ref<StyleBox> sb_slot = theme_cache.slot;
int separation = theme_cache.separation;
Size2 minsize = titlebar_hbox->get_minimum_size() + sb_titlebar->get_minimum_size();
Size2 minsize = (p_use_desired_sizes ? titlebar_hbox->get_bound_desired_size() : titlebar_hbox->get_minimum_size()) + sb_titlebar->get_minimum_size();
for (int i = 0; i < get_child_count(false); i++) {
Control *child = as_sortable_control(get_child(i, false));
@@ -1010,7 +1010,7 @@ Size2 GraphNode::get_minimum_size() const {
continue;
}
Size2i size = child->get_bound_minimum_size();
Size2i size = p_use_desired_sizes ? child->get_bound_desired_size() : child->get_bound_minimum_size();
size.width += sb_panel->get_minimum_size().width;
if (slot_table.has(i)) {
size += slot_table[i].draw_stylebox ? sb_slot->get_minimum_size() : Size2();
@@ -1029,6 +1029,14 @@ Size2 GraphNode::get_minimum_size() const {
return minsize;
}
Size2 GraphNode::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 GraphNode::get_desired_size() const {
return _get_minimum_size(true);
}
void GraphNode::_port_pos_update() {
int edgeofs = theme_cache.port_h_offset;
int separation = theme_cache.separation;
+3
View File
@@ -115,6 +115,8 @@ class GraphNode : public GraphElement {
void _port_pos_update();
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -195,6 +197,7 @@ public:
Control::FocusMode get_slots_focus_mode() const;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+10 -2
View File
@@ -378,7 +378,7 @@ void GridContainer::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GridContainer, v_separation);
}
Size2 GridContainer::get_minimum_size() const {
Size2 GridContainer::_get_minimum_size(bool p_use_desired_sizes) const {
RBMap<int, int> col_minw;
RBMap<int, int> row_minh;
@@ -395,7 +395,7 @@ Size2 GridContainer::get_minimum_size() const {
int col = valid_controls_index % columns;
valid_controls_index++;
Size2i ms = c->get_bound_minimum_size();
Size2i ms = p_use_desired_sizes ? c->get_bound_desired_size() : c->get_bound_minimum_size();
if (col_minw.has(col)) {
col_minw[col] = MAX(col_minw[col], ms.width);
} else {
@@ -426,3 +426,11 @@ Size2 GridContainer::get_minimum_size() const {
return ms;
}
Size2 GridContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 GridContainer::get_desired_size() const {
return _get_minimum_size(true);
}
+2
View File
@@ -44,6 +44,7 @@ class GridContainer : public Container {
private:
void _resort();
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
void _notification(int p_what);
@@ -53,6 +54,7 @@ public:
void set_columns(int p_columns);
int get_columns() const;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
int get_h_separation() const;
};
+26 -24
View File
@@ -53,6 +53,7 @@ void Label::set_autowrap_mode(TextServer::AutowrapMode p_mode) {
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
update_minimum_size();
}
update_desired_size();
}
TextServer::AutowrapMode Label::get_autowrap_mode() const {
@@ -74,6 +75,7 @@ void Label::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags)
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
update_minimum_size();
}
update_desired_size();
}
BitField<TextServer::LineBreakFlag> Label::get_autowrap_trim_flags() const {
@@ -152,11 +154,7 @@ void Label::_shape() const {
if (maximum_width <= 0) {
maximum_width = 1;
}
if (width > 0) {
width = MIN(width, maximum_width);
} else {
width = maximum_width;
}
width = maximum_width;
}
if (text_dirty) {
@@ -914,6 +912,7 @@ void Label::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
font_dirty = true;
queue_redraw();
update_desired_size();
update_configuration_warnings();
} break;
@@ -993,9 +992,6 @@ Size2 Label::get_minimum_size() const {
_ensure_shaped();
Size2 min_size = minsize;
Size2 combined_maximum_size = get_combined_maximum_size();
bool wrap_with_max_width = autowrap_mode != TextServer::AUTOWRAP_OFF && combined_maximum_size.x > 0;
bool overrun_with_max_width = autowrap_mode == TextServer::AUTOWRAP_OFF && combined_maximum_size.x > 0 && (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING);
const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
@@ -1010,28 +1006,29 @@ Size2 Label::get_minimum_size() const {
} else if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
min_size.height = 1;
}
if (wrap_with_max_width) {
min_size.width = MAX(1, min_size.width);
return min_size + min_style;
} else {
return Size2(1, min_size.height) + min_style;
}
return Size2(1, min_size.height) + min_style;
} else {
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
if (overrun_with_max_width) {
min_size.width = MAX(1, min_size.width);
} else {
min_size.width = 1;
}
min_size.width = 1;
}
Size2 computed_min_size = min_size + min_style;
if (overrun_with_max_width) {
computed_min_size.width = MIN(computed_min_size.width, combined_maximum_size.x);
}
return computed_min_size;
return min_size + min_style;
}
}
Size2 Label::get_desired_size() const {
Size2 combined_max = get_combined_maximum_size();
if (combined_max.width < 0) {
return Size2();
}
_ensure_shaped();
Size2 min_style = theme_cache.normal_style->get_minimum_size();
Size2 content_size = minsize + min_style;
content_size.width = MIN(content_size.width, int(combined_max.width));
return content_size;
}
#ifndef DISABLE_DEPRECATED
bool Label::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == SNAME("valign")) {
@@ -1144,6 +1141,7 @@ void Label::set_text(const String &p_string) {
queue_accessibility_update();
queue_redraw();
update_minimum_size();
update_desired_size();
update_configuration_warnings();
}
@@ -1163,6 +1161,7 @@ void Label::_maximum_size_changed() {
}
queue_redraw();
update_minimum_size();
update_desired_size();
update_configuration_warnings();
}
@@ -1264,6 +1263,7 @@ void Label::set_clip_text(bool p_clip) {
clip = p_clip;
queue_redraw();
update_minimum_size();
update_desired_size();
}
bool Label::is_clipping_text() const {
@@ -1297,6 +1297,7 @@ void Label::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) {
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
update_minimum_size();
}
update_desired_size();
}
TextServer::OverrunBehavior Label::get_text_overrun_behavior() const {
@@ -1320,6 +1321,7 @@ void Label::set_ellipsis_char(const String &p_char) {
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
update_minimum_size();
}
update_desired_size();
}
String Label::get_ellipsis_char() const {
+1
View File
@@ -116,6 +116,7 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual PackedStringArray get_configuration_warnings() const override;
void set_horizontal_alignment(HorizontalAlignment p_alignment);
+20 -6
View File
@@ -42,12 +42,7 @@ Size2 MarginContainer::get_minimum_size() const {
}
Size2 s = c->get_bound_minimum_size();
if (s.width > max.width) {
max.width = s.width;
}
if (s.height > max.height) {
max.height = s.height;
}
max = max.max(s);
}
max.width += (theme_cache.margin_left + theme_cache.margin_right);
@@ -56,6 +51,25 @@ Size2 MarginContainer::get_minimum_size() const {
return max;
}
Size2 MarginContainer::get_desired_size() const {
Size2 ds;
for (int i = 0; i < get_child_count(); i++) {
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
if (!c) {
continue;
}
Size2 s = c->get_desired_size();
ds = ds.max(s);
}
ds.width += (theme_cache.margin_left + theme_cache.margin_right);
ds.height += (theme_cache.margin_top + theme_cache.margin_bottom);
return ds;
}
Size2 MarginContainer::get_inner_combined_maximum_size() const {
Size2 ms = Container::get_inner_combined_maximum_size();
+1
View File
@@ -48,6 +48,7 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Size2 get_inner_combined_maximum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+18
View File
@@ -50,6 +50,24 @@ Size2 PanelContainer::get_minimum_size() const {
return ms;
}
Size2 PanelContainer::get_desired_size() const {
Size2 ds;
for (int i = 0; i < get_child_count(); i++) {
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
if (!c) {
continue;
}
Size2 minsize = c->get_desired_size();
ds = ds.max(minsize);
}
if (theme_cache.panel_style.is_valid()) {
ds += theme_cache.panel_style->get_minimum_size();
}
return ds;
}
Size2 PanelContainer::get_inner_combined_maximum_size() const {
Size2 ms = Container::get_inner_combined_maximum_size();
+1
View File
@@ -45,6 +45,7 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Size2 get_inner_combined_maximum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+10 -2
View File
@@ -40,7 +40,7 @@
#include "servers/display/accessibility_server.h"
#include "servers/display/display_server.h"
Size2 ScrollContainer::get_minimum_size() const {
Size2 ScrollContainer::_get_minimum_size(bool p_use_desired_sizes) const {
// Calculated in this function, as it needs to traverse all child controls once to calculate;
// and needs to be calculated before being used by `_update_scrollbars()`.
largest_child_min_size = Size2();
@@ -51,7 +51,7 @@ Size2 ScrollContainer::get_minimum_size() const {
continue;
}
Size2 child_min_size = c->get_bound_minimum_size();
Size2 child_min_size = p_use_desired_sizes ? c->get_bound_desired_size() : c->get_bound_minimum_size();
largest_child_min_size = largest_child_min_size.max(child_min_size);
}
@@ -93,6 +93,14 @@ Size2 ScrollContainer::get_minimum_size() const {
return min_size;
}
Size2 ScrollContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 ScrollContainer::get_desired_size() const {
return _get_minimum_size(true);
}
Size2 ScrollContainer::get_inner_combined_maximum_size() const {
Size2 ms = Container::get_inner_combined_maximum_size();
+3
View File
@@ -117,8 +117,11 @@ private:
bool focus_border_is_drawn = false;
bool child_has_focus();
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
Size2 get_minimum_size() const override;
Size2 get_desired_size() const override;
Size2 get_inner_combined_maximum_size() const override;
void _gui_focus_changed(Control *p_control);
+10 -2
View File
@@ -1073,7 +1073,7 @@ void SplitContainer::_update_draggers() {
}
}
Size2 SplitContainer::get_minimum_size() const {
Size2 SplitContainer::_get_minimum_size(bool p_use_desired_sizes) const {
const int sep = _get_separation();
const int axis = vertical ? 1 : 0;
const int other_axis = vertical ? 0 : 1;
@@ -1085,7 +1085,7 @@ Size2 SplitContainer::get_minimum_size() const {
}
for (const Control *child : valid_children) {
const Size2 min_size = child->get_bound_minimum_size();
const Size2 min_size = p_use_desired_sizes ? child->get_bound_desired_size() : child->get_bound_minimum_size();
minimum[axis] += (int)min_size[axis];
minimum[other_axis] = (int)MAX(minimum[other_axis], min_size[other_axis]);
}
@@ -1093,6 +1093,14 @@ Size2 SplitContainer::get_minimum_size() const {
return minimum;
}
Size2 SplitContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 SplitContainer::get_desired_size() const {
return _get_minimum_size(true);
}
void SplitContainer::_validate_property(PropertyInfo &p_property) const {
if (is_fixed && p_property.name == "vertical") {
p_property.usage = PROPERTY_USAGE_NONE;
+3
View File
@@ -169,6 +169,8 @@ private:
void _update_nested_ancestors(bool p_remove = false);
void _update_all_nested_descendents(Control *p_control, Control *p_first_child = nullptr);
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
bool is_fixed = false;
@@ -207,6 +209,7 @@ public:
bool is_dragging_enabled() const;
virtual Size2 get_minimum_size() const override;
virtual Size2 get_desired_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
+94 -21
View File
@@ -50,8 +50,6 @@ static inline Color _select_color(const Color &p_override_color, const Color &p_
Size2 TabBar::get_minimum_size() const {
Size2 ms;
Size2 combined_max = get_combined_maximum_size();
int computed_max_width = 0;
bool stop_adding = combined_max.width < 0;
int buttons_size = get_tab_count() > 1 ? theme_cache.decrement_icon->get_width() + theme_cache.increment_icon->get_width() : 0;
@@ -120,31 +118,18 @@ Size2 TabBar::get_minimum_size() const {
ms.width += theme_cache.tab_separation;
}
if (!stop_adding) {
int buttons_eff_size = clip_tabs ? (i != tabs.size() - 1 ? buttons_size : 0) : 0;
if (computed_max_width + ms.width - ofs + buttons_eff_size >= combined_max.width) {
stop_adding = true;
} else {
computed_max_width += ms.width - ofs;
}
}
if (ms.width - ofs > max_tab_width) {
max_tab_width = ms.width - ofs;
}
}
if (clip_tabs) {
int clip_min_width = max_tab_width + buttons_size;
ms.width = max_tab_width + buttons_size;
if (combined_max.width >= 0) {
int fitted_width = stop_adding ? (computed_max_width + buttons_size) : computed_max_width;
ms.width = MAX(clip_min_width, fitted_width);
ms.width = MIN(ms.width, int(combined_max.width));
} else {
ms.width = clip_min_width;
}
} else if (combined_max.width >= 0) {
ms.width = stop_adding ? combined_max.width : MIN(computed_max_width, combined_max.width);
ms.width = MIN(ms.width, int(combined_max.width));
}
return ms;
@@ -507,10 +492,21 @@ void TabBar::_notification(int p_what) {
queue_accessibility_update();
queue_redraw();
update_minimum_size();
[[fallthrough]];
}
int ofs_old = offset;
int max_old = max_drawn_tab;
_update_cache();
_ensure_no_over_offset();
if (scroll_to_selected && (offset != ofs_old || max_drawn_tab != max_old)) {
ensure_tab_visible(current);
}
update_desired_size();
update_minimum_size();
} break;
case NOTIFICATION_RESIZED: {
int ofs_old = offset;
int max_old = max_drawn_tab;
@@ -813,6 +809,7 @@ void TabBar::set_tab_count(int p_count) {
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
notify_property_list_changed();
}
@@ -943,6 +940,7 @@ void TabBar::set_tab_title(int p_tab, const String &p_title) {
}
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -994,6 +992,7 @@ void TabBar::set_tab_language(int p_tab, const String &p_language) {
}
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
}
}
@@ -1018,6 +1017,7 @@ void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ensure_tab_visible(current);
}
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -1041,6 +1041,7 @@ void TabBar::set_tab_icon_max_width(int p_tab, int p_width) {
ensure_tab_visible(current);
}
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -1096,6 +1097,7 @@ void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
}
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -1120,6 +1122,7 @@ void TabBar::set_tab_hidden(int p_tab, bool p_hidden) {
}
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -1153,6 +1156,7 @@ void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ensure_tab_visible(current);
}
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -1232,7 +1236,7 @@ void TabBar::_update_cache(bool p_update_hover) {
int combined_max_width = combined_max.width >= 0 ? int(combined_max.width) - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width() : INT_MAX;
int effective_max_width = max_width > 0 ? MIN(max_width, combined_max_width) : combined_max_width;
int limit = combined_max.width > 0 ? combined_max.width : get_size().width;
int limit = combined_max.width > 0 ? MIN(combined_max.width, get_size().width) : get_size().width;
int limit_minus_buttons = limit - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
int w = 0;
@@ -1323,6 +1327,68 @@ void TabBar::_update_cache(bool p_update_hover) {
}
}
Size2 TabBar::get_desired_size() const {
if (!clip_tabs || tabs.is_empty()) {
return Size2();
}
Size2 combined_max = get_combined_maximum_size();
if (combined_max.width < 0) {
return Size2();
}
int buttons_size = tabs.size() > 1 ? theme_cache.decrement_icon->get_width() + theme_cache.increment_icon->get_width() : 0;
int limit = int(combined_max.width);
int limit_minus_buttons = limit - buttons_size;
int w = 0;
bool overflowed = false;
// First pass: check if all tabs fit without buttons.
for (int i = offset; i < tabs.size(); i++) {
if (tabs[i].hidden) {
continue;
}
int next_w = w + tabs[i].size_cache;
if (i > offset) {
next_w += theme_cache.tab_separation;
}
if (next_w > limit) {
overflowed = true;
break;
}
w = next_w;
}
// If buttons are needed, recompute against the reduced limit.
if (offset > 0 || overflowed) {
w = 0;
for (int i = offset; i < tabs.size(); i++) {
if (tabs[i].hidden) {
continue;
}
int next_w = w + tabs[i].size_cache;
if (i > offset) {
next_w += theme_cache.tab_separation;
}
if (next_w > limit_minus_buttons) {
break;
}
w = next_w;
}
}
int desired = (offset > 0 || overflowed) ? w + buttons_size : w;
desired = MIN(desired, limit);
return Size2(desired, 0);
}
void TabBar::_hover_switch_timeout() {
set_current_tab(hover);
}
@@ -1352,6 +1418,7 @@ void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
}
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
if (!deselect_enabled && tabs.size() == 1) {
@@ -1383,6 +1450,7 @@ void TabBar::clear_tabs() {
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
notify_property_list_changed();
}
@@ -1445,6 +1513,7 @@ void TabBar::remove_tab(int p_idx) {
queue_accessibility_update();
queue_redraw();
update_desired_size();
update_minimum_size();
notify_property_list_changed();
@@ -1653,6 +1722,7 @@ void TabBar::_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_in
}
queue_accessibility_update();
update_desired_size();
update_minimum_size();
}
@@ -1733,6 +1803,7 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) {
ensure_tab_visible(current);
}
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -1988,6 +2059,7 @@ void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
ensure_tab_visible(current);
}
queue_redraw();
update_desired_size();
update_minimum_size();
}
@@ -2010,6 +2082,7 @@ void TabBar::set_max_tab_width(int p_width) {
ensure_tab_visible(current);
}
queue_redraw();
update_desired_size();
update_minimum_size();
}
+1
View File
@@ -334,6 +334,7 @@ public:
Rect2 get_tab_rect(int p_tab) const;
Size2 get_minimum_size() const override;
Size2 get_desired_size() const override;
TabBar();
};
+17 -4
View File
@@ -266,6 +266,7 @@ void TabContainer::_on_theme_changed() {
_repaint();
} else {
update_minimum_size();
update_desired_size();
}
queue_redraw();
@@ -933,6 +934,7 @@ void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) {
tab_bar->set_tab_disabled(p_tab, p_disabled);
_update_margins();
update_desired_size();
if (!get_clip_tabs()) {
update_minimum_size();
}
@@ -958,6 +960,7 @@ void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
child->hide();
_update_margins();
update_desired_size();
if (!get_clip_tabs()) {
update_minimum_size();
}
@@ -987,16 +990,16 @@ Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const {
return tab_bar->get_tab_button_icon(p_tab);
}
Size2 TabContainer::get_minimum_size() const {
Size2 TabContainer::_get_minimum_size(bool p_use_desired_sizes) const {
Size2 ms;
if (tabs_visible) {
ms = tab_bar->get_minimum_size();
ms = p_use_desired_sizes ? tab_bar->get_bound_desired_size() : tab_bar->get_minimum_size();
ms.width += theme_cache.tabbar_style->get_margin(SIDE_LEFT) + theme_cache.tabbar_style->get_margin(SIDE_RIGHT);
ms.height += theme_cache.tabbar_style->get_margin(SIDE_TOP) + theme_cache.tabbar_style->get_margin(SIDE_BOTTOM);
if (get_popup()) {
ms.width += popup_button->get_minimum_size().x;
ms.width += p_use_desired_sizes ? popup_button->get_bound_desired_size().x : popup_button->get_minimum_size().x;
}
if (theme_cache.side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
@@ -1014,7 +1017,7 @@ Size2 TabContainer::get_minimum_size() const {
continue;
}
Size2 cms = c->get_bound_minimum_size();
Size2 cms = p_use_desired_sizes ? c->get_bound_desired_size() : c->get_bound_minimum_size();
largest_child_min_size = largest_child_min_size.max(cms);
}
ms.height += largest_child_min_size.height;
@@ -1027,6 +1030,14 @@ Size2 TabContainer::get_minimum_size() const {
return ms;
}
Size2 TabContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 TabContainer::get_desired_size() const {
return _get_minimum_size(true);
}
Size2 TabContainer::get_inner_combined_maximum_size() const {
Size2 ms = Container::get_inner_combined_maximum_size();
@@ -1105,6 +1116,7 @@ void TabContainer::set_popup(Node *p_popup) {
popup_button->set_visible(popup != nullptr);
_update_margins();
update_minimum_size();
update_desired_size();
}
}
@@ -1155,6 +1167,7 @@ void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) {
use_hidden_tabs_for_min_size = p_use_hidden_tabs;
update_minimum_size();
update_desired_size();
}
bool TabContainer::get_use_hidden_tabs_for_min_size() const {
+3
View File
@@ -152,6 +152,8 @@ private:
void _popup_button_hovered(bool p_hover);
void _popup_button_pressed();
Size2 _get_minimum_size(bool p_use_desired_sizes) const;
protected:
bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); }
bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); }
@@ -236,6 +238,7 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Size2 get_inner_combined_maximum_size() const override;
virtual Size2 get_desired_size() const override;
void set_popup(Node *p_popup);
Popup *get_popup() const;
+3 -1
View File
@@ -32,6 +32,8 @@
TEST_FORCE_LINK(test_label)
#include "modules/modules_enabled.gen.h" // IWYU pragma: keep. Needed for MODULE_TEXT_SERVER_FB_ENABLED and MODULE_TEXT_SERVER_ADV_ENABLED definitions.
#if defined(MODULE_TEXT_SERVER_FB_ENABLED) || defined(MODULE_TEXT_SERVER_ADV_ENABLED)
#include "scene/gui/label.h"
@@ -139,7 +141,7 @@ TEST_CASE("[SceneTree][Label] Sizing") {
real_t max_width = 50;
real_t min_width = 25;
test_label->set_custom_maximum_size(Size2(max_width, 0));
test_label->set_custom_maximum_size(Size2(max_width, -1));
test_label->set_custom_minimum_size(Size2(min_width, 0));
test_label->set_text("This is a long text that should be wrapped and exceeds minimum size.");
SceneTree::get_singleton()->process(0);