26 constexpr static auto prefix = Name /
"aperture";
28 observer<float> content_width;
29 observer<float> content_height;
30 observer<float> aperture_width;
31 observer<float> aperture_height;
32 observer<float> offset_x;
33 observer<float> offset_y;
43 _content_width_cbt = content_width.subscribe([&](
auto...) {
44 ++global_counter<
"scroll_aperture_widget:content_width:relayout">;
47 _content_height_cbt = content_height.subscribe([&](
auto...) {
48 ++global_counter<
"scroll_aperture_widget:content_height:relayout">;
51 _aperture_width_cbt = aperture_width.subscribe([&](
auto...) {
52 ++global_counter<
"scroll_aperture_widget:aperture_width:relayout">;
55 _aperture_height_cbt = aperture_height.subscribe([&](
auto...) {
56 ++global_counter<
"scroll_aperture_widget:aperture_height:relayout">;
59 _offset_x_cbt = offset_x.subscribe([&](
auto...) {
60 ++global_counter<
"scroll_aperture_widget:offset_x:relayout">;
63 _offset_y_cbt = offset_y.subscribe([&](
auto...) {
64 ++global_counter<
"scroll_aperture_widget:offset_y:relayout">;
67 _minimum_cbt = minimum.subscribe([&](
auto...) {
68 ++global_counter<
"scroll_aperture_widget:minimum:reconstrain">;
73 template<
typename Widget,
typename... Args>
74 Widget& make_widget(Args&&...args)
noexcept
79 auto tmp = std::make_unique<Widget>(
this, std::forward<Args>(args)...);
85 [[nodiscard]]
bool x_axis_scrolls()
const noexcept
87 return *content_width > *aperture_width;
90 [[nodiscard]]
bool y_axis_scrolls()
const noexcept
92 return *content_height > *aperture_height;
96 [[nodiscard]] generator<widget const&> children(
bool include_invisible)
const noexcept override
103 _content_constraints = _content->update_constraints();
117 return aperture_constraints.constrain(*minimum, *maximum);
120 void set_layout(
widget_layout const& context)
noexcept override
123 aperture_width = context.width() - _content_constraints.margins.left() - _content_constraints.margins.right();
124 aperture_height = context.height() - _content_constraints.margins.bottom() - _content_constraints.margins.top();
128 content_width = *aperture_width < _content_constraints.preferred.
width() ? _content_constraints.preferred.
width() :
130 content_height = *aperture_height < _content_constraints.preferred.
height() ?
131 _content_constraints.preferred.
height() :
136 hilet offset_x_max =
std::max(*content_width - *aperture_width, 0.0f);
137 hilet offset_y_max =
std::max(*content_height - *aperture_height, 0.0f);
138 offset_x = std::clamp(*offset_x, 0.0f, offset_x_max);
139 offset_y = std::clamp(*offset_y, 0.0f, offset_y_max);
144 _content_constraints,
146 -*offset_x + _content_constraints.margins.left(),
147 -*offset_y + _content_constraints.margins.bottom(),
150 theme<prefix>.cap_height(
this)};
154 _content->set_layout(context.
transform(_content_shape, 1.0f, context.rectangle()));
160 _content->draw(context);
164 [[nodiscard]] hitbox hitbox_test(point2 position)
const noexcept override
169 auto r = _content->hitbox_test_from_parent(position);
171 if (layout.contains(position)) {
172 r =
std::max(r, hitbox{
id, layout.elevation});
181 bool handle_event(
gui_event const& event)
noexcept override
185 if (event == gui_event_type::mouse_wheel) {
186 hilet new_offset_x = *offset_x +
event.mouse().wheel_delta.x() * _scale;
187 hilet new_offset_y = *offset_y +
event.mouse().wheel_delta.y() * _scale;
188 hilet max_offset_x =
std::max(0.0f, *content_width - *aperture_width);
189 hilet max_offset_y =
std::max(0.0f, *content_height - *aperture_height);
191 offset_x = std::clamp(new_offset_x, 0.0f, max_offset_x);
192 offset_y = std::clamp(new_offset_y, 0.0f, max_offset_y);
193 ++global_counter<
"scroll_aperture_widget:mouse_wheel:relayout">;
204 auto safe_rectangle = intersect(layout.rectangle(), layout.clipping_rectangle);
209 {theme<prefix>.margin_left(
this),
210 theme<prefix>.margin_right(
this),
211 theme<prefix>.margin_top(
this),
212 theme<prefix>.margin_bottom(
this)});
213 if (safe_rectangle.width() > margin * 2.0f and safe_rectangle.height() > margin * 2.0f) {
217 safe_rectangle = safe_rectangle - margin;
219 if (to_show.right() > safe_rectangle.right()) {
220 delta_x = to_show.right() - safe_rectangle.right();
221 }
else if (to_show.left() < safe_rectangle.left()) {
222 delta_x = to_show.left() - safe_rectangle.left();
225 if (to_show.top() > safe_rectangle.top()) {
226 delta_y = to_show.top() - safe_rectangle.top();
227 }
else if (to_show.bottom() < safe_rectangle.bottom()) {
228 delta_y = to_show.bottom() - safe_rectangle.bottom();
250 decltype(content_width)::callback_token _content_width_cbt;
251 decltype(content_height)::callback_token _content_height_cbt;
252 decltype(aperture_width)::callback_token _aperture_width_cbt;
253 decltype(aperture_height)::callback_token _aperture_height_cbt;
254 decltype(offset_x)::callback_token _offset_x_cbt;
255 decltype(offset_y)::callback_token _offset_y_cbt;
256 decltype(minimum)::callback_token _minimum_cbt;