Skip to content

Commit b03e1b0

Browse files
committed
LibGUI: Add Widget shrink-to-fit layout flag
If this flag is enabled for a widget, it will be automatically sized based on its children. This only works for widgets using a layout. This allows you to put widgets inside each other without having to manually calculate how large the container should be. It's not the perfect API but it's a decent progression in ergonomics. :^)
1 parent ce7b09a commit b03e1b0

File tree

5 files changed

+90
-0
lines changed

5 files changed

+90
-0
lines changed

Libraries/LibGUI/BoxLayout.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,70 @@ BoxLayout::BoxLayout(Orientation orientation)
4444
"orientation", [this] { return m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"; }, nullptr);
4545
}
4646

47+
Gfx::IntSize BoxLayout::preferred_size() const
48+
{
49+
Gfx::IntSize size;
50+
size.set_primary_size_for_orientation(orientation(), preferred_primary_size());
51+
size.set_secondary_size_for_orientation(orientation(), preferred_secondary_size());
52+
return size;
53+
}
54+
55+
int BoxLayout::preferred_primary_size() const
56+
{
57+
int size = 0;
58+
59+
for (auto& entry : m_entries) {
60+
if (!entry.widget)
61+
continue;
62+
int min_size = entry.widget->min_size().primary_size_for_orientation(orientation());
63+
int max_size = entry.widget->max_size().primary_size_for_orientation(orientation());
64+
int preferred_primary_size = -1;
65+
if (entry.widget->is_shrink_to_fit() && entry.widget->layout()) {
66+
preferred_primary_size = entry.widget->layout()->preferred_size().primary_size_for_orientation(orientation());
67+
}
68+
int item_size = max(0, preferred_primary_size);
69+
item_size = max(min_size, item_size);
70+
item_size = min(max_size, item_size);
71+
size += item_size + spacing();
72+
}
73+
if (size > 0)
74+
size -= spacing();
75+
76+
if (orientation() == Gfx::Orientation::Horizontal)
77+
size += margins().left() + margins().right();
78+
else
79+
size += margins().top() + margins().bottom();
80+
81+
if (!size)
82+
return -1;
83+
return size;
84+
}
85+
86+
int BoxLayout::preferred_secondary_size() const
87+
{
88+
int size = 0;
89+
for (auto& entry : m_entries) {
90+
if (!entry.widget)
91+
continue;
92+
int min_size = entry.widget->min_size().secondary_size_for_orientation(orientation());
93+
int preferred_secondary_size = -1;
94+
if (entry.widget->is_shrink_to_fit() && entry.widget->layout()) {
95+
preferred_secondary_size = entry.widget->layout()->preferred_size().secondary_size_for_orientation(orientation());
96+
size = max(size, preferred_secondary_size);
97+
}
98+
size = max(min_size, size);
99+
}
100+
101+
if (orientation() == Gfx::Orientation::Horizontal)
102+
size += margins().top() + margins().bottom();
103+
else
104+
size += margins().left() + margins().right();
105+
106+
if (!size)
107+
return -1;
108+
return size;
109+
}
110+
47111
void BoxLayout::run(Widget& widget)
48112
{
49113
if (m_entries.is_empty())
@@ -71,6 +135,12 @@ void BoxLayout::run(Widget& widget)
71135
continue;
72136
auto min_size = entry.widget->min_size();
73137
auto max_size = entry.widget->max_size();
138+
139+
if (entry.widget->is_shrink_to_fit() && entry.widget->layout()) {
140+
auto preferred_size = entry.widget->layout()->preferred_size();
141+
min_size = max_size = preferred_size;
142+
}
143+
74144
items.append(Item { entry.widget.ptr(), min_size.primary_size_for_orientation(orientation()), max_size.primary_size_for_orientation(orientation()) });
75145
}
76146

Libraries/LibGUI/BoxLayout.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ class BoxLayout : public Layout {
4141
Gfx::Orientation orientation() const { return m_orientation; }
4242

4343
virtual void run(Widget&) override;
44+
virtual Gfx::IntSize preferred_size() const override;
4445

4546
protected:
4647
explicit BoxLayout(Gfx::Orientation);
4748

4849
private:
50+
int preferred_primary_size() const;
51+
int preferred_secondary_size() const;
52+
4953
Gfx::Orientation m_orientation;
5054
};
5155

Libraries/LibGUI/Layout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <LibCore/Object.h>
3333
#include <LibGUI/Forward.h>
3434
#include <LibGUI/Margins.h>
35+
#include <LibGfx/Forward.h>
3536

3637
namespace GUI {
3738

@@ -49,6 +50,7 @@ class Layout : public Core::Object {
4950
void remove_widget(Widget&);
5051

5152
virtual void run(Widget&) = 0;
53+
virtual Gfx::IntSize preferred_size() const = 0;
5254

5355
void notify_adopted(Badge<Widget>, Widget&);
5456
void notify_disowned(Badge<Widget>, Widget&);

Libraries/LibGUI/Widget.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ Widget::Widget()
105105
REGISTER_INT_PROPERTY("fixed_height", dummy_fixed_height, set_fixed_height);
106106
REGISTER_SIZE_PROPERTY("fixed_size", dummy_fixed_size, set_fixed_size);
107107

108+
REGISTER_BOOL_PROPERTY("shrink_to_fit", is_shrink_to_fit, set_shrink_to_fit);
109+
108110
REGISTER_INT_PROPERTY("x", x, set_x);
109111
REGISTER_INT_PROPERTY("y", y, set_y);
110112

@@ -1010,4 +1012,12 @@ bool Widget::has_focus_within() const
10101012
return window->focused_widget() == &effective_focus_widget || is_ancestor_of(*window->focused_widget());
10111013
}
10121014

1015+
void Widget::set_shrink_to_fit(bool b)
1016+
{
1017+
if (m_shrink_to_fit == b)
1018+
return;
1019+
m_shrink_to_fit = b;
1020+
invalidate_layout();
1021+
}
1022+
10131023
}

Libraries/LibGUI/Widget.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ class Widget : public Core::Object {
297297

298298
bool load_from_gml(const StringView&);
299299

300+
void set_shrink_to_fit(bool);
301+
bool is_shrink_to_fit() const { return m_shrink_to_fit; }
302+
300303
protected:
301304
Widget();
302305

@@ -367,6 +370,7 @@ class Widget : public Core::Object {
367370
bool m_enabled { true };
368371
bool m_updates_enabled { true };
369372
bool m_accepts_emoji_input { false };
373+
bool m_shrink_to_fit { false };
370374

371375
NonnullRefPtr<Gfx::PaletteImpl> m_palette;
372376

0 commit comments

Comments
 (0)