The Battle for Wesnoth  1.19.5+dev
scrollbar_container.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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"
25 #include "gui/widgets/spacer.hpp"
26 #include "gui/widgets/window.hpp"
27 #include "sdl/rect.hpp"
28 
29 #include <algorithm>
30 #include <functional>
31 #include <string_view>
32 
33 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
34 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
35 
36 namespace gui2
37 {
38 
39 namespace
40 {
41 
42 using namespace std::literals;
43 constexpr std::array button_up_names { "_begin"sv, "_line_up"sv, "_half_page_up"sv, "_page_up"sv };
44 constexpr std::array button_down_names { "_end"sv, "_line_down"sv, "_half_page_down"sv, "_page_down"sv };
45 
46 /**
47  * Returns a map with the names of all buttons and the scrollbar jump they're
48  * supposed to execute.
49  */
50 const std::map<std::string, scrollbar_base::scroll_mode>& scroll_lookup()
51 {
52  static std::map<std::string, scrollbar_base::scroll_mode> lookup;
53  if(lookup.empty()) {
54  lookup["_begin"] = scrollbar_base::BEGIN;
55  lookup["_line_up"] = scrollbar_base::ITEM_BACKWARDS;
56  lookup["_half_page_up"] = scrollbar_base::HALF_JUMP_BACKWARDS;
57  lookup["_page_up"] = scrollbar_base::JUMP_BACKWARDS;
58 
59  lookup["_end"] = scrollbar_base::END;
60  lookup["_line_down"] = scrollbar_base::ITEM_FORWARD;
61  lookup["_half_page_down"] = scrollbar_base::HALF_JUMP_FORWARD;
62  lookup["_page_down"] = scrollbar_base::JUMP_FORWARD;
63  }
64 
65  return lookup;
66 }
67 
68 } // namespace
69 
71  const implementation::builder_styled_widget& builder, const std::string& control_type)
72  : container_base(builder, control_type)
73  , state_(ENABLED)
74  , vertical_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
75  , horizontal_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
76  , vertical_scrollbar_grid_(nullptr)
77  , horizontal_scrollbar_grid_(nullptr)
78  , vertical_scrollbar_(nullptr)
79  , horizontal_scrollbar_(nullptr)
80  , content_grid_(nullptr)
81  , content_(nullptr)
82  , content_visible_area_()
83 {
84  connect_signal<event::SDL_KEY_DOWN>(
85  std::bind(&scrollbar_container::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6));
86 
87  connect_signal<event::SDL_WHEEL_UP>(
88  std::bind(&scrollbar_container::signal_handler_sdl_wheel_up, this, std::placeholders::_2, std::placeholders::_3),
90 
91  connect_signal<event::SDL_WHEEL_DOWN>(
92  std::bind(&scrollbar_container::signal_handler_sdl_wheel_down, this, std::placeholders::_2, std::placeholders::_3),
94 
95  connect_signal<event::SDL_WHEEL_LEFT>(
96  std::bind(&scrollbar_container::signal_handler_sdl_wheel_left, this, std::placeholders::_2, std::placeholders::_3),
98 
99  connect_signal<event::SDL_WHEEL_RIGHT>(
100  std::bind(&scrollbar_container::signal_handler_sdl_wheel_right, this, std::placeholders::_2, std::placeholders::_3),
102 
103  connect_signal<event::SDL_TOUCH_MOTION>(
105  this,
106  std::placeholders::_2,
107  std::placeholders::_3,
108  std::placeholders::_5,
109  std::placeholders::_6),
111 }
112 
113 void scrollbar_container::layout_initialize(const bool full_initialization)
114 {
115  // Inherited.
116  container_base::layout_initialize(full_initialization);
117 
118  if(full_initialization) {
119  assert(vertical_scrollbar_grid_);
120 
121  switch(vertical_scrollbar_mode_) {
122  case ALWAYS_VISIBLE:
124  break;
125 
126  case AUTO_VISIBLE:
128  break;
129 
130  default:
132  }
133 
135 
137  case ALWAYS_VISIBLE:
139  break;
140 
141  case AUTO_VISIBLE:
143  break;
144 
145  default:
147  }
148  }
149 
150  assert(content_grid_);
151  content_grid_->layout_initialize(full_initialization);
152 }
153 
154 void scrollbar_container::request_reduce_height(const unsigned maximum_height)
155 {
156  DBG_GUI_L << LOG_HEADER << " requested height " << maximum_height << ".";
157 
158  /*
159  * First ask the content to reduce it's height. This seems to work for now,
160  * but maybe some sizing hints will be required later.
161  */
162  /** @todo Evaluate whether sizing hints are required. */
163  assert(content_grid_);
164  const unsigned offset =
167  : 0;
168 
169  content_grid_->request_reduce_height(maximum_height - offset);
170 
171  // Did we manage to achieve the wanted size?
173  if(static_cast<unsigned>(size.y) <= maximum_height) {
174  DBG_GUI_L << LOG_HEADER << " child honored request, height " << size.y << ".";
175  return;
176  }
177 
179  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.";
180  return;
181  }
182 
183  assert(vertical_scrollbar_grid_);
185 
186  // Always set the bar visible, is a nop is already visible.
188 
189  const point scrollbar_size = vertical_scrollbar_grid_->get_best_size();
190 
191  // If showing the scrollbar increased the height, hide and abort.
192  if(resized && scrollbar_size.y > size.y) {
194  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
195  << " increased the height to " << scrollbar_size.y << ".";
196  return;
197  }
198 
199  if(maximum_height > static_cast<unsigned>(scrollbar_size.y)) {
200  size.y = maximum_height;
201  } else {
202  size.y = scrollbar_size.y;
203  }
204 
205  // FIXME adjust for the step size of the scrollbar
206 
208  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.y << ".";
209 
210  if(resized) {
211  DBG_GUI_L << LOG_HEADER << " resize modified the width, throw notification.";
212 
214  }
215 }
216 
217 void scrollbar_container::request_reduce_width(const unsigned maximum_width)
218 {
219  DBG_GUI_L << LOG_HEADER << " requested width " << maximum_width << ".";
220 
221  if(static_cast<unsigned>(get_grid().get_best_size().x) > maximum_width) {
222  get_grid().request_reduce_width(maximum_width);
223  }
224 
225  // First ask our content, it might be able to wrap which looks better as
226  // a scrollbar.
227  assert(content_grid_);
228  const unsigned offset =
231  : 0;
232 
233  content_grid_->request_reduce_width(maximum_width - offset);
234 
235  // Did we manage to achieve the wanted size?
237  if(static_cast<unsigned>(size.x) <= maximum_width) {
238  DBG_GUI_L << LOG_HEADER << " child honored request, width " << size.x << ".";
239  return;
240  }
241 
243  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.";
244  return;
245  }
246 
247  // Always set the bar visible, is a nop when it's already visible.
250  size = get_best_size();
251 
252  point scrollbar_size = horizontal_scrollbar_grid_->get_best_size();
253 
254  /*
255  * If the vertical bar is not invisible it's size needs to be added to the
256  * minimum size.
257  */
259  scrollbar_size.x += vertical_scrollbar_grid_->get_best_size().x;
260  }
261 
262  // If showing the scrollbar increased the width, hide and abort.
263  if(horizontal_scrollbar_mode_ == AUTO_VISIBLE_FIRST_RUN && scrollbar_size.x > size.x) {
265  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
266  << " increased the width to " << scrollbar_size.x << ".";
267  return;
268  }
269 
270  if(maximum_width > static_cast<unsigned>(scrollbar_size.x)) {
271  size.x = maximum_width;
272  } else {
273  size.x = scrollbar_size.x;
274  }
275 
276  size.x = std::max(size.x, get_grid().get_best_size().x);
277 
278  // FIXME adjust for the step size of the scrollbar
279 
281  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.x << ".";
282 }
283 
285 {
286  return content_grid_ ? content_grid_->can_wrap() : false;
287 }
288 
290 {
292 
293  /***** get vertical scrollbar size *****/
295  ? point()
297 
298  /***** get horizontal scrollbar size *****/
300  ? point()
302 
303  /***** get content size *****/
304  assert(content_grid_);
305  const point content = content_grid_->get_best_size();
306 
307  point result(
308  vertical_scrollbar.x + std::max(horizontal_scrollbar.x, content.x),
309  horizontal_scrollbar.y + std::max(vertical_scrollbar.y, content.y));
310 
311  DBG_GUI_L << LOG_HEADER << " vertical_scrollbar " << vertical_scrollbar << " horizontal_scrollbar "
312  << horizontal_scrollbar << " content " << content << " result " << result << ".";
313 
314  return result;
315 }
316 
317 static void set_scrollbar_mode(grid* scrollbar_grid,
318  scrollbar_base* scrollbar,
320  const unsigned items,
321  const unsigned visible_items,
322  grid* content_grid)
323 {
324  assert(scrollbar_grid && scrollbar);
325 
328  return;
329  }
330 
331  scrollbar->set_item_count(items);
332  scrollbar->set_visible_items(visible_items);
333 
335  const bool scrollbar_needed = items > visible_items;
336  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
338  if(items <= visible_items && content_grid != nullptr
339  && scrollbar_grid->get_visible() == widget::visibility::visible
340  ) {
342  // Give newly freed space to the items.
343  content_grid->layout_initialize(false);
344  }
345  }
346 }
347 static bool is_inserted_before(
348  unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
349 {
350  if(old_position == 0) {
351  return false;
352  } else if(old_position + visible_items >= old_item_count) {
353  return true;
354  } else if(insertion_pos <= old_position) {
355  return true;
356  }
357 
358  return false;
359 }
360 
361 static void adjust_scrollbar_mode(grid* scrollbar_grid,
362  scrollbar_base* scrollbar,
364  const unsigned items_before,
365  const unsigned items_after,
366  const int insertion_pos,
367  const unsigned visible_items)
368 {
369  assert(scrollbar_grid && scrollbar);
370  if(items_before != scrollbar->get_item_count()) {
371  return set_scrollbar_mode(scrollbar_grid, scrollbar, scrollbar_mode, items_after, visible_items, nullptr);
372  }
373 
374  // TODO: does this also work well in case the items were removed?
375  const unsigned previous_item_position = scrollbar->get_item_position();
376 
377  // Casts insertion_pos to an unsigned so negative values are interpreted as 'at end'
378  const bool inserted_before_visible_area = is_inserted_before(
379  static_cast<unsigned>(insertion_pos), items_before, previous_item_position, visible_items);
380 
383  return;
384  }
385 
386  scrollbar->set_item_count(items_after);
387  scrollbar->set_item_position(inserted_before_visible_area
388  ? previous_item_position + items_after - items_before
389  : previous_item_position);
390 
391  // scrollbar->set_item_position(0);
392  scrollbar->set_visible_items(visible_items);
393 
395  const bool scrollbar_needed = items_after > visible_items;
396  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
397  }
398 }
399 
400 void scrollbar_container::place(const point& origin, const point& size)
401 {
402  // Inherited.
403  container_base::place(origin, size);
404 
405  // Set content size
406  assert(content_ && content_grid_);
407 
408  const point content_origin = content_->get_origin();
409 
410  const point best_size = content_grid_->get_best_size();
411  const point content_size(content_->get_width(), content_->get_height());
412 
413  const point content_grid_size(std::max(best_size.x, content_size.x), std::max(best_size.y, content_size.y));
414 
415  set_content_size(content_origin, content_grid_size);
416 
417  // Set vertical scrollbar
422  content_grid_->get_height(),
423  content_->get_height(),
424  content_grid_.get()
425  );
426 
427  // Set horizontal scrollbar
432  content_grid_->get_width(),
433  content_->get_width(),
434  content_grid_.get()
435  );
436 
437  // Update the buttons.
439 
440  // Now set the visible part of the content.
442  content_grid_->set_visible_rectangle(content_visible_area_);
443 }
444 
446 {
447  // Inherited.
449 
450  // Set content size
451  assert(content_ && content_grid_);
452 
453  const point content_origin = content_->get_origin();
454 
455  content_grid_->set_origin(content_origin);
456 
457  // Changing the origin also invalidates the visible area.
458  content_grid_->set_visible_rectangle(content_visible_area_);
459 }
460 
461 void scrollbar_container::set_visible_rectangle(const SDL_Rect& rectangle)
462 {
463  // Inherited.
465 
466  // Now get the visible part of the content.
468 
469  content_grid_->set_visible_rectangle(content_visible_area_);
470 }
471 
473 {
474  return state_ != DISABLED;
475 }
476 
478 {
479  return state_;
480 }
481 
482 widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active)
483 {
484  widget* w = scrollbar_container_implementation::find_at<widget>(*this, coordinate, must_be_active);
485  if(w == nullptr) {
486  w = widget::find_at(coordinate, must_be_active);
487  }
488 
489  return w;
490 }
491 
492 const widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active) const
493 {
494  const widget* w = scrollbar_container_implementation::find_at<const widget>(*this, coordinate, must_be_active);
495  if(w == nullptr) {
496  w = widget::find_at(coordinate, must_be_active);
497  }
498 
499  return w;
500 }
501 
502 widget* scrollbar_container::find(const std::string& id, const bool must_be_active)
503 {
504  return scrollbar_container_implementation::find<widget>(*this, id, must_be_active);
505 }
506 
507 const widget* scrollbar_container::find(const std::string& id, const bool must_be_active) const
508 {
509  return scrollbar_container_implementation::find<const widget>(*this, id, must_be_active);
510 }
511 
513 {
514  assert(content_grid_);
515  return container_base::disable_click_dismiss() || content_grid_->disable_click_dismiss();
516 }
517 
519 {
520  return std::make_unique<gui2::iteration::scrollbar_container>(*this);
521 }
522 
523 bool scrollbar_container::content_resize_request(const bool force_sizing)
524 {
525  /**
526  * @todo Try to handle AUTO_VISIBLE_FIRST_RUN here as well.
527  *
528  * Handling it here makes the code a bit more complex but allows to not
529  * reserve space for scrollbars, which will look nicer in the MP lobby.
530  * But the extra complexity is no 1.8 material.
531  */
532 
533  assert(content_ && content_grid_);
534 
535  point best_size = content_grid_->recalculate_best_size();
537 
538  DBG_GUI_L << LOG_HEADER << " wanted size " << best_size << " available size " << size << ".";
539 
540  if(size == point()) {
541  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
542  return false;
543  }
544 
545  if(best_size.x <= size.x && best_size.y <= size.y) {
546  const point content_size = content_grid_->get_size();
547 
548  if(content_size.x > size.x || content_size.y > size.y) {
549  DBG_GUI_L << LOG_HEADER << " will fit, only needs a resize.";
550  goto resize;
551  }
552 
553  if(force_sizing) {
554  DBG_GUI_L << LOG_HEADER << " fits, but resize forced.";
555  goto resize;
556  }
557 
558  DBG_GUI_L << LOG_HEADER << " fits, nothing to do.";
559  return true;
560  }
561 
562  if(best_size.x > size.x) {
563  DBG_GUI_L << LOG_HEADER << " content too wide.";
564 
566  (
569  )
570  ) {
571  DBG_GUI_L << LOG_HEADER << " can't use horizontal scrollbar, request placement.";
572 
575  return false;
576  }
577  }
578 
579  if(best_size.y > size.y) {
580  DBG_GUI_L << LOG_HEADER << " content too high.";
581 
583  (
586  )
587  ) {
588  DBG_GUI_L << LOG_HEADER << " can't use vertical scrollbar, request placement.";
589 
592  return false;
593  }
594  }
595 
596 resize:
597  DBG_GUI_L << LOG_HEADER << " handle resizing.";
598 
599  place(get_origin(), get_size());
600  return true;
601 }
602 
603 bool scrollbar_container::content_resize_request(const int width_modification,
604  const int height_modification,
605  const int width_modification_pos,
606  const int height_modification_pos)
607 {
608  DBG_GUI_L << LOG_HEADER << " wanted width modification " << width_modification << " wanted height modification "
609  << height_modification << ".";
610 
611  if(get_size() == point()) {
612  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
613  return false;
614  }
615 
616  window* window = get_window();
617  assert(window);
618 
619  if(window->get_need_layout()) {
620  DBG_GUI_L << LOG_HEADER << " window already needs a layout phase, bailing out.";
621  return false;
622  }
623 
624  assert(content_ && content_grid_);
625 
626  const bool result =
627  content_resize_width(width_modification, width_modification_pos) &&
628  content_resize_height(height_modification, height_modification_pos);
629 
630  scrollbar_moved();
631 
632  /*
633  * The subroutines set the new size of the scrollbar but don't
634  * update the button status.
635  */
636  if(result) {
638  }
639 
640  DBG_GUI_L << LOG_HEADER << " result " << result << ".";
641  return result;
642 }
643 
644 bool scrollbar_container::content_resize_width(const int width_modification, const int width_modification_pos)
645 {
646  if(width_modification == 0) {
647  return true;
648  }
649 
650  const int new_width = content_grid_->get_width() + width_modification;
651  DBG_GUI_L << LOG_HEADER << " current width " << content_grid_->get_width() << " wanted width " << new_width;
652 
653  if(new_width < 0) {
654  return false;
655  }
656 
657  if(static_cast<unsigned>(new_width) <= content_->get_width()) {
658  DBG_GUI_L << " width fits in container, test height.";
659 
661  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
662  content_->get_width());
663  return true;
664  }
665 
668  (
671  )
672  ) {
673  DBG_GUI_L << " can't use horizontal scrollbar, ask window.";
674 
675  window* window = get_window();
676  assert(window);
677 
679  return false;
680  }
681 
682  DBG_GUI_L << " use the horizontal scrollbar, test height.";
684  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
685  content_->get_width());
686 
687  return true;
688 }
689 
690 bool scrollbar_container::content_resize_height(const int height_modification, const int height_modification_pos)
691 {
692  if(height_modification == 0) {
693  return true;
694  }
695 
696  const int new_height = content_grid_->get_height() + height_modification;
697 
698  DBG_GUI_L << LOG_HEADER << " current height " << content_grid_->get_height() << " wanted height " << new_height;
699 
700  if(new_height < 0) {
701  return false;
702  }
703 
704  if(static_cast<unsigned>(new_height) <= content_->get_height()) {
705  DBG_GUI_L << " height in container, resize allowed.";
706 
708  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
709  return true;
710  }
711 
714  (
717  )
718  ) {
719  DBG_GUI_L << " can't use vertical scrollbar, ask window.";
720 
721  window* window = get_window();
722  assert(window);
723 
725  return false;
726  }
727 
728  DBG_GUI_L << " use the vertical scrollbar, resize allowed.";
729 
731  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
732 
733  return true;
734 }
735 
737 {
738  /***** Setup vertical scrollbar *****/
739  vertical_scrollbar_grid_ = find_widget<grid>("_vertical_scrollbar_grid", false, true);
740 
742  vertical_scrollbar_grid_->find_widget<scrollbar_base>("_vertical_scrollbar", false, true);
743 
746 
747  /***** Setup horizontal scrollbar *****/
748  horizontal_scrollbar_grid_ = find_widget<grid>("_horizontal_scrollbar_grid", false, true);
749 
751  horizontal_scrollbar_grid_->find_widget<scrollbar_base>("_horizontal_scrollbar", false, true);
752 
755 
756  /***** Setup the scrollbar buttons *****/
757  for(const auto& item : scroll_lookup()) {
758  // Vertical.
760 
761  if(button) {
763  std::bind(&scrollbar_container::scroll_vertical_scrollbar, this, item.second));
764  }
765 
766  // Horizontal.
767  button = horizontal_scrollbar_grid_->find_widget<clickable_item>(item.first, false, false);
768 
769  if(button) {
771  std::bind(&scrollbar_container::scroll_horizontal_scrollbar, this, item.second));
772  }
773  }
774 
775  /***** Setup the content *****/
776  auto content = build_single_widget_instance<spacer>();
777  content_ = content.get();
778 
779  // TODO: possibly move this unique_ptr casting functionality to a helper function.
780  content_grid_.reset(static_cast<grid*>(get_grid().swap_child("_content_grid", std::move(content), true).release()));
781  assert(content_grid_);
782 
783  content_grid_->set_parent(this);
784 
785  /***** Let our subclasses initialize themselves. *****/
787 }
788 
790 {
793  }
794 }
795 
797 {
800  }
801 }
802 
804 {
806 
807  // Inherited.
809 
810  content_grid_->draw_children();
811 }
812 
814 {
815  // Inherited.
817 
818  assert(content_grid_);
819  content_grid_->layout_children();
820 }
821 
823 {
824  content_grid_->place(origin, size);
825 }
826 
828 {
829  assert(content_);
831 
832  // Set the bottom right location first if it doesn't fit the top left
833  // will look good. First calculate the left and top position depending on
834  // the current position.
835 
836  const int left_position = horizontal_scrollbar_->get_item_position() + (rect.x - content_->get_x());
837  const int top_position = vertical_scrollbar_->get_item_position() + (rect.y - content_->get_y());
838 
839  // bottom.
840  const int wanted_bottom = rect.y + rect.h;
841  const int current_bottom = content_->get_y() + content_->get_height();
842 
843  int distance = wanted_bottom - current_bottom;
844  if(distance > 0) {
846  }
847 
848  // right.
849  const int wanted_right = rect.x + rect.w;
850  const int current_right = content_->get_x() + content_->get_width();
851 
852  distance = wanted_right - current_right;
853  if(distance > 0) {
855  }
856 
857  // top.
858  if(top_position < static_cast<int>(vertical_scrollbar_->get_item_position())) {
859  vertical_scrollbar_->set_item_position(top_position);
860  }
861 
862  // left.
863  if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
865  }
866 
867  // Update.
868  scrollbar_moved();
869 }
870 
872 {
873  if(true) { /** @todo scrollbar visibility. */
874  /***** set scroll up button status *****/
875  for(const auto& name : button_up_names) {
876  styled_widget* button = vertical_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
877 
878  if(button) {
880  }
881  }
882 
883  /***** set scroll down status *****/
884  for(const auto& name : button_down_names) {
885  styled_widget* button = vertical_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
886 
887  if(button) {
889  }
890  }
891 
892  /***** Set the status if the scrollbars *****/
894  }
895 
896  if(true) { /** @todo scrollbar visibility. */
897  /***** Set scroll left button status *****/
898  for(const auto& name : button_up_names) {
899  styled_widget* button = horizontal_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
900 
901  if(button) {
903  }
904  }
905 
906  /***** Set scroll right button status *****/
907  for(const auto& name : button_down_names) {
908  styled_widget* button = horizontal_scrollbar_grid_->find_widget<styled_widget>(std::string{name}, false, false);
909 
910  if(button) {
912  }
913  }
914 
915  /***** Set the status if the scrollbars *****/
917  }
918 }
919 
921 {
922  assert(vertical_scrollbar_);
923 
924  return vertical_scrollbar_->at_end();
925 }
926 
928 {
929  assert(vertical_scrollbar_);
930 
932 }
933 
935 {
936  assert(vertical_scrollbar_);
937 
939  scrollbar_moved();
940 }
941 
943 {
944  assert(horizontal_scrollbar_);
945 
947 }
948 
950 {
951  assert(horizontal_scrollbar_);
952 
954  scrollbar_moved();
955 }
956 
958 {
959  assert(vertical_scrollbar_);
960 
961  vertical_scrollbar_->scroll(scroll);
962  scrollbar_moved();
963 }
964 
966 {
967  assert(horizontal_scrollbar_);
968 
969  horizontal_scrollbar_->scroll(scroll);
970  scrollbar_moved();
971 }
972 
974 {
975  assert(vertical_scrollbar_);
976 
978  move_viewport(0, pixels);
979 }
980 
982 {
983  assert(horizontal_scrollbar_);
984 
986  move_viewport(pixels, 0);
987 }
988 
989 void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
990 {
992 
995  scrollbar_moved();
996 
997  handled = true;
998 }
999 
1000 void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
1001 {
1002  assert(vertical_scrollbar_);
1003 
1005  scrollbar_moved();
1006 
1007  handled = true;
1008 }
1009 
1010 void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
1011 {
1012  assert(vertical_scrollbar_);
1013 
1015  scrollbar_moved();
1016 
1017  handled = true;
1018 }
1019 
1020 void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1021 
1022 {
1023  assert(vertical_scrollbar_);
1024 
1026  scrollbar_moved();
1027 
1028  handled = true;
1029 }
1030 
1031 void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1032 {
1033  assert(vertical_scrollbar_);
1034 
1036  scrollbar_moved();
1037 
1038  handled = true;
1039 }
1040 
1041 void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1042 {
1043  assert(vertical_scrollbar_);
1044 
1046  scrollbar_moved();
1047 
1048  handled = true;
1049 }
1050 
1051 void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1052 {
1053  assert(horizontal_scrollbar_);
1054 
1056  scrollbar_moved();
1057 
1058  handled = true;
1059 }
1060 
1061 void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1062 {
1063  assert(horizontal_scrollbar_);
1064 
1066  scrollbar_moved();
1067 
1068  handled = true;
1069 }
1070 
1072 {
1073  // Init.
1075 
1076  /*** Update the content location. ***/
1077  const int x_offset = horizontal_scrollbar_mode_ == ALWAYS_INVISIBLE
1078  ? 0
1080 
1081  const int y_offset = vertical_scrollbar_mode_ == ALWAYS_INVISIBLE
1082  ? 0
1084 
1085  move_viewport(x_offset, y_offset);
1086 }
1087 
1088 void scrollbar_container::move_viewport(const int pixels_x, const int pixels_y)
1089 {
1090  // Initialize
1091  assert(content_ && content_grid_);
1092 
1093  const point content_origin {content_->get_x() - pixels_x, content_->get_y() - pixels_y};
1094 
1095  content_grid_->set_origin(content_origin);
1096  content_grid_->set_visible_rectangle(content_visible_area_);
1098 
1099  // Update scrollbar
1101 }
1102 
1103 const std::string& scrollbar_container::type()
1104 {
1105  static const std::string type = "scrollbar_container";
1106  return type;
1107 }
1108 
1109 const std::string& scrollbar_container::get_control_type() const
1110 {
1111  return type();
1112 }
1113 
1115  const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier)
1116 {
1117  DBG_GUI_E << LOG_HEADER << event << ".";
1118 
1119  switch(key) {
1120  case SDLK_HOME:
1121  handle_key_home(modifier, handled);
1122  break;
1123 
1124  case SDLK_END:
1125  handle_key_end(modifier, handled);
1126  break;
1127 
1128  case SDLK_PAGEUP:
1129  handle_key_page_up(modifier, handled);
1130  break;
1131 
1132  case SDLK_PAGEDOWN:
1133  handle_key_page_down(modifier, handled);
1134  break;
1135 
1136  case SDLK_UP:
1137  handle_key_up_arrow(modifier, handled);
1138  break;
1139 
1140  case SDLK_DOWN:
1141  handle_key_down_arrow(modifier, handled);
1142  break;
1143 
1144  case SDLK_LEFT:
1145  handle_key_left_arrow(modifier, handled);
1146  break;
1147 
1148  case SDLK_RIGHT:
1149  handle_key_right_arrow(modifier, handled);
1150  break;
1151  default:
1152  /* ignore */
1153  break;
1154  }
1155 }
1156 
1158 {
1159  DBG_GUI_E << LOG_HEADER << event << ".";
1160 
1162 
1165  scrollbar_moved();
1166  handled = true;
1167  }
1168 }
1169 
1171 {
1172  DBG_GUI_E << LOG_HEADER << event << ".";
1173 
1175 
1178  scrollbar_moved();
1179  handled = true;
1180  }
1181 }
1182 
1184 {
1185  DBG_GUI_E << LOG_HEADER << event << ".";
1186 
1188 
1191  scrollbar_moved();
1192  handled = true;
1193  }
1194 }
1195 
1197 {
1198  DBG_GUI_E << LOG_HEADER << event << ".";
1199 
1201 
1204  scrollbar_moved();
1205  handled = true;
1206  }
1207 }
1208 
1209 void
1211  bool& handled,
1212  const point& position,
1213  const point& distance)
1214 {
1215  (void) position;
1216  DBG_GUI_E << LOG_HEADER << event << ".";
1217 
1218  bool is_scrollbar_moved = false;
1219 
1221 
1223  horizontal_scrollbar_->scroll_by(-distance.x);
1224  is_scrollbar_moved = true;
1225  }
1226  }
1227 
1229 
1231  vertical_scrollbar_->scroll_by(-distance.y);
1232  is_scrollbar_moved = true;
1233  }
1234  }
1235 
1236  if (is_scrollbar_moved) {
1237  scrollbar_moved();
1238  handled = true;
1239  }
1240 }
1241 
1242 
1243 
1244 } // namespace gui2
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 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 set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
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.
Definition: dispatcher.cpp:74
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.
Definition: scrollbar.cpp:152
unsigned get_item_position() const
Definition: scrollbar.hpp:142
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
Definition: scrollbar.hpp:158
void set_item_count(const unsigned item_count)
Definition: scrollbar.hpp:127
unsigned get_item_count() const
Definition: scrollbar.hpp:132
void set_visible_items(const unsigned visible_items)
Definition: scrollbar.hpp:152
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.
Definition: scrollbar.cpp:135
SDL_Rect content_visible_area_
Cache for the visible area for the content.
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.
virtual void finalize_subclass()
Function for the subclasses to do their setup.
state_t state_
Current state of the widget.
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
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.
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.
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
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 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)
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.
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.
scrollbar_container(const implementation::builder_styled_widget &builder, const std::string &control_type)
grid * vertical_scrollbar_grid_
These are valid after finalize_setup().
virtual void impl_draw_children() override
See widget::impl_draw_children.
scrollbar_base * horizontal_scrollbar_
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).
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
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
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
NOT_DANGLING T * find_widget(const std::string &id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:742
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:61
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:761
bool get_need_layout() const
Definition: window.hpp:417
This file contains the definitions for the gui2::event::message class.
int w
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
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:279
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
@ REQUEST_PLACEMENT
Definition: handler.hpp:163
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
scrollbar_container::scrollbar_mode scrollbar_mode
Definition: helper.hpp:31
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)
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
Contains the SDL_Rect helper code.
#define LOG_HEADER
#define LOG_SCOPE_HEADER
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:47
rect intersect(const SDL_Rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
Definition: rect.cpp:91