45 static_assert(Axis == axis::horizontal or Axis == axis::vertical or Axis == axis::both);
50 static constexpr tt::axis axis = Axis;
51 static constexpr bool controls_window = ControlsWindow;
63 tt_axiom(is_gui_thread());
80 template<
typename Widget,
typename... Args>
83 tt_axiom(is_gui_thread());
84 tt_axiom(not _content);
86 auto &
widget = super::make_widget<Widget>(std::forward<Args>(args)...);
92 void init() noexcept
override
96 _horizontal_scroll_bar =
97 &super::make_widget<horizontal_scroll_bar_widget>(_scroll_content_width, _scroll_aperture_width, _scroll_offset_x);
98 _vertical_scroll_bar =
99 &super::make_widget<vertical_scroll_bar_widget>(_scroll_content_height, _scroll_aperture_height, _scroll_offset_y);
101 if (
auto delegate = _delegate.lock()) {
102 delegate->init(*
this);
106 void deinit() noexcept
override
108 if (
auto delegate = _delegate.lock()) {
109 delegate->deinit(*
this);
114 [[nodiscard]]
float margin() const noexcept
override
119 [[nodiscard]]
bool constrain(hires_utc_clock::time_point display_time_point,
bool need_reconstrain)
noexcept override
121 tt_axiom(is_gui_thread());
124 auto has_updated_contraints =
super::constrain(display_time_point, need_reconstrain);
127 if (has_updated_contraints) {
136 if constexpr (any(axis & axis::horizontal)) {
142 if constexpr (any(axis & axis::vertical)) {
149 if constexpr (any(axis & axis::horizontal)) {
154 if constexpr (any(axis & axis::vertical)) {
160 tt_axiom(_minimum_size <= _preferred_size && _preferred_size <= _maximum_size);
161 return has_updated_contraints;
164 [[nodiscard]]
void layout(hires_utc_clock::time_point display_time_point,
bool need_layout)
noexcept override
166 tt_axiom(is_gui_thread());
169 need_layout |= _request_layout.exchange(
false);
176 ttlet height_adjustment = _horizontal_scroll_bar->
visible ? horizontal_scroll_bar_height : 0.0f;
177 ttlet width_adjustment = _vertical_scroll_bar->
visible ? vertical_scroll_bar_width : 0.0f;
179 ttlet vertical_scroll_bar_rectangle = aarectangle{
180 width() - vertical_scroll_bar_width, height_adjustment, vertical_scroll_bar_width, height() - height_adjustment};
182 ttlet horizontal_scroll_bar_rectangle =
183 aarectangle{0.0f, 0.0f, width() - width_adjustment, horizontal_scroll_bar_height};
185 _vertical_scroll_bar->set_layout_parameters_from_parent(vertical_scroll_bar_rectangle);
186 _horizontal_scroll_bar->set_layout_parameters_from_parent(horizontal_scroll_bar_rectangle);
188 _aperture_rectangle = aarectangle{0.0f, height_adjustment, width() - width_adjustment, height() - height_adjustment};
195 _scroll_aperture_width = _aperture_rectangle.width();
196 _scroll_aperture_height = _aperture_rectangle.height();
198 ttlet scroll_offset_x_max =
std::max(_scroll_content_width - _scroll_aperture_width, 0.0f);
199 ttlet scroll_offset_y_max =
std::max(_scroll_content_height - _scroll_aperture_height, 0.0f);
201 _scroll_offset_x = std::clamp(
std::round(*_scroll_offset_x), 0.0f, scroll_offset_x_max);
202 _scroll_offset_y = std::clamp(
std::round(*_scroll_offset_y), 0.0f, scroll_offset_y_max);
205 ttlet content_size = extent2{
206 std::max(*_scroll_content_width, _aperture_rectangle.width()),
207 std::max(*_scroll_content_height, _aperture_rectangle.height())};
211 ttlet content_rectangle = aarectangle{
212 -_scroll_offset_x, -_scroll_offset_y - height_adjustment, content_size.width(), content_size.height()};
215 _content->set_layout_parameters_from_parent(
218 if constexpr (controls_window) {
219 window.set_resize_border_priority(
220 true, not _vertical_scroll_bar->
visible, not _horizontal_scroll_bar->
visible,
true);
227 [[nodiscard]] hitbox hitbox_test(point2 position)
const noexcept override
229 tt_axiom(is_gui_thread());
234 if (_visible_rectangle.contains(position)) {
242 bool handle_event(mouse_event
const &event)
noexcept override
244 tt_axiom(is_gui_thread());
247 if (event.type == mouse_event::Type::Wheel) {
249 _scroll_offset_x +=
event.wheelDelta.x();
250 _scroll_offset_y +=
event.wheelDelta.y();
251 _request_layout =
true;
259 auto rectangle_ = aarectangle{
rectangle};
261 float delta_x = 0.0f;
262 if (rectangle_.right() > _aperture_rectangle.right()) {
263 delta_x = rectangle_.right() - _aperture_rectangle.right();
264 }
else if (rectangle_.left() < _aperture_rectangle.left()) {
265 delta_x = rectangle_.left() - _aperture_rectangle.left();
268 float delta_y = 0.0f;
269 if (rectangle_.top() > _aperture_rectangle.top()) {
270 delta_y = rectangle_.top() - _aperture_rectangle.top();
271 }
else if (rectangle_.bottom() < _aperture_rectangle.bottom()) {
272 delta_y = rectangle_.bottom() - _aperture_rectangle.bottom();
275 _scroll_offset_x += delta_x;
276 _scroll_offset_y += delta_y;
286 widget *_content =
nullptr;
287 horizontal_scroll_bar_widget *_horizontal_scroll_bar =
nullptr;
288 vertical_scroll_bar_widget *_vertical_scroll_bar =
nullptr;
290 observable<float> _scroll_content_width;
291 observable<float> _scroll_content_height;
292 observable<float> _scroll_aperture_width;
293 observable<float> _scroll_aperture_height;
294 observable<float> _scroll_offset_x;
295 observable<float> _scroll_offset_y;
297 aarectangle _aperture_rectangle;
304 ttlet content_size = _content->preferred_size();
306 if (content_size <= size()) {
307 return {
false,
false};
308 }
else if (content_size.width() - _vertical_scroll_bar->preferred_size().width() <= width()) {
309 return {
false,
true};
310 }
else if (content_size.height() - _horizontal_scroll_bar->preferred_size().height() <= height()) {
311 return {
true,
false};