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 "../GUI/gui_window.hpp"
15#include "../geometry/axis.hpp"
16
17namespace hi { inline namespace v1 {
18
47template<axis Axis = axis::both, bool ControlsWindow = false>
48class scroll_widget final : public widget {
49public:
50 using super = widget;
51
52 static constexpr hi::axis axis = Axis;
53 static constexpr bool controls_window = ControlsWindow;
54
56
62 scroll_widget(gui_window& window, widget *parent) noexcept : super(window, parent)
63 {
64 hi_axiom(is_gui_thread());
65 hi_axiom(parent);
66
67 // The scroll-widget will not draw itself, only its selected content.
69
70 _aperture = std::make_unique<scroll_aperture_widget>(window, this);
71 _horizontal_scroll_bar = std::make_unique<horizontal_scroll_bar_widget>(
72 window, this, _aperture->content_width, _aperture->aperture_width, _aperture->offset_x);
73 _vertical_scroll_bar = std::make_unique<vertical_scroll_bar_widget>(
74 window, this, _aperture->content_height, _aperture->aperture_height, _aperture->offset_y);
75 }
76
86 template<typename Widget, typename... Args>
87 Widget& make_widget(Args&&...args) noexcept
88 {
89 return _aperture->make_widget<Widget>(std::forward<Args>(args)...);
90 }
91
93 [[nodiscard]] generator<widget *> children() const noexcept override
94 {
95 co_yield _aperture.get();
96 co_yield _vertical_scroll_bar.get();
97 co_yield _horizontal_scroll_bar.get();
98 }
99
100 widget_constraints const& set_constraints() noexcept override
101 {
102 _layout = {};
103 hilet aperture_constraints = _aperture->set_constraints();
104 hilet horizontal_constraints = _horizontal_scroll_bar->set_constraints();
105 hilet vertical_constraints = _vertical_scroll_bar->set_constraints();
106
107 _constraints = aperture_constraints;
108
109 // When there are scrollbars the widget minimum size becomes basically zero.
110 // However we should at least have enough room to fit in the scroll-bars length-wise.
111 if constexpr (any(axis & axis::horizontal)) {
112 _constraints.minimum.width() = horizontal_constraints.minimum.width();
113 inplace_max(_constraints.preferred.width(), horizontal_constraints.minimum.width());
114 inplace_max(_constraints.maximum.width(), horizontal_constraints.minimum.width());
115 }
116 if constexpr (any(axis & axis::vertical)) {
117 _constraints.minimum.height() = vertical_constraints.minimum.height();
118 inplace_max(_constraints.preferred.height(), vertical_constraints.minimum.height());
119 inplace_max(_constraints.maximum.height(), vertical_constraints.minimum.height());
120 }
121
122 // Make room for the thickness of the scroll-bars.
123 if constexpr (any(axis & axis::horizontal)) {
124 _constraints.minimum.height() += horizontal_constraints.preferred.height();
125 _constraints.preferred.height() += horizontal_constraints.preferred.height();
126 _constraints.maximum.height() += horizontal_constraints.preferred.height();
127 }
128 if constexpr (any(axis & axis::vertical)) {
129 _constraints.minimum.width() += vertical_constraints.preferred.width();
130 _constraints.preferred.width() += vertical_constraints.preferred.width();
131 _constraints.maximum.width() += vertical_constraints.preferred.width();
132 }
133 return _constraints;
134 }
135
136 void set_layout(widget_layout const& layout) noexcept override
137 {
138 if (compare_store(_layout, layout)) {
139 hilet horizontal_visible = _aperture->x_axis_scrolls() and any(axis & axis::horizontal);
140 hilet vertical_visible = _aperture->y_axis_scrolls() and any(axis & axis::vertical);
141 hilet both_visible = horizontal_visible and vertical_visible;
142
143 _horizontal_scroll_bar->mode = horizontal_visible ? widget_mode::enabled : widget_mode::invisible;
144 _vertical_scroll_bar->mode = vertical_visible ? widget_mode::enabled : widget_mode::invisible;
145
146 hilet vertical_scroll_bar_width = _vertical_scroll_bar->constraints().preferred.width();
147 hilet horizontal_scroll_bar_height = _horizontal_scroll_bar->constraints().preferred.height();
148
149 // The aperture size grows to fill the size of the layout.
150 hilet aperture_size = extent2{
151 vertical_visible ? layout.width() - vertical_scroll_bar_width : layout.width(),
152 horizontal_visible ? layout.height() - horizontal_scroll_bar_height : layout.height()};
153 hilet aperture_offset = point2{0.0f, horizontal_visible ? horizontal_scroll_bar_height : 0.0f};
154 _aperture_rectangle = aarectangle{aperture_offset, aperture_size};
155
156 // The length of the scroll-bar is the full length of the widget, or just the length of the aperture depending
157 // if the counter-part scroll-bar is visible.
158 hilet horizontal_scroll_bar_size =
159 extent2{both_visible ? aperture_size.width() : layout.width(), horizontal_scroll_bar_height};
160 hilet vertical_scroll_bar_size =
161 extent2{vertical_scroll_bar_width, both_visible ? aperture_size.height() : layout.height()};
162
163 _vertical_scroll_bar_rectangle = aarectangle{
164 point2{layout.width() - vertical_scroll_bar_size.width(), layout.height() - vertical_scroll_bar_size.height()},
165 vertical_scroll_bar_size};
166
167 _horizontal_scroll_bar_rectangle = aarectangle{point2{0.0f, 0.0f}, horizontal_scroll_bar_size};
168
169 if constexpr (controls_window) {
170 window.set_resize_border_priority(true, not vertical_visible, not horizontal_visible, true);
171 }
172 }
173
174 _aperture->set_layout(layout.transform(_aperture_rectangle));
175 if (*_vertical_scroll_bar->mode > widget_mode::invisible) {
176 _vertical_scroll_bar->set_layout(layout.transform(_vertical_scroll_bar_rectangle));
177 }
178 if (*_horizontal_scroll_bar->mode > widget_mode::invisible) {
179 _horizontal_scroll_bar->set_layout(layout.transform(_horizontal_scroll_bar_rectangle));
180 }
181 }
182
183 void draw(draw_context const& context) noexcept
184 {
186 _vertical_scroll_bar->draw(context);
187 _horizontal_scroll_bar->draw(context);
188 _aperture->draw(context);
189 }
190 }
191
192 [[nodiscard]] hitbox hitbox_test(point3 position) const noexcept override
193 {
194 hi_axiom(is_gui_thread());
195
196 if (*mode >= widget_mode::partial) {
197 auto r = _aperture->hitbox_test_from_parent(position);
198 r = _horizontal_scroll_bar->hitbox_test_from_parent(position, r);
199 r = _vertical_scroll_bar->hitbox_test_from_parent(position, r);
200
201 if (layout().contains(position)) {
202 r = std::max(r, hitbox{this, position});
203 }
204 return r;
205
206 } else {
207 return {};
208 }
209 }
210 // @endprivatesection
211private:
212 aarectangle _aperture_rectangle;
214
215 aarectangle _horizontal_scroll_bar_rectangle;
217
218 aarectangle _vertical_scroll_bar_rectangle;
220};
221
230template<bool ControlsWindow = false>
232
241template<bool ControlsWindow = false>
243
244}} // namespace hi::v1
#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
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utils.hpp:27
The HikoGUI namespace.
Definition ascii.hpp:19
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:48
scroll_widget(gui_window &window, widget *parent) noexcept
Constructs an empty scroll widget.
Definition scroll_widget.hpp:62
Widget & make_widget(Args &&...args) noexcept
Add a content widget directly to this scroll widget.
Definition scroll_widget.hpp:87
An interactive graphical object as part of the user-interface.
Definition widget.hpp:44
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget.hpp:198
int semantic_layer
The draw layer of the widget.
Definition widget.hpp:86
widget *const parent
Pointer to the parent widget.
Definition widget.hpp:53
widget(gui_window &window, widget *parent) noexcept
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:62
gui_window & window
Convenient reference to the Window.
Definition widget.hpp:48
The constraints of a widget.
Definition widget_constraints.hpp:26
constexpr widget_layout transform(aarectangle const &child_rectangle, float elevation, aarectangle new_clipping_rectangle, widget_baseline new_baseline=widget_baseline{}) const noexcept
Create a new widget_layout for the child widget.
Definition widget_layout.hpp:186
T max(T... args)