1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-22 18:55:07 +00:00

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. :^)
This commit is contained in:
Andreas Kling 2021-01-04 18:17:14 +01:00
parent ce7b09a3c5
commit b03e1b08b5
5 changed files with 90 additions and 0 deletions

View file

@ -44,6 +44,70 @@ BoxLayout::BoxLayout(Orientation orientation)
"orientation", [this] { return m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"; }, nullptr); "orientation", [this] { return m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"; }, nullptr);
} }
Gfx::IntSize BoxLayout::preferred_size() const
{
Gfx::IntSize size;
size.set_primary_size_for_orientation(orientation(), preferred_primary_size());
size.set_secondary_size_for_orientation(orientation(), preferred_secondary_size());
return size;
}
int BoxLayout::preferred_primary_size() const
{
int size = 0;
for (auto& entry : m_entries) {
if (!entry.widget)
continue;
int min_size = entry.widget->min_size().primary_size_for_orientation(orientation());
int max_size = entry.widget->max_size().primary_size_for_orientation(orientation());
int preferred_primary_size = -1;
if (entry.widget->is_shrink_to_fit() && entry.widget->layout()) {
preferred_primary_size = entry.widget->layout()->preferred_size().primary_size_for_orientation(orientation());
}
int item_size = max(0, preferred_primary_size);
item_size = max(min_size, item_size);
item_size = min(max_size, item_size);
size += item_size + spacing();
}
if (size > 0)
size -= spacing();
if (orientation() == Gfx::Orientation::Horizontal)
size += margins().left() + margins().right();
else
size += margins().top() + margins().bottom();
if (!size)
return -1;
return size;
}
int BoxLayout::preferred_secondary_size() const
{
int size = 0;
for (auto& entry : m_entries) {
if (!entry.widget)
continue;
int min_size = entry.widget->min_size().secondary_size_for_orientation(orientation());
int preferred_secondary_size = -1;
if (entry.widget->is_shrink_to_fit() && entry.widget->layout()) {
preferred_secondary_size = entry.widget->layout()->preferred_size().secondary_size_for_orientation(orientation());
size = max(size, preferred_secondary_size);
}
size = max(min_size, size);
}
if (orientation() == Gfx::Orientation::Horizontal)
size += margins().top() + margins().bottom();
else
size += margins().left() + margins().right();
if (!size)
return -1;
return size;
}
void BoxLayout::run(Widget& widget) void BoxLayout::run(Widget& widget)
{ {
if (m_entries.is_empty()) if (m_entries.is_empty())
@ -71,6 +135,12 @@ void BoxLayout::run(Widget& widget)
continue; continue;
auto min_size = entry.widget->min_size(); auto min_size = entry.widget->min_size();
auto max_size = entry.widget->max_size(); auto max_size = entry.widget->max_size();
if (entry.widget->is_shrink_to_fit() && entry.widget->layout()) {
auto preferred_size = entry.widget->layout()->preferred_size();
min_size = max_size = preferred_size;
}
items.append(Item { entry.widget.ptr(), min_size.primary_size_for_orientation(orientation()), max_size.primary_size_for_orientation(orientation()) }); items.append(Item { entry.widget.ptr(), min_size.primary_size_for_orientation(orientation()), max_size.primary_size_for_orientation(orientation()) });
} }

View file

@ -41,11 +41,15 @@ public:
Gfx::Orientation orientation() const { return m_orientation; } Gfx::Orientation orientation() const { return m_orientation; }
virtual void run(Widget&) override; virtual void run(Widget&) override;
virtual Gfx::IntSize preferred_size() const override;
protected: protected:
explicit BoxLayout(Gfx::Orientation); explicit BoxLayout(Gfx::Orientation);
private: private:
int preferred_primary_size() const;
int preferred_secondary_size() const;
Gfx::Orientation m_orientation; Gfx::Orientation m_orientation;
}; };

View file

@ -32,6 +32,7 @@
#include <LibCore/Object.h> #include <LibCore/Object.h>
#include <LibGUI/Forward.h> #include <LibGUI/Forward.h>
#include <LibGUI/Margins.h> #include <LibGUI/Margins.h>
#include <LibGfx/Forward.h>
namespace GUI { namespace GUI {
@ -49,6 +50,7 @@ public:
void remove_widget(Widget&); void remove_widget(Widget&);
virtual void run(Widget&) = 0; virtual void run(Widget&) = 0;
virtual Gfx::IntSize preferred_size() const = 0;
void notify_adopted(Badge<Widget>, Widget&); void notify_adopted(Badge<Widget>, Widget&);
void notify_disowned(Badge<Widget>, Widget&); void notify_disowned(Badge<Widget>, Widget&);

View file

@ -105,6 +105,8 @@ Widget::Widget()
REGISTER_INT_PROPERTY("fixed_height", dummy_fixed_height, set_fixed_height); REGISTER_INT_PROPERTY("fixed_height", dummy_fixed_height, set_fixed_height);
REGISTER_SIZE_PROPERTY("fixed_size", dummy_fixed_size, set_fixed_size); REGISTER_SIZE_PROPERTY("fixed_size", dummy_fixed_size, set_fixed_size);
REGISTER_BOOL_PROPERTY("shrink_to_fit", is_shrink_to_fit, set_shrink_to_fit);
REGISTER_INT_PROPERTY("x", x, set_x); REGISTER_INT_PROPERTY("x", x, set_x);
REGISTER_INT_PROPERTY("y", y, set_y); REGISTER_INT_PROPERTY("y", y, set_y);
@ -1010,4 +1012,12 @@ bool Widget::has_focus_within() const
return window->focused_widget() == &effective_focus_widget || is_ancestor_of(*window->focused_widget()); return window->focused_widget() == &effective_focus_widget || is_ancestor_of(*window->focused_widget());
} }
void Widget::set_shrink_to_fit(bool b)
{
if (m_shrink_to_fit == b)
return;
m_shrink_to_fit = b;
invalidate_layout();
}
} }

View file

@ -297,6 +297,9 @@ public:
bool load_from_gml(const StringView&); bool load_from_gml(const StringView&);
void set_shrink_to_fit(bool);
bool is_shrink_to_fit() const { return m_shrink_to_fit; }
protected: protected:
Widget(); Widget();
@ -367,6 +370,7 @@ private:
bool m_enabled { true }; bool m_enabled { true };
bool m_updates_enabled { true }; bool m_updates_enabled { true };
bool m_accepts_emoji_input { false }; bool m_accepts_emoji_input { false };
bool m_shrink_to_fit { false };
NonnullRefPtr<Gfx::PaletteImpl> m_palette; NonnullRefPtr<Gfx::PaletteImpl> m_palette;