12#include "../GUI/GUI.hpp"
13#include "../geometry/geometry.hpp"
14#include "../observer/observer.hpp"
15#include "../utility/utility.hpp"
16#include "../macros.hpp"
23hi_export_module(hikogui.widgets.scroll_bar_widget);
25hi_export
namespace hi {
48 template<forward_of<observer<
float>> Content, forward_of<observer<
float>> Aperture, forward_of<observer<
float>> Offset>
52 Offset&& offset) noexcept :
58 _content_cbt = this->content.
subscribe([&](
auto...) {
59 ++global_counter<
"scroll_bar_widget:content:relayout">;
62 _aperture_cbt = this->aperture.
subscribe([&](
auto...) {
63 ++global_counter<
"scroll_bar_widget:aperture:relayout">;
66 _offset_cbt = this->offset.
subscribe([&](
auto...) {
67 ++global_counter<
"scroll_bar_widget:offset:relayout">;
83 if constexpr (axis == axis::vertical) {
85 extent2{theme().icon_size(), theme().size() * 4},
86 extent2{theme().icon_size(), theme().size() * 4},
87 extent2{theme().icon_size(), large_number_v<int>}};
90 extent2{theme().size() * 4, theme().icon_size()},
91 extent2{theme().size() * 4, theme().icon_size()},
92 extent2{large_number_v<int>, theme().icon_size()}};
101 _slider_rectangle = {};
106 auto const slider_offset =
std::round(*offset * travel_vs_hidden_content_ratio());
107 if constexpr (axis == axis::vertical) {
108 _slider_rectangle =
aarectangle{0.0f, slider_offset, context.width(), slider_length()};
110 _slider_rectangle =
aarectangle{slider_offset, 0.0f, slider_length(), context.height()};
114 [[nodiscard]]
bool visible() const noexcept
116 return *aperture < *content;
123 draw_slider(context);
129 hi_axiom(loop::main().on_thread());
132 _slider_rectangle.
contains(position)) {
133 return {
id, _layout.elevation, hitbox_type::scroll_bar};
141 switch (event.type()) {
142 case gui_event_type::mouse_down:
143 if (event.mouse().cause.left_button) {
145 _offset_before_drag = *offset;
150 case gui_event_type::mouse_drag:
151 if (event.mouse().cause.left_button) {
154 auto const slider_movement = axis == axis::vertical ?
event.drag_delta().y() :
event.drag_delta().x();
155 auto const content_movement = round_cast<int>(slider_movement * hidden_content_vs_travel_ratio());
156 auto const new_offset = _offset_before_drag + content_movement;
157 offset = clamp_offset(new_offset);
173 [[nodiscard]]
color background_color() const noexcept
override
175 return theme().fill_color(_layout.layer);
178 [[nodiscard]]
color foreground_color() const noexcept
override
180 if (phase() == widget_phase::hover) {
181 return theme().fill_color(_layout.layer + 2);
183 return theme().fill_color(_layout.layer + 1);
188 aarectangle _slider_rectangle;
190 float _offset_before_drag;
192 callback<void(
float)> _content_cbt;
193 callback<void(
float)> _aperture_cbt;
194 callback<void(
float)> _offset_cbt;
200 [[nodiscard]]
float clamp_offset(
float new_offset)
const noexcept
202 auto const scrollable_distance =
std::max(0.0f, *content - *aperture);
203 return std::clamp(new_offset, 0.0f, scrollable_distance);
206 [[nodiscard]]
float rail_length() const noexcept
208 hi_axiom(loop::main().on_thread());
209 return axis == axis::vertical ?
layout().height() :
layout().width();
212 [[nodiscard]]
float slider_length() const noexcept
214 hi_axiom(loop::main().on_thread());
216 auto const preferred_length = [&] {
217 if (*content == 0.0f) {
218 return rail_length();
220 return std::round(*aperture * rail_length() / *content);
224 return std::clamp(preferred_length, theme().size() * 2.0f, rail_length());
229 [[nodiscard]]
float slider_travel_range() const noexcept
231 hi_axiom(loop::main().on_thread());
232 return rail_length() - slider_length();
237 [[nodiscard]]
float hidden_content() const noexcept
239 hi_axiom(loop::main().on_thread());
240 return *content - *aperture;
247 [[nodiscard]]
float hidden_content_vs_travel_ratio() const noexcept
249 hi_axiom(loop::main().on_thread());
251 auto const _slider_travel_range = slider_travel_range();
252 return _slider_travel_range != 0 ?
std::round(hidden_content() / _slider_travel_range) : 0.0f;
259 [[nodiscard]]
float travel_vs_hidden_content_ratio() const noexcept
261 hi_axiom(loop::main().on_thread());
263 auto const _hidden_content = hidden_content();
264 return _hidden_content != 0 ? slider_travel_range() / _hidden_content : 0.0f;
267 void draw_rails(draw_context
const& context)
noexcept
269 auto const corner_radii =
270 axis == axis::vertical ? hi::corner_radii{
layout().width() * 0.5f} : hi::corner_radii{
layout().height() * 0.5f};
274 void draw_slider(draw_context
const& context)
noexcept
276 auto const corner_radii = axis == axis::vertical ? hi::corner_radii{_slider_rectangle.width() / 2.0f} :
277 hi::corner_radii{_slider_rectangle.height() / 2.0f};
279 context.draw_box(
layout(), translate_z(0.1f) * _slider_rectangle, foreground_color(), corner_radii);
axis
An enumeration of the 3 axis for 3D geometry.
Definition axis.hpp:24
@ window_relayout
Request that widgets get laid out on the next frame.
Definition gui_event_type.hpp:47
@ rectangle
The gui_event has rectangle data.
Definition gui_event_variant.hpp:44
@ partial
A widget is partially enabled.
Definition widget_state.hpp:73
@ collapse
The widget is collapsed.
Definition widget_state.hpp:34
@ invisible
The widget is invisible.
Definition widget_state.hpp:41
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
This is a RGBA floating point color.
Definition color_intf.hpp:49
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:33
A high-level geometric extent.
Definition extent2.hpp:32
Draw context for drawing using the HikoGUI shaders.
Definition draw_context_intf.hpp:209
A user interface event.
Definition gui_event.hpp:82
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:31
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:241
The layout of a widget.
Definition widget_layout.hpp:56
constexpr bool contains(point3 mouse_position) const noexcept
Check if the mouse position is inside the widget.
Definition widget_layout.hpp:179
2D constraints.
Definition box_constraints.hpp:25
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
callback< void(value_type)> subscribe(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback to this observer.
Definition observer_intf.hpp:458
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:38
hitbox hitbox_test(point2 position) const noexcept override
Find the widget that is under the mouse cursor.
Definition scroll_bar_widget.hpp:127
void draw(draw_context const &context) noexcept override
Draw the widget.
Definition scroll_bar_widget.hpp:119
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition scroll_bar_widget.hpp:139
void set_layout(widget_layout const &context) noexcept override
Update the internal layout of the widget.
Definition scroll_bar_widget.hpp:96
box_constraints update_constraints() noexcept override
Update the constraints of the widget.
Definition scroll_bar_widget.hpp:74
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition scroll_bar_widget.hpp:168
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:50
bool process_event(gui_event const &event) const noexcept override
Send a event to the window.
Definition widget.hpp:125
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:145