HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
row_column_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2020-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
9#pragma once
10
11#include "widget.hpp"
12#include "grid_layout.hpp"
13#include "../GUI/theme.hpp"
14#include "../geometry/axis.hpp"
15#include <memory>
16
17namespace hi { inline namespace v1 {
18
38template<axis Axis>
39class row_column_widget final : public widget {
40public:
41 static_assert(Axis == axis::horizontal or Axis == axis::vertical);
42
43 using super = widget;
44 static constexpr hi::axis axis = Axis;
45
47
53 {
54 hi_axiom(loop::main().on_thread());
55
56 if (parent) {
58 }
59 }
60
73 template<typename Widget, typename... Args>
74 Widget& make_widget(Args&&...args)
75 {
76 auto tmp = std::make_unique<Widget>(this, std::forward<Args>(args)...);
77 auto& ref = *tmp;
78 _children.push_back(std::move(tmp));
79 ++global_counter<"row_column_widget:make_widget:constrain">;
81 return ref;
82 }
83
86 void clear() noexcept
87 {
88 hi_axiom(loop::main().on_thread());
89 _children.clear();
90 ++global_counter<"row_column_widget:clear:constrain">;
92 }
93
95 [[nodiscard]] generator<widget *> children() const noexcept override
96 {
97 for (hilet& child : _children) {
98 co_yield child.get();
99 }
100 }
101
102 widget_constraints const& set_constraints(set_constraints_context const& context) noexcept override
103 {
104 _layout = {};
105
106 ssize_t index = 0;
107
108 auto minimum_thickness = 0.0f;
109 auto preferred_thickness = 0.0f;
110 auto maximum_thickness = 0.0f;
111 float margin_before_thickness = 0.0f;
112 float margin_after_thickness = 0.0f;
113 widget_baseline baseline = {};
114
115 _grid_layout.clear();
116 for (hilet& child : _children) {
117 update_constraints_for_child(
118 context,
119 *child,
120 index++,
121 minimum_thickness,
122 preferred_thickness,
123 maximum_thickness,
124 margin_before_thickness,
125 margin_after_thickness,
126 baseline);
127 }
128 _grid_layout.commit_constraints();
129
130 hi_assert(index == ssize(_children));
131
132 if constexpr (axis == axis::row) {
133 return _constraints = {
134 {_grid_layout.minimum(), minimum_thickness},
135 {_grid_layout.preferred(), preferred_thickness},
136 {_grid_layout.maximum(), maximum_thickness},
137 {_grid_layout.margin_before(),
138 margin_before_thickness,
139 _grid_layout.margin_after(),
140 margin_after_thickness},
141 baseline};
142 } else {
143 return _constraints = {
144 {minimum_thickness, _grid_layout.minimum()},
145 {preferred_thickness, _grid_layout.preferred()},
146 {maximum_thickness, _grid_layout.maximum()},
147 {margin_before_thickness,
148 _grid_layout.margin_before(),
149 margin_after_thickness,
150 _grid_layout.margin_after()}};
151 }
152 }
153
154 void set_layout(widget_layout const& context) noexcept override
155 {
156 if (compare_store(_layout, context)) {
157 _grid_layout.layout(axis == axis::row ? context.width() : context.height());
158 }
159
160 ssize_t index = 0;
161 for (hilet& child : _children) {
162 update_layout_for_child(*child, index++, context);
163 }
164
165 hi_assert(index == ssize(_children));
166 }
167
168 void draw(draw_context const& context) noexcept override
169 {
171 for (hilet& child : _children) {
172 child->draw(context);
173 }
174 }
175 }
176
177 hitbox hitbox_test(point3 position) const noexcept override
178 {
179 hi_axiom(loop::main().on_thread());
180
181 if (*mode >= widget_mode::partial) {
182 auto r = hitbox{};
183 for (hilet& child : _children) {
184 r = child->hitbox_test_from_parent(position, r);
185 }
186 return r;
187 } else {
188 return {};
189 }
190 }
192private:
194 grid_layout _grid_layout;
195
196 void update_constraints_for_child(
197 set_constraints_context const& context,
198 widget& child,
199 ssize_t index,
200 float& minimum_thickness,
201 float& preferred_thickness,
202 float& maximum_thickness,
203 float& margin_before_thickness,
204 float& margin_after_thickness,
205 widget_baseline& baseline) noexcept
206 {
207 hi_axiom(loop::main().on_thread());
208
209 hilet& child_constraints = child.set_constraints(context);
210 if (axis == axis::row) {
211 _grid_layout.add_constraint(
212 index,
213 child_constraints.minimum.width(),
214 child_constraints.preferred.width(),
215 child_constraints.maximum.width(),
216 child_constraints.margins.left(),
217 child_constraints.margins.right());
218
219 inplace_max(minimum_thickness, child_constraints.minimum.height());
220 inplace_max(preferred_thickness, child_constraints.preferred.height());
221 inplace_max(maximum_thickness, child_constraints.maximum.height());
222 inplace_max(margin_before_thickness, child_constraints.margins.top());
223 inplace_max(margin_after_thickness, child_constraints.margins.bottom());
224 inplace_max(baseline, child_constraints.baseline);
225
226 } else {
227 _grid_layout.add_constraint(
228 index,
229 child_constraints.minimum.height(),
230 child_constraints.preferred.height(),
231 child_constraints.maximum.height(),
232 child_constraints.margins.top(),
233 child_constraints.margins.bottom(),
234 child_constraints.baseline);
235
236 inplace_max(minimum_thickness, child_constraints.minimum.width());
237 inplace_max(preferred_thickness, child_constraints.preferred.width());
238 inplace_max(maximum_thickness, child_constraints.maximum.width());
239 inplace_max(margin_before_thickness, child_constraints.margins.left());
240 inplace_max(margin_after_thickness, child_constraints.margins.right());
241 }
242 }
243
244 void update_layout_for_child(widget& child, ssize_t index, widget_layout const& context) const noexcept
245 {
246 hi_axiom(loop::main().on_thread());
247
248 hilet[child_position, child_length] = _grid_layout.get_position_and_size(index);
249
250 if (axis == axis::row) {
251 auto x0 = context.left_to_right() ? child_position : layout().width() - child_position - child_length;
252
253 hilet child_rectangle = aarectangle{x0, 0.0f, child_length, layout().height()};
254 // The baseline for a row widget is inherited from the context received from the parent.
255 child.set_layout(context.transform(child_rectangle, 0.0f));
256
257 } else {
258 hilet child_rectangle =
259 aarectangle{0.0f, layout().height() - child_position - child_length, layout().width(), child_length};
260 child.set_layout(context.transform(child_rectangle, 0.0f, _grid_layout.get_baseline(index)));
261 }
262 }
263};
264
269
274
275}} // namespace hi::v1
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_assert(expression)
Assert if expression is true.
Definition assert.hpp:86
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
Defines widget.
@ window_reconstrain
Request that widget get constraint on the next frame.
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:15
The HikoGUI namespace.
Definition ascii.hpp:19
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition utility.hpp:173
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utility.hpp:196
void commit_constraints() noexcept
Commit all the constraints.
float maximum() const noexcept
The minimum size of the total grid_layout.
Definition grid_layout.hpp:129
void clear() noexcept
Clear the list of widgets in the layout.
Definition grid_layout.hpp:34
void layout(float size) noexcept
Layout the cells based on the total size.
float preferred() const noexcept
The minimum size of the total grid_layout.
Definition grid_layout.hpp:120
float minimum() const noexcept
The minimum size of the total grid_layout.
Definition grid_layout.hpp:111
A row/column widget lays out child widgets along a row or column.
Definition row_column_widget.hpp:39
Widget & make_widget(Args &&...args)
Add a widget directly to this grid-widget.
Definition row_column_widget.hpp:74
void clear() noexcept
Remove and deallocate all child widgets.
Definition row_column_widget.hpp:86
row_column_widget(widget *parent) noexcept
Constructs an empty row/column widget.
Definition row_column_widget.hpp:52
An interactive graphical object as part of the user-interface.
Definition widget.hpp:45
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget.hpp:181
int semantic_layer
The draw layer of the widget.
Definition widget.hpp:83
widget(widget *parent) noexcept
widget * parent
Pointer to the parent widget.
Definition widget.hpp:50
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:59
T move(T... args)