26 observer<int> content_width;
27 observer<int> content_height;
28 observer<int> aperture_width;
29 observer<int> aperture_height;
30 observer<int> offset_x;
31 observer<int> offset_y;
41 _content_width_cbt = content_width.subscribe([&](
auto...) {
42 ++global_counter<
"scroll_aperture_widget:content_width:relayout">;
45 _content_height_cbt = content_height.subscribe([&](
auto...) {
46 ++global_counter<
"scroll_aperture_widget:content_height:relayout">;
49 _aperture_width_cbt = aperture_width.subscribe([&](
auto...) {
50 ++global_counter<
"scroll_aperture_widget:aperture_width:relayout">;
53 _aperture_height_cbt = aperture_height.subscribe([&](
auto...) {
54 ++global_counter<
"scroll_aperture_widget:aperture_height:relayout">;
57 _offset_x_cbt = offset_x.subscribe([&](
auto...) {
58 ++global_counter<
"scroll_aperture_widget:offset_x:relayout">;
61 _offset_y_cbt = offset_y.subscribe([&](
auto...) {
62 ++global_counter<
"scroll_aperture_widget:offset_y:relayout">;
65 _minimum_cbt =
minimum.subscribe([&](
auto...) {
66 ++global_counter<
"scroll_aperture_widget:minimum:reconstrain">;
71 template<
typename Widget,
typename... Args>
72 Widget& make_widget(Args&&...args)
noexcept
77 auto tmp = std::make_unique<Widget>(
this, std::forward<Args>(args)...);
83 [[nodiscard]]
bool x_axis_scrolls()
const noexcept
85 return *content_width > *aperture_width;
88 [[nodiscard]]
bool y_axis_scrolls()
const noexcept
90 return *content_height > *aperture_height;
94 [[nodiscard]] generator<widget const &> children(
bool include_invisible)
const noexcept override
102 _content_constraints = _content->update_constraints();
105 auto aperture_constraints = _content_constraints;
106 aperture_constraints.minimum =
extent2i{0, 0};
108 return aperture_constraints.internalize_margins().constrain(*
minimum, *
maximum);
111 void set_layout(
widget_layout const& context)
noexcept override
114 aperture_width = context.width() - _content_constraints.margins.left() - _content_constraints.margins.right();
115 aperture_height = context.height() - _content_constraints.margins.bottom() - _content_constraints.margins.top();
119 content_width = *aperture_width < _content_constraints.preferred.
width() ? _content_constraints.preferred.
width() :
121 content_height = *aperture_height < _content_constraints.preferred.
height() ?
122 _content_constraints.preferred.
height() :
127 hilet offset_x_max =
std::max(*content_width - *aperture_width, 0);
128 hilet offset_y_max =
std::max(*content_height - *aperture_height, 0);
129 offset_x = std::clamp(*offset_x, 0, offset_x_max);
130 offset_y = std::clamp(*offset_y, 0, offset_y_max);
135 _content_constraints,
137 -*offset_x + _content_constraints.margins.left(),
138 -*offset_y + _content_constraints.margins.bottom(),
141 theme().baseline_adjustment()};
145 _content->set_layout(context.
transform(_content_shape, 1.0f, context.rectangle()));
148 void draw(
draw_context const& context)
noexcept override
151 _content->draw(context);
155 [[nodiscard]] hitbox hitbox_test(point2i position)
const noexcept override
160 auto r = _content->hitbox_test_from_parent(position);
162 if (
layout().contains(position)) {
163 r =
std::max(r, hitbox{
id, _layout.elevation});
172 bool handle_event(
gui_event const& event)
noexcept override
176 if (event == gui_event_type::mouse_wheel) {
179 hilet max_offset_x =
std::max(0, *content_width - *aperture_width);
180 hilet max_offset_y =
std::max(0, *content_height - *aperture_height);
182 offset_x = std::clamp(new_offset_x, 0, max_offset_x);
183 offset_y = std::clamp(new_offset_y, 0, max_offset_y);
184 ++global_counter<
"scroll_aperture_widget:mouse_wheel:relayout">;
195 auto safe_rectangle = intersect(_layout.rectangle(), _layout.clipping_rectangle);
199 if (safe_rectangle.width() > theme().margin<int>() * 2 and safe_rectangle.height() > theme().margin<int>() * 2) {
203 safe_rectangle = safe_rectangle - theme().margin<
int>();
205 if (to_show.right() > safe_rectangle.right()) {
206 delta_x = to_show.right() - safe_rectangle.right();
207 }
else if (to_show.left() < safe_rectangle.left()) {
208 delta_x = to_show.left() - safe_rectangle.left();
211 if (to_show.top() > safe_rectangle.top()) {
212 delta_y = to_show.top() - safe_rectangle.top();
213 }
else if (to_show.bottom() < safe_rectangle.bottom()) {
214 delta_y = to_show.bottom() - safe_rectangle.bottom();
236 decltype(content_width)::callback_token _content_width_cbt;
237 decltype(content_height)::callback_token _content_height_cbt;
238 decltype(aperture_width)::callback_token _aperture_width_cbt;
239 decltype(aperture_height)::callback_token _aperture_height_cbt;
240 decltype(offset_x)::callback_token _offset_x_cbt;
241 decltype(offset_y)::callback_token _offset_y_cbt;
242 decltype(
minimum)::callback_token _minimum_cbt;