36 constexpr static auto prefix = Name / (Axis == axis::horizontal ?
"hbar" :
"vbar");
40 observer<float> offset;
41 observer<float> aperture;
42 observer<float> content;
46 forward_of<observer<float>>
auto&& content,
47 forward_of<observer<float>>
auto&& aperture,
48 forward_of<observer<float>>
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 extent2{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
82 extent2{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
83 extent2{theme<prefix>.
width(
this), large_number_v<int>}};
86 extent2{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
87 extent2{theme<prefix>.
width(
this), theme<prefix>.height(
this)},
92 void set_layout(
widget_layout const& context)
noexcept override
97 _slider_rectangle = {};
102 hilet slider_offset =
std::round(*offset * travel_vs_hidden_content_ratio());
103 if constexpr (
axis == axis::vertical) {
104 hilet slider_width =
theme<prefix /
"slider">.width(
this);
107 _slider_rectangle =
aarectangle{x, slider_offset, slider_width, slider_length()};
110 hilet slider_height =
theme<prefix /
"slider">.height(
this);
113 _slider_rectangle =
aarectangle{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 axis == axis::vertical ?
event.drag_delta().y() :
event.drag_delta().x();
158 hilet content_movement = slider_movement * hidden_content_vs_travel_ratio();
159 hilet new_offset = _offset_before_drag + content_movement;
160 offset = clamp_offset(new_offset);
179 float _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]]
float clamp_offset(
float new_offset)
const noexcept
191 hilet scrollable_distance =
std::max(0.0f, *content - *aperture);
192 return std::clamp(new_offset, 0.0f, scrollable_distance);
195 [[nodiscard]]
float rail_length() const noexcept
198 return axis == axis::vertical ? layout.height() : layout.width();
201 [[nodiscard]]
float 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]]
float slider_travel_range() const noexcept
222 return rail_length() - slider_length();
227 [[nodiscard]]
float 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.0f ? narrow_cast<float>(slider_travel_range()) / _hidden_content : 0.0f;
257 void draw_rails(widget_draw_context& context)
noexcept
262 theme<prefix>.background_color(
this),
263 theme<prefix>.border_color(
this),
264 theme<prefix>.border_width(
this),
265 theme<prefix>.border_radius(
this));
268 void draw_slider(widget_draw_context& 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">.border_radius(
this));