ASW Lib
A.D.S. Games SDL Wrapper Library. A library targeted at Allegro4 users who want to switch to SDL3 and use modern c++.
Loading...
Searching...
No Matches
layout.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <vector>
5
7{
8 const std::size_t count = _last.size();
9 for (std::size_t i = 0; i < count; ++i) {
10 const std::size_t index = (_cursor + i) % count;
11 if (_last[index].id == id) {
12 _cursor = index + 1;
13 return &_last[index];
14 }
15 }
16 return nullptr;
17}
18
19float asw::ui::detail::ForcedSizes::natural(const Widget& w, float current) const
20{
21 const Entry* entry = find(w.id());
22 if (entry != nullptr && entry->forced == current) {
23 return entry->natural;
24 }
25 return current;
26}
27
28void asw::ui::detail::ForcedSizes::record(const Widget& w, float natural, float forced)
29{
30 _next.push_back({ .id = w.id(), .natural = natural, .forced = forced });
31}
32
34{
35 if (const Entry* entry = find(w.id())) {
36 _next.push_back(*entry);
37 }
38}
39
41{
42 _last.swap(_next);
43 _next.clear();
44 _cursor = 0;
45}
46
48{
49 const bool vertical = direction == Direction::Vertical;
50 const auto& area = transform;
51
52 // Along is the stacking axis, across the other one
53 float along = (vertical ? area.position.y : area.position.x) + padding;
54 const float across_start = (vertical ? area.position.x : area.position.y) + padding;
55 const float across_size = (vertical ? area.size.x : area.size.y) - (padding * 2.0F);
56
57 for (auto const& c : children()) {
58 if (!c->visible) {
59 _across.keep(*c);
60 continue;
61 }
62
63 c->measure(ctx);
64 auto& t = c->transform;
65 float& child_across_size = vertical ? t.size.x : t.size.y;
66 const float natural = _across.natural(*c, child_across_size);
67 child_across_size = align == Align::Stretch ? across_size : natural;
68 _across.record(*c, natural, child_across_size);
69
70 float across = across_start;
71 if (align == Align::Center) {
72 across += (across_size - child_across_size) / 2.0F;
73 } else if (align == Align::End) {
74 across += across_size - child_across_size;
75 }
76
77 if (vertical) {
78 t.position = { across, along };
79 } else {
80 t.position = { along, across };
81 }
82
83 c->layout(ctx);
84 along += (vertical ? t.size.y : t.size.x) + gap;
85 }
86
87 _across.finish();
88}
89
91{
92 const std::size_t cols = std::max<std::size_t>(columns, 1);
93 const float inner_w = transform.size.x - (padding * 2.0F);
94 const float cell_w
95 = std::max(0.0F, (inner_w - (gap * static_cast<float>(cols - 1))) / static_cast<float>(cols));
96
97 auto& shown = _shown;
98 auto& natural_h = _natural_h;
99 shown.clear();
100 natural_h.clear();
101 for (auto const& c : children()) {
102 if (c->visible) {
103 c->measure(ctx);
104 shown.push_back(c.get());
105 natural_h.push_back(_heights.natural(*c, c->transform.size.y));
106 } else {
107 _heights.keep(*c);
108 }
109 }
110
111 float y = transform.position.y + padding;
112 for (std::size_t row = 0; row * cols < shown.size(); ++row) {
113 const std::size_t first = row * cols;
114 const std::size_t last = std::min(first + cols, shown.size());
115
116 float h = row_height;
117 if (h <= 0.0F) {
118 for (std::size_t i = first; i < last; ++i) {
119 h = std::max(h, natural_h[i]);
120 }
121 }
122
123 for (std::size_t i = first; i < last; ++i) {
124 const auto col = static_cast<float>(i - first);
125 auto& t = shown[i]->transform;
126 t.position = { transform.position.x + padding + (col * (cell_w + gap)), y };
127 t.size = { cell_w, h };
128 _heights.record(*shown[i], natural_h[i], h);
129 shown[i]->layout(ctx);
130 }
131
132 y += h + gap;
133 }
134
135 _heights.finish();
136}
137
139{
140 const Widget* parent = w.parent;
141 if (parent == nullptr) {
142 return false;
143 }
144
145 const auto* stack = dynamic_cast<const Stack*>(parent);
146 const auto* grid = dynamic_cast<const Grid*>(parent);
147 const bool row = (stack != nullptr && stack->direction == Direction::Horizontal)
148 || (grid != nullptr && grid->columns > 1);
149 if (!row) {
150 return false;
151 }
152
153 return std::ranges::any_of(parent->children(), [&w](const std::unique_ptr<Widget>& c) {
154 return c.get() != &w && c->visible && c->enabled && c->focusable;
155 });
156}
Shared state for the UI system.
Definition context.h:97
Places children in a grid, filling each row left to right, e.g. a level select or a 2 by 2 menu.
Definition layout.h:144
void layout(Context &ctx) override
Place the children.
Definition layout.cpp:90
Places children one after another, top to bottom or left to right.
Definition layout.h:100
void layout(Context &ctx) override
Place the children.
Definition layout.cpp:47
Base class for all UI widgets.
Definition widget.h:43
const std::vector< std::unique_ptr< Widget > > & children() const
Get the child widgets. Change them with add_child, remove_child and clear_children,...
Definition widget.h:115
WidgetId id() const
Get the unique identifier for this widget.
Definition widget.h:75
Widget * parent
Pointer to the parent widget.
Definition widget.h:107
std::vector< Entry > _last
Definition layout.h:87
void keep(const Widget &w)
Keep last layout's record for a child skipped this layout, e.g. a hidden one.
Definition layout.cpp:33
void finish()
End the layout. Records not made or kept this layout are dropped.
Definition layout.cpp:40
const Entry * find(WidgetId id) const
Definition layout.cpp:6
float natural(const Widget &w, float current) const
The child's own size: its current size, unless that is still the size this layout forced on it last t...
Definition layout.cpp:19
void record(const Widget &w, float natural, float forced)
Record the size forced on a child this layout.
Definition layout.cpp:28
Layout widgets for the ASW UI module.
bool in_row_with_focusables(const Widget &w)
Whether a widget sits in a row with other widgets that can take focus: its parent is a horizontal Sta...
Definition layout.cpp:138
uint32_t WidgetId
Unique identifier type for widgets.
Definition widget.h:39
@ Stretch
Children fill the stack across.