The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
scrollbar_container.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
3 by Mark de Wever <koraq@xs4all.nl>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#define GETTEXT_DOMAIN "wesnoth-lib"
17
19
23#include "gui/core/log.hpp"
28#include "sdl/rect.hpp"
29
30#include <algorithm>
31#include <functional>
32#include <string_view>
33
34#define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
35#define LOG_HEADER LOG_SCOPE_HEADER + ':'
36
37namespace gui2
38{
39
40namespace
41{
42
43using namespace std::literals;
44constexpr std::array button_up_names { "_begin"sv, "_line_up"sv, "_half_page_up"sv, "_page_up"sv };
45constexpr std::array button_down_names { "_end"sv, "_line_down"sv, "_half_page_down"sv, "_page_down"sv };
46
47/**
48 * Returns a map with the names of all buttons and the scrollbar jump they're
49 * supposed to execute.
50 */
51const std::map<std::string, scrollbar_base::scroll_mode>& scroll_lookup()
52{
53 static std::map<std::string, scrollbar_base::scroll_mode> lookup;
54 if(lookup.empty()) {
55 lookup["_begin"] = scrollbar_base::BEGIN;
56 lookup["_line_up"] = scrollbar_base::ITEM_BACKWARDS;
57 lookup["_half_page_up"] = scrollbar_base::HALF_JUMP_BACKWARDS;
58 lookup["_page_up"] = scrollbar_base::JUMP_BACKWARDS;
59
60 lookup["_end"] = scrollbar_base::END;
61 lookup["_line_down"] = scrollbar_base::ITEM_FORWARD;
62 lookup["_half_page_down"] = scrollbar_base::HALF_JUMP_FORWARD;
63 lookup["_page_down"] = scrollbar_base::JUMP_FORWARD;
64 }
65
66 return lookup;
67}
68
69} // namespace
70
72 const implementation::builder_scrollbar_container& builder, const std::string& control_type)
73 : container_base(builder, control_type)
74 , state_(ENABLED)
75 , vertical_scrollbar_mode_(builder.vertical_scrollbar_mode)
76 , horizontal_scrollbar_mode_(builder.horizontal_scrollbar_mode)
77 , vertical_scrollbar_grid_(nullptr)
78 , horizontal_scrollbar_grid_(nullptr)
79 , vertical_scrollbar_(nullptr)
80 , horizontal_scrollbar_(nullptr)
81 , content_grid_(nullptr)
82 , content_(nullptr)
83 , content_visible_area_()
84{
85 connect_signal<event::SDL_KEY_DOWN>(
86 std::bind(&scrollbar_container::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6));
87
88 connect_signal<event::SDL_WHEEL_UP>(
89 std::bind(&scrollbar_container::signal_handler_sdl_wheel_up, this, std::placeholders::_2, std::placeholders::_3),
91
92 connect_signal<event::SDL_WHEEL_DOWN>(
93 std::bind(&scrollbar_container::signal_handler_sdl_wheel_down, this, std::placeholders::_2, std::placeholders::_3),
95
96 connect_signal<event::SDL_WHEEL_LEFT>(
97 std::bind(&scrollbar_container::signal_handler_sdl_wheel_left, this, std::placeholders::_2, std::placeholders::_3),
99
100 connect_signal<event::SDL_WHEEL_RIGHT>(
101 std::bind(&scrollbar_container::signal_handler_sdl_wheel_right, this, std::placeholders::_2, std::placeholders::_3),
103
104 connect_signal<event::SDL_TOUCH_MOTION>(
106 this,
107 std::placeholders::_2,
108 std::placeholders::_3,
109 std::placeholders::_5,
110 std::placeholders::_6),
112}
113
114void scrollbar_container::layout_initialize(const bool full_initialization)
115{
116 // Inherited.
117 container_base::layout_initialize(full_initialization);
118
119 if(full_initialization) {
121
123 case ALWAYS_VISIBLE:
125 break;
126
127 case AUTO_VISIBLE:
129 break;
130
131 default:
133 }
134
136
138 case ALWAYS_VISIBLE:
140 break;
141
142 case AUTO_VISIBLE:
144 break;
145
146 default:
148 }
149 }
150
151 assert(content_grid_);
152 content_grid_->layout_initialize(full_initialization);
153}
154
155void scrollbar_container::request_reduce_height(const unsigned maximum_height)
156{
157 DBG_GUI_L << LOG_HEADER << " requested height " << maximum_height << ".";
158
159 /*
160 * First ask the content to reduce it's height. This seems to work for now,
161 * but maybe some sizing hints will be required later.
162 */
163 /** @todo Evaluate whether sizing hints are required. */
164 assert(content_grid_);
165 const unsigned offset =
168 : 0;
169
170 content_grid_->request_reduce_height(maximum_height - offset);
171
172 // Did we manage to achieve the wanted size?
173 point size = get_best_size();
174 if(static_cast<unsigned>(size.y) <= maximum_height) {
175 DBG_GUI_L << LOG_HEADER << " child honored request, height " << size.y << ".";
176 return;
177 }
178
180 DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.";
181 return;
182 }
183
186
187 // Always set the bar visible, is a nop is already visible.
189
190 const point scrollbar_size = vertical_scrollbar_grid_->get_best_size();
191
192 // If showing the scrollbar increased the height, hide and abort.
193 if(resized && scrollbar_size.y > size.y) {
195 DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
196 << " increased the height to " << scrollbar_size.y << ".";
197 return;
198 }
199
200 if(maximum_height > static_cast<unsigned>(scrollbar_size.y)) {
201 size.y = maximum_height;
202 } else {
203 size.y = scrollbar_size.y;
204 }
205
206 // FIXME adjust for the step size of the scrollbar
207
208 set_layout_size(size);
209 DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.y << ".";
210
211 if(resized) {
212 DBG_GUI_L << LOG_HEADER << " resize modified the width, throw notification.";
213
215 }
216}
217
218void scrollbar_container::request_reduce_width(const unsigned maximum_width)
219{
220 DBG_GUI_L << LOG_HEADER << " requested width " << maximum_width << ".";
221
222 if(static_cast<unsigned>(get_grid().get_best_size().x) > maximum_width) {
223 get_grid().request_reduce_width(maximum_width);
224 }
225
226 // First ask our content, it might be able to wrap which looks better as
227 // a scrollbar.
228 assert(content_grid_);
229 const unsigned offset =
232 : 0;
233
234 content_grid_->request_reduce_width(maximum_width - offset);
235
236 // Did we manage to achieve the wanted size?
237 point size = get_best_size();
238 if(static_cast<unsigned>(size.x) <= maximum_width) {
239 DBG_GUI_L << LOG_HEADER << " child honored request, width " << size.x << ".";
240 return;
241 }
242
244 DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.";
245 return;
246 }
247
248 // Always set the bar visible, is a nop when it's already visible.
251 size = get_best_size();
252
254
255 /*
256 * If the vertical bar is not invisible it's size needs to be added to the
257 * minimum size.
258 */
260 scrollbar_size.x += vertical_scrollbar_grid_->get_best_size().x;
261 }
262
263 // If showing the scrollbar increased the width, hide and abort.
264 if(horizontal_scrollbar_mode_ == AUTO_VISIBLE_FIRST_RUN && scrollbar_size.x > size.x) {
266 DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
267 << " increased the width to " << scrollbar_size.x << ".";
268 return;
269 }
270
271 if(maximum_width > static_cast<unsigned>(scrollbar_size.x)) {
272 size.x = maximum_width;
273 } else {
274 size.x = scrollbar_size.x;
275 }
276
277 size.x = std::max(size.x, get_grid().get_best_size().x);
278
279 // FIXME adjust for the step size of the scrollbar
280
281 set_layout_size(size);
282 DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.x << ".";
283}
284
286{
287 return content_grid_ ? content_grid_->can_wrap() : false;
288}
289
291{
293
294 /***** get vertical scrollbar size *****/
296 ? point()
298
299 /***** get horizontal scrollbar size *****/
301 ? point()
303
304 /***** get content size *****/
305 assert(content_grid_);
306 const point content = content_grid_->get_best_size();
307
308 point result(
309 vertical_scrollbar.x + std::max(horizontal_scrollbar.x, content.x),
310 horizontal_scrollbar.y + std::max(vertical_scrollbar.y, content.y));
311
312 DBG_GUI_L << LOG_HEADER << " vertical_scrollbar " << vertical_scrollbar << " horizontal_scrollbar "
313 << horizontal_scrollbar << " content " << content << " result " << result << ".";
314
315 return result;
316}
317
318static void set_scrollbar_mode(grid* scrollbar_grid,
319 scrollbar_base* scrollbar,
321 const unsigned items,
322 const unsigned visible_items,
323 grid* content_grid)
324{
325 assert(scrollbar_grid && scrollbar);
326
327 if(scrollbar_mode == scrollbar_container::ALWAYS_INVISIBLE) {
329 return;
330 }
331
332 scrollbar->set_item_count(items);
333 scrollbar->set_visible_items(visible_items);
334
335 if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE) {
336 const bool scrollbar_needed = items > visible_items;
337 scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
338 } else if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE_FIRST_RUN) {
339 if(items <= visible_items && content_grid != nullptr
340 && scrollbar_grid->get_visible() == widget::visibility::visible
341 ) {
343 // Give newly freed space to the items.
344 content_grid->layout_initialize(false);
345 }
346 }
347}
349 unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
350{
351 if(old_position == 0) {
352 return false;
353 } else if(old_position + visible_items >= old_item_count) {
354 return true;
355 } else if(insertion_pos <= old_position) {
356 return true;
357 }
358
359 return false;
360}
361
362static void adjust_scrollbar_mode(grid* scrollbar_grid,
363 scrollbar_base* scrollbar,
365 const unsigned items_before,
366 const unsigned items_after,
367 const int insertion_pos,
368 const unsigned visible_items)
369{
370 assert(scrollbar_grid && scrollbar);
371 if(items_before != scrollbar->get_item_count()) {
372 return set_scrollbar_mode(scrollbar_grid, scrollbar, scrollbar_mode, items_after, visible_items, nullptr);
373 }
374
375 // TODO: does this also work well in case the items were removed?
376 const unsigned previous_item_position = scrollbar->get_item_position();
377
378 // Casts insertion_pos to an unsigned so negative values are interpreted as 'at end'
379 const bool inserted_before_visible_area = is_inserted_before(
380 static_cast<unsigned>(insertion_pos), items_before, previous_item_position, visible_items);
381
382 if(scrollbar_mode == scrollbar_container::ALWAYS_INVISIBLE) {
384 return;
385 }
386
387 scrollbar->set_item_count(items_after);
388 scrollbar->set_item_position(inserted_before_visible_area
389 ? previous_item_position + items_after - items_before
390 : previous_item_position);
391
392 // scrollbar->set_item_position(0);
393 scrollbar->set_visible_items(visible_items);
394
395 if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE) {
396 const bool scrollbar_needed = items_after > visible_items;
397 scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
398 }
399}
400
401void scrollbar_container::place(const point& origin, const point& size)
402{
403 // Inherited.
404 container_base::place(origin, size);
405
406 // Set content size
407 assert(content_ && content_grid_);
408
409 const point content_origin = content_->get_origin();
410
411 const point best_size = content_grid_->get_best_size();
412 const point content_size(content_->get_width(), content_->get_height());
413
414 const point content_grid_size(std::max(best_size.x, content_size.x), std::max(best_size.y, content_size.y));
415
416 set_content_size(content_origin, content_grid_size);
417
418 // Set vertical scrollbar
425 content_grid_.get()
426 );
427
428 // Set horizontal scrollbar
435 content_grid_.get()
436 );
437
438 // Update the buttons.
440
441 // Now set the visible part of the content.
443 content_grid_->set_visible_rectangle(content_visible_area_);
444}
445
447{
448 // Inherited.
450
451 // Set content size
452 assert(content_ && content_grid_);
453
454 const point content_origin = content_->get_origin();
455
456 content_grid_->set_origin(content_origin);
457
458 // Changing the origin also invalidates the visible area.
459 content_grid_->set_visible_rectangle(content_visible_area_);
460}
461
463{
464 // Inherited.
466
467 // Now get the visible part of the content.
469
470 content_grid_->set_visible_rectangle(content_visible_area_);
471}
472
474{
475 return state_ != DISABLED;
476}
477
479{
480 return state_;
481}
482
483widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active)
484{
485 widget* w = scrollbar_container_implementation::find_at<widget>(*this, coordinate, must_be_active);
486 if(w == nullptr) {
487 w = widget::find_at(coordinate, must_be_active);
488 }
489
490 return w;
491}
492
493const widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active) const
494{
495 const widget* w = scrollbar_container_implementation::find_at<const widget>(*this, coordinate, must_be_active);
496 if(w == nullptr) {
497 w = widget::find_at(coordinate, must_be_active);
498 }
499
500 return w;
501}
502
503widget* scrollbar_container::find(const std::string_view id, const bool must_be_active)
504{
505 return scrollbar_container_implementation::find<widget>(*this, id, must_be_active);
506}
507
508const widget* scrollbar_container::find(const std::string_view id, const bool must_be_active) const
509{
510 return scrollbar_container_implementation::find<const widget>(*this, id, must_be_active);
511}
512
514{
515 assert(content_grid_);
516 return container_base::disable_click_dismiss() || content_grid_->disable_click_dismiss();
517}
518
520{
521 return std::make_unique<gui2::iteration::scrollbar_container>(*this);
522}
523
525{
526 /**
527 * @todo Try to handle AUTO_VISIBLE_FIRST_RUN here as well.
528 *
529 * Handling it here makes the code a bit more complex but allows to not
530 * reserve space for scrollbars, which will look nicer in the MP lobby.
531 * But the extra complexity is no 1.8 material.
532 */
533
534 assert(content_ && content_grid_);
535
536 point best_size = content_grid_->recalculate_best_size();
537 point size = content_->get_size();
538
539 DBG_GUI_L << LOG_HEADER << " wanted size " << best_size << " available size " << size << ".";
540
541 if(size == point()) {
542 DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
543 return false;
544 }
545
546 if(best_size.x <= size.x && best_size.y <= size.y) {
547 const point content_size = content_grid_->get_size();
548
549 if(content_size.x > size.x || content_size.y > size.y) {
550 DBG_GUI_L << LOG_HEADER << " will fit, only needs a resize.";
551 goto resize;
552 }
553
554 if(force_sizing) {
555 DBG_GUI_L << LOG_HEADER << " fits, but resize forced.";
556 goto resize;
557 }
558
559 DBG_GUI_L << LOG_HEADER << " fits, nothing to do.";
560 return true;
561 }
562
563 if(best_size.x > size.x) {
564 DBG_GUI_L << LOG_HEADER << " content too wide.";
565
567 (
570 )
571 ) {
572 DBG_GUI_L << LOG_HEADER << " can't use horizontal scrollbar, request placement.";
573
576 return false;
577 }
578 }
579
580 if(best_size.y > size.y) {
581 DBG_GUI_L << LOG_HEADER << " content too high.";
582
584 (
587 )
588 ) {
589 DBG_GUI_L << LOG_HEADER << " can't use vertical scrollbar, request placement.";
590
593 return false;
594 }
595 }
596
597resize:
598 DBG_GUI_L << LOG_HEADER << " handle resizing.";
599
601 return true;
602}
603
604bool scrollbar_container::content_resize_request(const int width_modification,
605 const int height_modification,
606 const int width_modification_pos,
607 const int height_modification_pos)
608{
609 DBG_GUI_L << LOG_HEADER << " wanted width modification " << width_modification << " wanted height modification "
610 << height_modification << ".";
611
612 if(get_size() == point()) {
613 DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
614 return false;
615 }
616
618 assert(window);
619
620 if(window->get_need_layout()) {
621 DBG_GUI_L << LOG_HEADER << " window already needs a layout phase, bailing out.";
622 return false;
623 }
624
625 assert(content_ && content_grid_);
626
627 const bool result =
628 content_resize_width(width_modification, width_modification_pos) &&
629 content_resize_height(height_modification, height_modification_pos);
630
632
633 /*
634 * The subroutines set the new size of the scrollbar but don't
635 * update the button status.
636 */
637 if(result) {
639 }
640
641 DBG_GUI_L << LOG_HEADER << " result " << result << ".";
642 return result;
643}
644
645bool scrollbar_container::content_resize_width(const int width_modification, const int width_modification_pos)
646{
647 if(width_modification == 0) {
648 return true;
649 }
650
651 const int new_width = content_grid_->get_width() + width_modification;
652 DBG_GUI_L << LOG_HEADER << " current width " << content_grid_->get_width() << " wanted width " << new_width;
653
654 if(new_width < 0) {
655 return false;
656 }
657
658 if(static_cast<unsigned>(new_width) <= content_->get_width()) {
659 DBG_GUI_L << " width fits in container, test height.";
660
662 content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
664 return true;
665 }
666
669 (
672 )
673 ) {
674 DBG_GUI_L << " can't use horizontal scrollbar, ask window.";
675
677 assert(window);
678
680 return false;
681 }
682
683 DBG_GUI_L << " use the horizontal scrollbar, test height.";
685 content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
687
688 return true;
689}
690
691bool scrollbar_container::content_resize_height(const int height_modification, const int height_modification_pos)
692{
693 if(height_modification == 0) {
694 return true;
695 }
696
697 const int new_height = content_grid_->get_height() + height_modification;
698
699 DBG_GUI_L << LOG_HEADER << " current height " << content_grid_->get_height() << " wanted height " << new_height;
700
701 if(new_height < 0) {
702 return false;
703 }
704
705 if(static_cast<unsigned>(new_height) <= content_->get_height()) {
706 DBG_GUI_L << " height in container, resize allowed.";
707
709 content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
710 return true;
711 }
712
715 (
718 )
719 ) {
720 DBG_GUI_L << " can't use vertical scrollbar, ask window.";
721
723 assert(window);
724
726 return false;
727 }
728
729 DBG_GUI_L << " use the vertical scrollbar, resize allowed.";
730
732 content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
733
734 return true;
735}
736
738{
739 /***** Setup vertical scrollbar *****/
740 vertical_scrollbar_grid_ = find_widget<grid>("_vertical_scrollbar_grid", false, true);
741
743 vertical_scrollbar_grid_->find_widget<scrollbar_base>("_vertical_scrollbar", false, true);
744
745 connect_signal_notify_modified(*vertical_scrollbar_,
747
748 /***** Setup horizontal scrollbar *****/
749 horizontal_scrollbar_grid_ = find_widget<grid>("_horizontal_scrollbar_grid", false, true);
750
752 horizontal_scrollbar_grid_->find_widget<scrollbar_base>("_horizontal_scrollbar", false, true);
753
754 connect_signal_notify_modified(*horizontal_scrollbar_,
756
757 /***** Setup the scrollbar buttons *****/
758 for(const auto& item : scroll_lookup()) {
759 // Vertical.
761
762 if(button) {
764 std::bind(&scrollbar_container::scroll_vertical_scrollbar, this, item.second));
765 }
766
767 // Horizontal.
768 button = horizontal_scrollbar_grid_->find_widget<clickable_item>(item.first, false, false);
769
770 if(button) {
772 std::bind(&scrollbar_container::scroll_horizontal_scrollbar, this, item.second));
773 }
774 }
775
776 /***** Setup the content *****/
777 auto content = build_single_widget_instance<spacer>();
778 content_ = content.get();
779
780 // TODO: possibly move this unique_ptr casting functionality to a helper function.
781 content_grid_.reset(static_cast<grid*>(get_grid().swap_child("_content_grid", std::move(content), true).release()));
782 assert(content_grid_);
783
784 content_grid_->set_parent(this);
785
786 /***** Let our subclasses initialize themselves. *****/
788}
789
796
803
805{
807
808 // Inherited.
810
811 content_grid_->draw_children();
812}
813
815{
816 // Inherited.
818
819 assert(content_grid_);
820 content_grid_->layout_children();
821}
822
823void scrollbar_container::set_content_size(const point& origin, const point& size)
824{
825 content_grid_->place(origin, size);
826}
827
829{
830 assert(content_);
832
833 // Set the bottom right location first if it doesn't fit the top left
834 // will look good. First calculate the left and top position depending on
835 // the current position.
836
837 const int left_position = horizontal_scrollbar_->get_item_position() + (rect.x - content_->get_x());
838 const int top_position = vertical_scrollbar_->get_item_position() + (rect.y - content_->get_y());
839
840 // bottom.
841 const int wanted_bottom = rect.y + rect.h;
842 const int current_bottom = content_->get_y() + content_->get_height();
843
844 int distance = wanted_bottom - current_bottom;
845 if(distance > 0) {
847 }
848
849 // right.
850 const int wanted_right = rect.x + rect.w;
851 const int current_right = content_->get_x() + content_->get_width();
852
853 distance = wanted_right - current_right;
854 if(distance > 0) {
856 }
857
858 // top.
859 if(top_position < static_cast<int>(vertical_scrollbar_->get_item_position())) {
861 }
862
863 // left.
864 if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
866 }
867
868 // Update.
870}
871
873{
874 if(true) { /** @todo scrollbar visibility. */
875 /***** set scroll up button status *****/
876 for(const auto& name : button_up_names) {
877 styled_widget* button = vertical_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
878
879 if(button) {
881 }
882 }
883
884 /***** set scroll down status *****/
885 for(const auto& name : button_down_names) {
886 styled_widget* button = vertical_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
887
888 if(button) {
890 }
891 }
892
893 /***** Set the status if the scrollbars *****/
895 }
896
897 if(true) { /** @todo scrollbar visibility. */
898 /***** Set scroll left button status *****/
899 for(const auto& name : button_up_names) {
900 styled_widget* button = horizontal_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
901
902 if(button) {
904 }
905 }
906
907 /***** Set scroll right button status *****/
908 for(const auto& name : button_down_names) {
909 styled_widget* button = horizontal_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
910
911 if(button) {
913 }
914 }
915
916 /***** Set the status if the scrollbars *****/
918 }
919}
920
927
934
942
949
957
965
973
975{
976 assert(vertical_scrollbar_);
977
979 move_viewport(0, pixels);
980}
981
983{
984 assert(horizontal_scrollbar_);
985
987 move_viewport(pixels, 0);
988}
989
990void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
991{
993
997
998 handled = true;
999}
1000
1001void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
1002{
1003 assert(vertical_scrollbar_);
1004
1007
1008 handled = true;
1009}
1010
1011void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
1012{
1013 assert(vertical_scrollbar_);
1014
1017
1018 handled = true;
1019}
1020
1021void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1022
1023{
1024 assert(vertical_scrollbar_);
1025
1028
1029 handled = true;
1030}
1031
1032void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1033{
1034 assert(vertical_scrollbar_);
1035
1038
1039 handled = true;
1040}
1041
1042void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1043{
1044 assert(vertical_scrollbar_);
1045
1048
1049 handled = true;
1050}
1051
1052void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1053{
1054 assert(horizontal_scrollbar_);
1055
1058
1059 handled = true;
1060}
1061
1062void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1063{
1064 assert(horizontal_scrollbar_);
1065
1068
1069 handled = true;
1070}
1071
1073{
1074 // Init.
1076
1077 /*** Update the content location. ***/
1078 const int x_offset = horizontal_scrollbar_mode_ == ALWAYS_INVISIBLE
1079 ? 0
1081
1082 const int y_offset = vertical_scrollbar_mode_ == ALWAYS_INVISIBLE
1083 ? 0
1085
1086 move_viewport(x_offset, y_offset);
1087}
1088
1089void scrollbar_container::move_viewport(const int pixels_x, const int pixels_y)
1090{
1091 // Initialize
1092 assert(content_ && content_grid_);
1093
1094 const point content_origin {content_->get_x() - pixels_x, content_->get_y() - pixels_y};
1095
1096 content_grid_->set_origin(content_origin);
1097 content_grid_->set_visible_rectangle(content_visible_area_);
1099
1100 // Update scrollbar
1102}
1103
1104const std::string& scrollbar_container::type()
1105{
1106 static const std::string type = "scrollbar_container";
1107 return type;
1108}
1109
1111{
1112 return type();
1113}
1114
1116 const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier)
1117{
1118 DBG_GUI_E << LOG_HEADER << event << ".";
1119
1120 switch(key) {
1121 case SDLK_HOME:
1122 handle_key_home(modifier, handled);
1123 break;
1124
1125 case SDLK_END:
1126 handle_key_end(modifier, handled);
1127 break;
1128
1129 case SDLK_PAGEUP:
1130 handle_key_page_up(modifier, handled);
1131 break;
1132
1133 case SDLK_PAGEDOWN:
1134 handle_key_page_down(modifier, handled);
1135 break;
1136
1137 case SDLK_UP:
1138 handle_key_up_arrow(modifier, handled);
1139 break;
1140
1141 case SDLK_DOWN:
1142 handle_key_down_arrow(modifier, handled);
1143 break;
1144
1145 case SDLK_LEFT:
1146 handle_key_left_arrow(modifier, handled);
1147 break;
1148
1149 case SDLK_RIGHT:
1150 handle_key_right_arrow(modifier, handled);
1151 break;
1152 default:
1153 /* ignore */
1154 break;
1155 }
1156}
1157
1170
1183
1196
1209
1210void
1212 bool& handled,
1213 const point& position,
1214 const point& distance)
1215{
1216 (void) position;
1217 DBG_GUI_E << LOG_HEADER << event << ".";
1218
1219 bool is_scrollbar_moved = false;
1220
1222
1224 horizontal_scrollbar_->scroll_by(-distance.x);
1225 is_scrollbar_moved = true;
1226 }
1227 }
1228
1230
1232 vertical_scrollbar_->scroll_by(-distance.y);
1233 is_scrollbar_moved = true;
1234 }
1235 }
1236
1237 if (is_scrollbar_moved) {
1239 handled = true;
1240 }
1241}
1242
1243namespace implementation
1244{
1247 , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
1248 , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
1249{
1250}
1251
1252} // namespace implementation
1253
1254} // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
Simple push button.
Definition button.hpp:36
virtual void connect_click_handler(const event::signal &signal) override
Inherited from clickable_item.
Definition button.hpp:54
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition button.cpp:64
Small concept class.
A generic container base class.
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
const grid & get_grid() const
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
virtual void set_visible_rectangle(const rect &rectangle) override
See widget::set_visible_rectangle.
virtual void layout_children() override
See widget::layout_children.
virtual void set_origin(const point &origin) override
See widget::set_origin.
virtual void place(const point &origin, const point &size) override
See widget::place.
virtual void impl_draw_children() override
See widget::impl_draw_children.
Main class to show messages to the user.
Definition message.hpp:36
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Base container class.
Definition grid.hpp:32
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
Definition grid.cpp:190
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition grid.cpp:236
Base class for a scroll bar.
Definition scrollbar.hpp:39
bool at_begin() const
Is the positioner at the beginning of the scrollbar?
Definition scrollbar.hpp:76
void set_item_position(const unsigned item_position)
Note the position isn't guaranteed to be the wanted position the step size is honored.
unsigned get_item_position() const
bool at_end() const
Is the positioner at the and of the scrollbar?
Definition scrollbar.hpp:86
bool all_items_visible() const
Are all items visible?
Definition scrollbar.hpp:92
unsigned get_step_size() const
void set_item_count(const unsigned item_count)
unsigned get_item_count() const
void set_visible_items(const unsigned visible_items)
void scroll(const scroll_mode scroll)
Sets the item position.
Definition scrollbar.cpp:71
scroll_mode
scroll 'step size'.
Definition scrollbar.hpp:52
@ ITEM_FORWARD
Go one item towards the end.
Definition scrollbar.hpp:59
@ ITEM_BACKWARDS
Go one item towards the begin.
Definition scrollbar.hpp:54
@ END
Go to the end position.
Definition scrollbar.hpp:58
@ HALF_JUMP_FORWARD
Go half the visible items towards the end.
Definition scrollbar.hpp:60
@ BEGIN
Go to begin position.
Definition scrollbar.hpp:53
@ JUMP_BACKWARDS
Go the visible items towards the begin.
Definition scrollbar.hpp:57
@ HALF_JUMP_BACKWARDS
Go half the visible items towards the begin.
Definition scrollbar.hpp:55
void scroll_by(const int pixels)
Definition scrollbar.cpp:66
virtual void set_active(const bool active) override
See styled_widget::set_active.
virtual void handle_key_home(SDL_Keymod modifier, bool &handled)
Home key pressed.
void set_horizontal_scrollbar_item_position(const unsigned position)
Move the horizontal scrollbar to a position.
void signal_handler_sdl_wheel_left(const event::ui_event event, bool &handled)
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)
Down arrow key pressed.
virtual bool get_active() const override
See styled_widget::get_active.
void scroll_vertical_scrollbar(const scrollbar_base::scroll_mode scroll)
Scrolls the vertical scrollbar.
void show_content_rect(const rect &rect)
Shows a certain part of the content.
virtual void finalize_subclass()
Function for the subclasses to do their setup.
state_t state_
Current state of the widget.
scrollbar_mode
The way to handle the showing or hiding of the scrollbar.
@ ALWAYS_INVISIBLE
The scrollbar is never shown even notwhen needed.
@ ALWAYS_VISIBLE
The scrollbar is always shown, whether needed or not.
@ AUTO_VISIBLE_FIRST_RUN
Like AUTO_VISIBLE, but when not needed upon the initial layout phase, the bars are not shown and no s...
@ AUTO_VISIBLE
The scrollbar is shown when the number of items is larger as the visible items.
virtual const std::string & get_control_type() const override
See styled_widget::get_control_type.
virtual void layout_children() override
See widget::layout_children.
rect content_visible_area_
Cache for the visible area for the content.
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
void set_vertical_scrollbar_mode(const scrollbar_mode scrollbar_mode)
void finalize_setup()
The builder needs to call us so we do our setup.
void signal_handler_sdl_wheel_up(const event::ui_event event, bool &handled)
virtual bool can_wrap() const override
See widget::can_wrap.
virtual void set_origin(const point &origin) override
See widget::set_origin.
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
void signal_handler_sdl_touch_motion(const event::ui_event event, bool &handled, const point &position, const point &distance)
unsigned get_horizontal_scrollbar_item_position() const
Returns current position of the horizontal scrollbar.
void signal_handler_sdl_wheel_right(const event::ui_event event, bool &handled)
spacer * content_
Dummy spacer to hold the contents location.
virtual void place(const point &origin, const point &size) override
See widget::place.
void set_horizontal_scrollbar_mode(const scrollbar_mode scrollbar_mode)
void set_scrollbar_button_status()
Sets the status of the scrollbar buttons.
void move_viewport(const int pixels_x, const int pixels_y)
To be called after the scollbar moves manually (by pixel) to move the viewport.
virtual void handle_key_page_up(SDL_Keymod modifier, bool &handled)
Page up key pressed.
virtual void request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
virtual void set_visible_rectangle(const rect &rectangle) override
See widget::set_visible_rectangle.
virtual iteration::walker_ptr create_walker() override
See widget::create_walker.
unsigned get_vertical_scrollbar_item_position() const
Returns current position of the vertical scrollbar.
void scroll_horizontal_scrollbar(const scrollbar_base::scroll_mode scroll)
Scrolls the horizontal scrollbar.
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key, SDL_Keymod modifier)
scrollbar_container(const implementation::builder_scrollbar_container &builder, const std::string &control_type)
void set_vertical_scrollbar_item_position(const unsigned position)
Move the vertical scrollbar to a position.
scrollbar_base * vertical_scrollbar_
These are valid after finalize_setup().
virtual unsigned get_state() const override
See styled_widget::get_state.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
void scrollbar_moved()
Helper function which needs to be called after the scollbar moves by item.
void signal_handler_sdl_wheel_down(const event::ui_event event, bool &handled)
bool content_resize_width(const int width_modification, const int width_modification_pos)
Helper for content_resize_request.
std::unique_ptr< grid > content_grid_
The grid that holds the content.
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
bool content_resize_height(const int height_modification, const int width_modification_pos)
Helper for content_resize_request.
void scroll_horizontal_scrollbar_by(const int pixels)
Scrolls the horizontal scrollbar by pixel.
virtual void handle_key_page_down(SDL_Keymod modifier, bool &handled)
Page down key pressed.
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
grid * vertical_scrollbar_grid_
These are valid after finalize_setup().
virtual void impl_draw_children() override
See widget::impl_draw_children.
virtual void set_content_size(const point &origin, const point &size)
Sets the size of the content grid.
void vertical_scrollbar_moved()
Callback when the scrollbar moves (NOTE maybe only one callback needed).
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
virtual void handle_key_end(SDL_Keymod modifier, bool &handled)
End key pressed.
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)
Right arrow key pressed.
scrollbar_mode vertical_scrollbar_mode_
The mode of how to show the scrollbar.
void scroll_vertical_scrollbar_by(const int pixels)
Scrolls the vertical scrollbar by pixel.
Base class for all widgets.
Definition widget.hpp:55
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition widget.hpp:747
void set_layout_size(const point &size)
Definition widget.cpp:346
point get_best_size() const
Gets the best size for the widget.
Definition widget.cpp:203
void set_visible(const visibility visible)
Definition widget.cpp:479
void queue_redraw()
Indicates that this widget should be redrawn.
Definition widget.cpp:464
visibility get_visible() const
Definition widget.cpp:506
point get_origin() const
Returns the screen origin of the widget.
Definition widget.cpp:311
int get_x() const
Definition widget.cpp:326
unsigned get_width() const
Definition widget.cpp:336
int get_y() const
Definition widget.cpp:331
point get_size() const
Returns the size of the widget.
Definition widget.cpp:316
unsigned get_height() const
Definition widget.cpp:341
window * get_window()
Get the parent window.
Definition widget.cpp:117
@ visible
The user sets the widget visible, that means:
@ invisible
The user set the widget invisible, that means:
@ hidden
The user sets the widget hidden, that means:
rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition widget.cpp:321
virtual widget * find_at(const point &coordinate, const bool must_be_active)
Returns the widget at the wanted coordinates.
Definition widget.cpp:549
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition window.hpp:59
void invalidate_layout()
Updates the size of the window.
Definition window.cpp:759
bool get_need_layout() const
Definition window.hpp:404
This file contains the definitions for the gui2::event::message class.
#define LOG_HEADER
const config * cfg
Define the common log macros for the gui toolkit.
#define DBG_GUI_L
Definition log.hpp:55
#define DBG_GUI_E
Definition log.hpp:35
#define LOG_SCOPE_HEADER
Definition button.cpp:34
This file contains the window object, this object is a top level container which has the event manage...
Defines the exception classes for the layout algorithm.
#define log_scope2(domain, description)
Definition log.hpp:276
ui_event
The event sent to the dispatcher.
Definition handler.hpp:115
@ REQUEST_PLACEMENT
Definition handler.hpp:182
scrollbar_mode get_scrollbar_mode(const std::string &scrollbar_mode)
Returns the scrollbar mode flags.
Definition helper.cpp:121
std::unique_ptr< class walker_base > walker_ptr
Definition widget.hpp:44
Generic file dialog.
static bool is_inserted_before(unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
static void adjust_scrollbar_mode(grid *scrollbar_grid, scrollbar_base *scrollbar, scrollbar_container::scrollbar_mode &scrollbar_mode, const unsigned items_before, const unsigned items_after, const int insertion_pos, const unsigned visible_items)
lg::log_domain log_gui_layout("gui/layout")
Definition log.hpp:54
static void set_scrollbar_mode(grid *scrollbar_grid, scrollbar_base *scrollbar, scrollbar_container::scrollbar_mode &scrollbar_mode, const unsigned items, const unsigned visible_items, grid *content_grid)
Contains the implementation details for lexical_cast and shouldn't be used directly.
int w
Definition pathfind.cpp:188
Contains the SDL_Rect helper code.
Helper for header for the scrollbar_container.
The message callbacks hold a reference to a message.
Definition message.hpp:46
Exception thrown when the width has been modified during resizing.
Holds a 2D point.
Definition point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
rect intersect(const rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
Definition rect.cpp:92