26 observer<float> content_width;
27 observer<float> content_height;
28 observer<float> aperture_width;
29 observer<float> aperture_height;
30 observer<float> offset_x;
31 observer<float> offset_y;
35 hi_axiom(is_gui_thread());
42 _content_width_cbt = content_width.subscribe([&](
auto...){
request_relayout(); });
43 _content_height_cbt = content_height.subscribe([&](
auto...){
request_relayout(); });
44 _aperture_width_cbt = aperture_width.subscribe([&](
auto...){
request_relayout(); });
45 _aperture_height_cbt = aperture_height.subscribe([&](
auto...){
request_relayout(); });
51 template<
typename Widget,
typename... Args>
52 Widget &make_widget(Args &&...args)
noexcept
54 hi_axiom(is_gui_thread());
55 hi_axiom(not _content);
57 auto tmp = std::make_unique<Widget>(
window,
this, std::forward<Args>(args)...);
63 [[nodiscard]]
bool x_axis_scrolls()
const noexcept
65 return *content_width > *aperture_width;
68 [[nodiscard]]
bool y_axis_scrolls()
const noexcept
70 return *content_height > *aperture_height;
74 [[nodiscard]] generator<widget *> children()
const noexcept override
76 co_yield _content.get();
84 hilet content_constraints = _content->set_constraints();
86 hilet minimum_size = extent2{
87 content_constraints.margins.left() + content_constraints.minimum.
width() + content_constraints.margins.right(),
88 content_constraints.margins.top() + content_constraints.minimum.height() + content_constraints.margins.bottom()};
89 hilet preferred_size = extent2{
90 content_constraints.margins.left() + content_constraints.preferred.
width() + content_constraints.margins.right(),
91 content_constraints.margins.top() + content_constraints.preferred.height() + content_constraints.margins.bottom()};
92 hilet maximum_size = extent2{
93 content_constraints.margins.left() + content_constraints.maximum.
width() + content_constraints.margins.right(),
94 content_constraints.margins.top() + content_constraints.maximum.height() + content_constraints.margins.bottom()};
96 return _constraints = {minimum_size, preferred_size, maximum_size, margins{}};
101 hilet content_constraints = _content->constraints();
102 hilet margins = content_constraints.margins;
104 if (compare_store(_layout,
layout)) {
105 hilet preferred_size = content_constraints.preferred;
107 aperture_width =
layout.width() - margins.left() - margins.right();
108 aperture_height =
layout.height() - margins.bottom() - margins.top();
112 content_width = *aperture_width < preferred_size.width() ? preferred_size.width() : *aperture_width;
113 content_height = *aperture_height < preferred_size.height() ? preferred_size.height() : *aperture_height;
117 hilet offset_x_max =
std::max(*content_width - *aperture_width, 0.0f);
118 hilet offset_y_max =
std::max(*content_height - *aperture_height, 0.0f);
119 offset_x = std::clamp(
std::round(*offset_x), 0.0f, offset_x_max);
120 offset_y = std::clamp(
std::round(*offset_y), 0.0f, offset_y_max);
124 _content_rectangle = {
125 -*offset_x + margins.left(), -*offset_y + margins.bottom(), *content_width, *content_height};
132 void draw(draw_context
const &context)
noexcept
135 _content->draw(context);
139 [[nodiscard]] hitbox hitbox_test(point3 position)
const noexcept override
141 hi_axiom(is_gui_thread());
144 auto r = _content->hitbox_test_from_parent(position);
146 if (
layout().contains(position)) {
147 r =
std::max(r, hitbox{
this, position});
156 bool handle_event(
gui_event const &event)
noexcept override
158 hi_axiom(is_gui_thread());
160 if (event == gui_event_type::mouse_wheel) {
161 hilet new_offset_x = *offset_x +
event.mouse().wheel_delta.x() *
theme().scale;
162 hilet new_offset_y = *offset_y +
event.mouse().wheel_delta.y() *
theme().scale;
163 hilet max_offset_x =
std::max(0.0f, *content_width - *aperture_width);
164 hilet max_offset_y =
std::max(0.0f, *content_height - *aperture_height);
166 offset_x = std::clamp(new_offset_x, 0.0f, max_offset_x);
167 offset_y = std::clamp(new_offset_y, 0.0f, max_offset_y);
177 auto safe_rectangle = intersect(_layout.rectangle(), _layout.clipping_rectangle);
178 float delta_x = 0.0f;
179 float delta_y = 0.0f;
181 if (safe_rectangle.width() >
theme().margin and safe_rectangle.height() >
theme().margin) {
185 safe_rectangle = safe_rectangle -
theme().margin;
187 if (to_show.right() > safe_rectangle.right()) {
188 delta_x = to_show.right() - safe_rectangle.right();
189 }
else if (to_show.left() < safe_rectangle.left()) {
190 delta_x = to_show.left() - safe_rectangle.left();
193 if (to_show.top() > safe_rectangle.top()) {
194 delta_y = to_show.top() - safe_rectangle.top();
195 }
else if (to_show.bottom() < safe_rectangle.bottom()) {
196 delta_y = to_show.bottom() - safe_rectangle.bottom();
206 parent->
scroll_to_show(bounding_rectangle(_layout.to_parent * translate2(delta_x, delta_y) * to_show));
211 aarectangle _content_rectangle;
213 decltype(content_width)::callback_token _content_width_cbt;
214 decltype(content_height)::callback_token _content_height_cbt;
215 decltype(aperture_width)::callback_token _aperture_width_cbt;
216 decltype(aperture_height)::callback_token _aperture_height_cbt;
217 decltype(offset_x)::callback_token _offset_x_cbt;
218 decltype(offset_y)::callback_token _offset_y_cbt;