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.
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
6
7#pragma once
8
9#include "widget.hpp"
10#include "row_column_delegate.hpp"
11#include "grid_layout.hpp"
12#include "../GUI/theme.hpp"
13#include "../geometry/axis.hpp"
14#include <memory>
15
16namespace hi::inline v1 {
17
36template<axis Axis>
37class row_column_widget final : public widget {
38public:
39 static_assert(Axis == axis::horizontal or Axis == axis::vertical);
40
41 using super = widget;
43 static constexpr hi::axis axis = Axis;
44
46 {
47 if (auto delegate = _delegate.lock()) {
48 delegate->deinit(*this);
49 }
50 }
51
59 row_column_widget(gui_window& window, widget *parent, std::weak_ptr<delegate_type> delegate = {}) noexcept :
60 super(window, parent), _delegate(std::move(delegate))
61 {
62 hi_axiom(is_gui_thread());
63
64 if (parent) {
65 semantic_layer = parent->semantic_layer;
66 }
67 if (auto d = _delegate.lock()) {
68 d->init(*this);
69 }
70 }
71
84 template<typename Widget, typename... Args>
85 Widget& make_widget(Args&&...args)
86 {
87 auto tmp = std::make_unique<Widget>(window, this, std::forward<Args>(args)...);
88 auto& ref = *tmp;
89 _children.push_back(std::move(tmp));
90 request_reconstrain();
91 return ref;
92 }
93
96 void clear() noexcept
97 {
98 hi_axiom(is_gui_thread());
99 _children.clear();
100 request_reconstrain();
101 }
102
104 [[nodiscard]] generator<widget *> children() const noexcept override
105 {
106 for (hilet& child : _children) {
107 co_yield child.get();
108 }
109 }
110
111 widget_constraints const& set_constraints() noexcept override
112 {
113 _layout = {};
114
115 ssize_t index = 0;
116
117 auto minimum_thickness = 0.0f;
118 auto preferred_thickness = 0.0f;
119 auto maximum_thickness = 0.0f;
120 float margin_before_thickness = 0.0f;
121 float margin_after_thickness = 0.0f;
122 widget_baseline baseline = {};
123
124 _grid_layout.clear();
125 for (hilet& child : _children) {
126 update_constraints_for_child(
127 *child,
128 index++,
129 minimum_thickness,
130 preferred_thickness,
131 maximum_thickness,
132 margin_before_thickness,
133 margin_after_thickness,
134 baseline);
135 }
136 _grid_layout.commit_constraints();
137
138 hi_axiom(index == ssize(_children));
139
140 if constexpr (axis == axis::row) {
141 return _constraints = {
142 {_grid_layout.minimum(), minimum_thickness},
143 {_grid_layout.preferred(), preferred_thickness},
144 {_grid_layout.maximum(), maximum_thickness},
145 {_grid_layout.margin_before(),
146 margin_before_thickness,
147 _grid_layout.margin_after(),
148 margin_after_thickness},
149 baseline};
150 } else {
151 return _constraints = {
152 {minimum_thickness, _grid_layout.minimum()},
153 {preferred_thickness, _grid_layout.preferred()},
154 {maximum_thickness, _grid_layout.maximum()},
155 {margin_before_thickness,
156 _grid_layout.margin_before(),
157 margin_after_thickness,
158 _grid_layout.margin_after()}};
159 }
160 }
161
162 void set_layout(widget_layout const& layout) noexcept override
163 {
164 if (compare_store(_layout, layout)) {
165 _grid_layout.layout(axis == axis::row ? layout.width() : layout.height());
166 }
167
168 ssize_t index = 0;
169 for (hilet& child : _children) {
170 update_layout_for_child(*child, index++, layout);
171 }
172
173 hi_axiom(index == ssize(_children));
174 }
175
176 void draw(draw_context const& context) noexcept override
177 {
178 if (*mode > widget_mode::invisible) {
179 for (hilet& child : _children) {
180 child->draw(context);
181 }
182 }
183 }
184
185 hitbox hitbox_test(point3 position) const noexcept override
186 {
187 hi_axiom(is_gui_thread());
188
189 if (*mode >= widget_mode::partial) {
190 auto r = hitbox{};
191 for (hilet& child : _children) {
192 r = child->hitbox_test_from_parent(position, r);
193 }
194 return r;
195 } else {
196 return {};
197 }
198 }
200private:
203 grid_layout _grid_layout;
204
205 void update_constraints_for_child(
206 widget& child,
207 ssize_t index,
208 float& minimum_thickness,
209 float& preferred_thickness,
210 float& maximum_thickness,
211 float& margin_before_thickness,
212 float& margin_after_thickness,
213 widget_baseline& baseline) noexcept
214 {
215 hi_axiom(is_gui_thread());
216
217 hilet& child_constraints = child.set_constraints();
218 if (axis == axis::row) {
219 _grid_layout.add_constraint(
220 index,
221 child_constraints.minimum.width(),
222 child_constraints.preferred.width(),
223 child_constraints.maximum.width(),
224 child_constraints.margins.left(),
225 child_constraints.margins.right());
226
227 inplace_max(minimum_thickness, child_constraints.minimum.height());
228 inplace_max(preferred_thickness, child_constraints.preferred.height());
229 inplace_max(maximum_thickness, child_constraints.maximum.height());
230 inplace_max(margin_before_thickness, child_constraints.margins.top());
231 inplace_max(margin_after_thickness, child_constraints.margins.bottom());
232 inplace_max(baseline, child_constraints.baseline);
233
234 } else {
235 _grid_layout.add_constraint(
236 index,
237 child_constraints.minimum.height(),
238 child_constraints.preferred.height(),
239 child_constraints.maximum.height(),
240 child_constraints.margins.top(),
241 child_constraints.margins.bottom(),
242 child_constraints.baseline);
243
244 inplace_max(minimum_thickness, child_constraints.minimum.width());
245 inplace_max(preferred_thickness, child_constraints.preferred.width());
246 inplace_max(maximum_thickness, child_constraints.maximum.width());
247 inplace_max(margin_before_thickness, child_constraints.margins.left());
248 inplace_max(margin_after_thickness, child_constraints.margins.right());
249 }
250 }
251
252 void update_layout_for_child(widget& child, ssize_t index, widget_layout const& context) const noexcept
253 {
254 hi_axiom(is_gui_thread());
255
256 hilet[child_position, child_length] = _grid_layout.get_position_and_size(index);
257
258 if (axis == axis::row) {
259 hilet child_rectangle = aarectangle{child_position, 0.0f, child_length, layout().height()};
260 // The baseline for a row widget is inherited from the context received from the parent.
261 child.set_layout(context.transform(child_rectangle, 0.0f));
262
263 } else {
264 hilet child_rectangle =
265 aarectangle{0.0f, layout().height() - child_position - child_length, layout().width(), child_length};
266 child.set_layout(context.transform(child_rectangle, 0.0f, _grid_layout.get_baseline(index)));
267 }
268 }
269};
270
274
278
279} // namespace hi::inline v1
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition required.hpp:162
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
STL namespace.
A return value for a generator-function.
Definition generator.hpp:28
Definition gui_window.hpp:39
A row/column widget lays out child widgets along a row or column.
Definition row_column_widget.hpp:37
void clear() noexcept
Remove and deallocate all child widgets.
Definition row_column_widget.hpp:96
Widget & make_widget(Args &&...args)
Add a widget directly to this grid-widget.
Definition row_column_widget.hpp:85
row_column_widget(gui_window &window, widget *parent, std::weak_ptr< delegate_type > delegate={}) noexcept
Constructs an empty row/column widget.
Definition row_column_widget.hpp:59
Definition row_column_delegate.hpp:16
An interactive graphical object as part of the user-interface.
Definition widget.hpp:39
int semantic_layer
The draw layer of the widget.
Definition widget.hpp:81
T move(T... args)