HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
scroll_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021-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 "scroll_bar_widget.hpp"
14#include "../geometry/axis.hpp"
15
16namespace hi { inline namespace v1 {
17
44template<axis Axis = axis::both>
45class scroll_widget final : public widget {
46public:
47 using super = widget;
48
49 static constexpr hi::axis axis = Axis;
50
52
58 {
59 hi_axiom(loop::main().on_thread());
61
62 // The scroll-widget will not draw itself, only its selected content.
64
65 _aperture = std::make_unique<scroll_aperture_widget>(this);
66 _horizontal_scroll_bar = std::make_unique<horizontal_scroll_bar_widget>(
67 this, _aperture->content_width, _aperture->aperture_width, _aperture->offset_x);
68 _vertical_scroll_bar = std::make_unique<vertical_scroll_bar_widget>(
69 this, _aperture->content_height, _aperture->aperture_height, _aperture->offset_y);
70 }
71
81 template<typename Widget, typename... Args>
82 Widget& make_widget(Args&&...args) noexcept
83 {
84 return _aperture->make_widget<Widget>(std::forward<Args>(args)...);
85 }
86
88 [[nodiscard]] generator<widget *> children() const noexcept override
89 {
90 co_yield _aperture.get();
91 co_yield _vertical_scroll_bar.get();
92 co_yield _horizontal_scroll_bar.get();
93 }
94
95 widget_constraints const& set_constraints(set_constraints_context const& context) noexcept override
96 {
97 _layout = {};
98 hilet aperture_constraints = _aperture->set_constraints(context);
99 hilet horizontal_constraints = _horizontal_scroll_bar->set_constraints(context);
100 hilet vertical_constraints = _vertical_scroll_bar->set_constraints(context);
101
102 _constraints = aperture_constraints;
103
104 // When there are scrollbars the widget minimum size becomes basically zero.
105 // However we should at least have enough room to fit in the scroll-bars length-wise.
106 if constexpr (to_bool(axis & axis::horizontal)) {
107 _constraints.minimum.width() = horizontal_constraints.minimum.width();
108 inplace_max(_constraints.preferred.width(), horizontal_constraints.minimum.width());
109 inplace_max(_constraints.maximum.width(), horizontal_constraints.minimum.width());
110 }
111 if constexpr (to_bool(axis & axis::vertical)) {
112 _constraints.minimum.height() = vertical_constraints.minimum.height();
113 inplace_max(_constraints.preferred.height(), vertical_constraints.minimum.height());
114 inplace_max(_constraints.maximum.height(), vertical_constraints.minimum.height());
115 }
116
117 // Make room for the thickness of the scroll-bars.
118 if constexpr (to_bool(axis & axis::horizontal)) {
119 _constraints.minimum.height() += horizontal_constraints.preferred.height();
120 _constraints.preferred.height() += horizontal_constraints.preferred.height();
121 _constraints.maximum.height() += horizontal_constraints.preferred.height();
122 }
123 if constexpr (to_bool(axis & axis::vertical)) {
124 _constraints.minimum.width() += vertical_constraints.preferred.width();
125 _constraints.preferred.width() += vertical_constraints.preferred.width();
126 _constraints.maximum.width() += vertical_constraints.preferred.width();
127 }
128 return _constraints;
129 }
130
131 void set_layout(widget_layout const& context) noexcept override
132 {
133 if (compare_store(_layout, context)) {
134 hilet horizontal_visible = _aperture->x_axis_scrolls() and to_bool(axis & axis::horizontal);
135 hilet vertical_visible = _aperture->y_axis_scrolls() and to_bool(axis & axis::vertical);
136 hilet both_visible = horizontal_visible and vertical_visible;
137
138 _horizontal_scroll_bar->mode = horizontal_visible ? widget_mode::enabled : widget_mode::invisible;
139 _vertical_scroll_bar->mode = vertical_visible ? widget_mode::enabled : widget_mode::invisible;
140
141 hilet vertical_scroll_bar_width = _vertical_scroll_bar->constraints().preferred.width();
142 hilet horizontal_scroll_bar_height = _horizontal_scroll_bar->constraints().preferred.height();
143
144 // The aperture size grows to fill the size of the layout.
145 hilet aperture_size = extent2{
146 vertical_visible ? context.width() - vertical_scroll_bar_width : context.width(),
147 horizontal_visible ? context.height() - horizontal_scroll_bar_height : context.height()};
148
149 hilet aperture_x = context.left_to_right() ? 0.0f : vertical_visible ? vertical_scroll_bar_width : 0.0f;
150 hilet aperture_y = horizontal_visible ? horizontal_scroll_bar_height : 0.0f;
151
152 hilet aperture_offset = point2{aperture_x, aperture_y};
153 _aperture_rectangle = aarectangle{aperture_offset, aperture_size};
154
155 // The length of the scroll-bar is the full length of the widget, or just the length of the aperture depending
156 // if the counter-part scroll-bar is visible.
157 hilet horizontal_scroll_bar_size =
158 extent2{both_visible ? aperture_size.width() : context.width(), horizontal_scroll_bar_height};
159 hilet vertical_scroll_bar_size =
160 extent2{vertical_scroll_bar_width, both_visible ? aperture_size.height() : context.height()};
161
162 hilet vertical_scroll_bar_x = context.left_to_right() ? context.width() - vertical_scroll_bar_size.width() : 0.0f;
163 hilet vertical_scroll_bar_y = context.height() - vertical_scroll_bar_size.height();
164 _vertical_scroll_bar_rectangle =
165 aarectangle{point2{vertical_scroll_bar_x, vertical_scroll_bar_y}, vertical_scroll_bar_size};
166
167 hilet horizontal_scroll_bar_x = context.left_to_right() ? 0.0f : vertical_visible ? vertical_scroll_bar_width : 0.0f;
168 hilet horizontal_scroll_bar_y = 0.0f;
169 _horizontal_scroll_bar_rectangle =
170 aarectangle{point2{horizontal_scroll_bar_x, horizontal_scroll_bar_y}, horizontal_scroll_bar_size};
171 }
172
173 _aperture->set_layout(context.transform(_aperture_rectangle));
174 if (*_vertical_scroll_bar->mode > widget_mode::invisible) {
175 _vertical_scroll_bar->set_layout(context.transform(_vertical_scroll_bar_rectangle));
176 }
177 if (*_horizontal_scroll_bar->mode > widget_mode::invisible) {
178 _horizontal_scroll_bar->set_layout(context.transform(_horizontal_scroll_bar_rectangle));
179 }
180 }
181
182 void draw(draw_context const& context) noexcept
183 {
185 _vertical_scroll_bar->draw(context);
186 _horizontal_scroll_bar->draw(context);
187 _aperture->draw(context);
188 }
189 }
190
191 [[nodiscard]] hitbox hitbox_test(point3 position) const noexcept override
192 {
193 hi_axiom(loop::main().on_thread());
194
195 if (*mode >= widget_mode::partial) {
196 auto r = _aperture->hitbox_test_from_parent(position);
197 r = _horizontal_scroll_bar->hitbox_test_from_parent(position, r);
198 r = _vertical_scroll_bar->hitbox_test_from_parent(position, r);
199
200 if (layout().contains(position)) {
201 r = std::max(r, hitbox{this, position});
202 }
203 return r;
204
205 } else {
206 return {};
207 }
208 }
209 // @endprivatesection
210private:
211 aarectangle _aperture_rectangle;
213
214 aarectangle _horizontal_scroll_bar_rectangle;
216
217 aarectangle _vertical_scroll_bar_rectangle;
219};
220
229
238
239}} // namespace hi::v1
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_assert_not_null(x)
Assert if an expression is not nullptr.
Definition assert.hpp:118
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
Defines widget.
Defines scroll_bar_widget.
Defines scroll_aperture_widget.
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
@ enabled
The widget is fully enabled.
DOXYGEN BUG.
Definition algorithm.hpp:15
The HikoGUI namespace.
Definition ascii.hpp:19
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utility.hpp:196
constexpr float & height() noexcept
Access the y-as-height element from the extent.
Definition extent.hpp:151
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:140
The scroll widget allows a content widget to be shown in less space than is required.
Definition scroll_widget.hpp:45
Widget & make_widget(Args &&...args) noexcept
Add a content widget directly to this scroll widget.
Definition scroll_widget.hpp:82
scroll_widget(widget *parent) noexcept
Constructs an empty scroll widget.
Definition scroll_widget.hpp:57
Definition set_constraints_context.hpp:15
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
The constraints of a widget.
Definition widget_constraints.hpp:26
T max(T... args)