11#include "../GUI/module.hpp"
12#include "../geometry/module.hpp"
13#include "../observer.hpp"
14#include "../utility/module.hpp"
21namespace hi {
inline namespace v1 {
32template<
axis Axis, fixed_string Name =
"">
36 constexpr static auto prefix = Name / (Axis == axis::horizontal ?
"hbar" :
"vbar");
41 observer<int> aperture;
42 observer<int> content;
46 forward_of<observer<int>>
auto&& content,
47 forward_of<observer<int>>
auto&& aperture,
48 forward_of<observer<int>>
auto&& offset) noexcept :
51 _content_cbt = this->content.subscribe([&](
auto...) {
52 ++global_counter<
"scroll_bar_widget:content:relayout">;
55 _aperture_cbt = this->aperture.subscribe([&](
auto...) {
56 ++global_counter<
"scroll_bar_widget:aperture:relayout">;
59 _offset_cbt = this->offset.subscribe([&](
auto...) {
60 ++global_counter<
"scroll_bar_widget:offset:relayout">;
77 hi_axiom(theme<prefix>.width(
this) >= theme<prefix / "slider">.width(
this));
78 hi_axiom(theme<prefix>.height(
this) >= theme<prefix / "slider">.height(
this));
79 if constexpr (
axis == axis::vertical) {
81 extent2i{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
82 extent2i{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
86 extent2i{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
87 extent2i{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
97 _slider_rectangle = {};
102 hilet slider_offset = narrow_cast<int>(
std::round(*offset * travel_vs_hidden_content_ratio()));
103 if constexpr (
axis == axis::vertical) {
104 hilet slider_width =
theme<prefix /
"slider">.width();
105 hilet x = (context.width() - slider_width) / 2;
107 _slider_rectangle =
aarectanglei{x, slider_offset, slider_width, slider_length()};
110 hilet slider_height =
theme < prefix /
"slider".height();
111 hilet y = (context.height() - slider_height) / 2;
113 _slider_rectangle =
aarectanglei{slider_offset, y, slider_length(), slider_height};
117 [[nodiscard]]
bool visible() const noexcept
119 return *aperture < *content;
126 draw_slider(context);
135 return {
id, layout.
elevation, hitbox_type::scroll_bar};
143 switch (event.type()) {
144 case gui_event_type::mouse_down:
145 if (event.mouse().cause.left_button) {
147 _offset_before_drag = *offset;
152 case gui_event_type::mouse_drag:
153 if (event.mouse().cause.left_button) {
156 hilet slider_movement =
157 narrow_cast<int>(
axis == axis::vertical ? event.drag_delta().y() :
event.drag_delta().x());
158 hilet content_movement = narrow_cast<int>(
std::round(slider_movement * hidden_content_vs_travel_ratio()));
159 hilet new_offset = _offset_before_drag + content_movement;
160 offset = clamp_offset(new_offset);
179 int _offset_before_drag;
181 typename decltype(content)::callback_token _content_cbt;
182 typename decltype(aperture)::callback_token _aperture_cbt;
183 typename decltype(offset)::callback_token _offset_cbt;
189 [[nodiscard]]
int clamp_offset(
int new_offset)
const noexcept
191 hilet scrollable_distance =
std::max(0, *content - *aperture);
192 return std::clamp(new_offset, 0, scrollable_distance);
195 [[nodiscard]]
int rail_length() const noexcept
198 return axis == axis::vertical ? layout.height() : layout.width();
201 [[nodiscard]]
int slider_length() const noexcept
206 auto length = axis == axis::vertical ?
theme<prefix /
"slider">.height(
this) :
theme<prefix /
"slider">.width(
this);
210 if (*content != 0 and *aperture <= *content) {
211 inplace_max(length, *aperture * rail_length() / *content);
219 [[nodiscard]]
int slider_travel_range() const noexcept
222 return rail_length() - slider_length();
227 [[nodiscard]]
int hidden_content() const noexcept
230 return *content - *aperture;
237 [[nodiscard]]
float hidden_content_vs_travel_ratio() const noexcept
241 hilet _slider_travel_range = slider_travel_range();
242 return _slider_travel_range != 0 ? narrow_cast<float>(hidden_content()) / _slider_travel_range : 0.0f;
249 [[nodiscard]]
float travel_vs_hidden_content_ratio() const noexcept
253 hilet _hidden_content = hidden_content();
254 return _hidden_content != 0 ? narrow_cast<float>(slider_travel_range()) / _hidden_content : 0.0f;
257 void draw_rails(widget_draw_context
const& context)
noexcept
262 theme<prefix>.background_color(
this),
263 theme<prefix>.border_color(
this),
264 theme<prefix>.border_width(
this),
265 theme<prefix>.corner_radius(
this));
268 void draw_slider(widget_draw_context
const& context)
noexcept
272 translate_z(0.1f) * narrow_cast<aarectangle>(_slider_rectangle),
273 theme<prefix / "slider">.background_color(
this),
274 theme<prefix / "slider">.border_color(
this),
275 theme<prefix / "slider">.border_width(
this),
276 theme<prefix / "slider">.corner_radius(
this));
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
axis
An enumeration of the 3 axis for 3D geometry.
Definition axis.hpp:18
@ window_relayout
Request that widgets get laid out on the next frame.
@ partial
A widget is partially enabled.
@ collapse
The widget is collapsed.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
auto theme
A tagged global variable to a theme model for a widget's component.
Definition theme_model.hpp:433
constexpr bool contains(point< value_type, 2 > const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:265
constexpr value_type & width() noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:166
constexpr value_type & height() noexcept
Access the y-as-height element from the extent.
Definition extent.hpp:177
A user interface event.
Definition gui_event.hpp:75
widget_id id
The numeric identifier of a widget.
Definition widget.hpp:35
virtual bool handle_event(gui_event const &event) noexcept
Handle command.
Definition widget.hpp:279
widget * parent
Pointer to the parent widget.
Definition widget.hpp:40
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:53
Draw context for drawing using the HikoGUI shaders.
Definition widget_draw_context.hpp:204
The layout of a widget.
Definition widget_layout.hpp:37
constexpr bool contains(point3i mouse_position) const noexcept
Check if the mouse position is inside the widget.
Definition widget_layout.hpp:126
float elevation
The elevation of the widget above the window.
Definition widget_layout.hpp:72
2D constraints.
Definition box_constraints.hpp:22
Scroll bar widget This widget is used in a pair of a vertical and horizontal scrollbar as a child of ...
Definition scroll_bar_widget.hpp:33
void draw(widget_draw_context const &context) noexcept override
Draw the widget.
Definition scroll_bar_widget.hpp:122
void set_layout(widget_layout const &context) noexcept override
Update the internal layout of the widget.
Definition scroll_bar_widget.hpp:92
box_constraints update_constraints() noexcept override
Update the constraints of the widget.
Definition scroll_bar_widget.hpp:67
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition scroll_bar_widget.hpp:141
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition scroll_bar_widget.hpp:171
hitbox hitbox_test(point2i position) const noexcept override
Find the widget that is under the mouse cursor.
Definition scroll_bar_widget.hpp:130