The Battle for Wesnoth  1.19.1+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 
24 #include "gui/core/log.hpp"
26 #include "gui/widgets/spacer.hpp"
27 #include "gui/widgets/window.hpp"
28 #include "sdl/rect.hpp"
29 
30 #include <algorithm>
31 #include <functional>
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 static const std::string button_up_names[]
43  { "_begin", "_line_up", "_half_page_up", "_page_up" };
44 
45 static const std::string button_down_names[]
46  { "_end", "_line_down", "_half_page_down", "_page_down" };
47 
48 /**
49  * Returns a map with the names of all buttons and the scrollbar jump they're
50  * supposed to execute.
51  */
52 const std::map<std::string, scrollbar_base::scroll_mode>& scroll_lookup()
53 {
54  static std::map<std::string, scrollbar_base::scroll_mode> lookup;
55  if(lookup.empty()) {
56  lookup["_begin"] = scrollbar_base::BEGIN;
57  lookup["_line_up"] = scrollbar_base::ITEM_BACKWARDS;
58  lookup["_half_page_up"] = scrollbar_base::HALF_JUMP_BACKWARDS;
59  lookup["_page_up"] = scrollbar_base::JUMP_BACKWARDS;
60 
61  lookup["_end"] = scrollbar_base::END;
62  lookup["_line_down"] = scrollbar_base::ITEM_FORWARD;
63  lookup["_half_page_down"] = scrollbar_base::HALF_JUMP_FORWARD;
64  lookup["_page_down"] = scrollbar_base::JUMP_FORWARD;
65  }
66 
67  return lookup;
68 }
69 
70 } // namespace
71 
73  const implementation::builder_styled_widget& builder, const std::string& control_type)
74  : container_base(builder, control_type)
75  , state_(ENABLED)
76  , vertical_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
77  , horizontal_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
78  , vertical_scrollbar_grid_(nullptr)
79  , horizontal_scrollbar_grid_(nullptr)
80  , vertical_scrollbar_(nullptr)
81  , horizontal_scrollbar_(nullptr)
82  , content_grid_(nullptr)
83  , content_(nullptr)
84  , content_visible_area_()
85 {
86  connect_signal<event::SDL_KEY_DOWN>(
87  std::bind(&scrollbar_container::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6));
88 
89  connect_signal<event::SDL_WHEEL_UP>(
90  std::bind(&scrollbar_container::signal_handler_sdl_wheel_up, this, std::placeholders::_2, std::placeholders::_3),
92 
93  connect_signal<event::SDL_WHEEL_DOWN>(
94  std::bind(&scrollbar_container::signal_handler_sdl_wheel_down, this, std::placeholders::_2, std::placeholders::_3),
96 
97  connect_signal<event::SDL_WHEEL_LEFT>(
98  std::bind(&scrollbar_container::signal_handler_sdl_wheel_left, this, std::placeholders::_2, std::placeholders::_3),
100 
101  connect_signal<event::SDL_WHEEL_RIGHT>(
102  std::bind(&scrollbar_container::signal_handler_sdl_wheel_right, this, std::placeholders::_2, std::placeholders::_3),
104 
105  connect_signal<event::SDL_TOUCH_MOTION>(
107  this,
108  std::placeholders::_2,
109  std::placeholders::_3,
110  std::placeholders::_5,
111  std::placeholders::_6),
113 }
114 
115 void scrollbar_container::layout_initialize(const bool full_initialization)
116 {
117  // Inherited.
118  container_base::layout_initialize(full_initialization);
119 
120  if(full_initialization) {
121  assert(vertical_scrollbar_grid_);
122 
123  switch(vertical_scrollbar_mode_) {
124  case ALWAYS_VISIBLE:
126  break;
127 
128  case AUTO_VISIBLE:
130  break;
131 
132  default:
134  }
135 
137 
139  case ALWAYS_VISIBLE:
141  break;
142 
143  case AUTO_VISIBLE:
145  break;
146 
147  default:
149  }
150  }
151 
152  assert(content_grid_);
153  content_grid_->layout_initialize(full_initialization);
154 }
155 
156 void scrollbar_container::request_reduce_height(const unsigned maximum_height)
157 {
158  DBG_GUI_L << LOG_HEADER << " requested height " << maximum_height << ".";
159 
160  /*
161  * First ask the content to reduce it's height. This seems to work for now,
162  * but maybe some sizing hints will be required later.
163  */
164  /** @todo Evaluate whether sizing hints are required. */
165  assert(content_grid_);
166  const unsigned offset =
169  : 0;
170 
171  content_grid_->request_reduce_height(maximum_height - offset);
172 
173  // Did we manage to achieve the wanted size?
175  if(static_cast<unsigned>(size.y) <= maximum_height) {
176  DBG_GUI_L << LOG_HEADER << " child honored request, height " << size.y << ".";
177  return;
178  }
179 
181  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.";
182  return;
183  }
184 
185  assert(vertical_scrollbar_grid_);
187 
188  // Always set the bar visible, is a nop is already visible.
190 
191  const point scrollbar_size = vertical_scrollbar_grid_->get_best_size();
192 
193  // If showing the scrollbar increased the height, hide and abort.
194  if(resized && scrollbar_size.y > size.y) {
196  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
197  << " increased the height to " << scrollbar_size.y << ".";
198  return;
199  }
200 
201  if(maximum_height > static_cast<unsigned>(scrollbar_size.y)) {
202  size.y = maximum_height;
203  } else {
204  size.y = scrollbar_size.y;
205  }
206 
207  // FIXME adjust for the step size of the scrollbar
208 
210  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.y << ".";
211 
212  if(resized) {
213  DBG_GUI_L << LOG_HEADER << " resize modified the width, throw notification.";
214 
216  }
217 }
218 
219 void scrollbar_container::request_reduce_width(const unsigned maximum_width)
220 {
221  DBG_GUI_L << LOG_HEADER << " requested width " << maximum_width << ".";
222 
223  if(static_cast<unsigned>(get_grid().get_best_size().x) > maximum_width) {
224  get_grid().request_reduce_width(maximum_width);
225  }
226 
227  // First ask our content, it might be able to wrap which looks better as
228  // a scrollbar.
229  assert(content_grid_);
230  const unsigned offset =
233  : 0;
234 
235  content_grid_->request_reduce_width(maximum_width - offset);
236 
237  // Did we manage to achieve the wanted size?
239  if(static_cast<unsigned>(size.x) <= maximum_width) {
240  DBG_GUI_L << LOG_HEADER << " child honored request, width " << size.x << ".";
241  return;
242  }
243 
245  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.";
246  return;
247  }
248 
249  // Always set the bar visible, is a nop when it's already visible.
252  size = get_best_size();
253 
254  point scrollbar_size = horizontal_scrollbar_grid_->get_best_size();
255 
256  /*
257  * If the vertical bar is not invisible it's size needs to be added to the
258  * minimum size.
259  */
261  scrollbar_size.x += vertical_scrollbar_grid_->get_best_size().x;
262  }
263 
264  // If showing the scrollbar increased the width, hide and abort.
265  if(horizontal_scrollbar_mode_ == AUTO_VISIBLE_FIRST_RUN && scrollbar_size.x > size.x) {
267  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
268  << " increased the width to " << scrollbar_size.x << ".";
269  return;
270  }
271 
272  if(maximum_width > static_cast<unsigned>(scrollbar_size.x)) {
273  size.x = maximum_width;
274  } else {
275  size.x = scrollbar_size.x;
276  }
277 
278  size.x = std::max(size.x, get_grid().get_best_size().x);
279 
280  // FIXME adjust for the step size of the scrollbar
281 
283  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.x << ".";
284 }
285 
287 {
288  return content_grid_ ? content_grid_->can_wrap() : false;
289 }
290 
292 {
294 
295  /***** get vertical scrollbar size *****/
297  ? point()
299 
300  /***** get horizontal scrollbar size *****/
302  ? point()
304 
305  /***** get content size *****/
306  assert(content_grid_);
307  const point content = content_grid_->get_best_size();
308 
309  point result(
310  vertical_scrollbar.x + std::max(horizontal_scrollbar.x, content.x),
311  horizontal_scrollbar.y + std::max(vertical_scrollbar.y, content.y));
312 
313  DBG_GUI_L << LOG_HEADER << " vertical_scrollbar " << vertical_scrollbar << " horizontal_scrollbar "
314  << horizontal_scrollbar << " content " << content << " result " << result << ".";
315 
316  return result;
317 }
318 
319 static void set_scrollbar_mode(grid* scrollbar_grid,
320  scrollbar_base* scrollbar,
322  const unsigned items,
323  const unsigned visible_items,
324  grid* content_grid)
325 {
326  assert(scrollbar_grid && scrollbar);
327 
330  return;
331  }
332 
333  scrollbar->set_item_count(items);
334  scrollbar->set_item_position(0);
335  scrollbar->set_visible_items(visible_items);
336 
338  const bool scrollbar_needed = items > visible_items;
339  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
341  if(items <= visible_items && content_grid != nullptr
342  && scrollbar_grid->get_visible() == widget::visibility::visible
343  ) {
345  // Give newly freed space to the items.
346  content_grid->layout_initialize(false);
347  }
348  }
349 }
350 static bool is_inserted_before(
351  unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
352 {
353  if(old_position == 0) {
354  return false;
355  } else if(old_position + visible_items >= old_item_count) {
356  return true;
357  } else if(insertion_pos <= old_position) {
358  return true;
359  }
360 
361  return false;
362 }
363 
364 static void adjust_scrollbar_mode(grid* scrollbar_grid,
365  scrollbar_base* scrollbar,
367  const unsigned items_before,
368  const unsigned items_after,
369  const int insertion_pos,
370  const unsigned visible_items)
371 {
372  assert(scrollbar_grid && scrollbar);
373  if(items_before != scrollbar->get_item_count()) {
374  return set_scrollbar_mode(scrollbar_grid, scrollbar, scrollbar_mode, items_after, visible_items, nullptr);
375  }
376 
377  // TODO: does this also work well in case the items were removed?
378  const unsigned previous_item_position = scrollbar->get_item_position();
379 
380  // Casts insertion_pos to an unsigned so negative values are interpreted as 'at end'
381  const bool inserted_before_visible_area = is_inserted_before(
382  static_cast<unsigned>(insertion_pos), items_before, previous_item_position, visible_items);
383 
386  return;
387  }
388 
389  scrollbar->set_item_count(items_after);
390  scrollbar->set_item_position(inserted_before_visible_area
391  ? previous_item_position + items_after - items_before
392  : previous_item_position);
393 
394  // scrollbar->set_item_position(0);
395  scrollbar->set_visible_items(visible_items);
396 
398  const bool scrollbar_needed = items_after > visible_items;
399  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
400  }
401 }
402 
403 void scrollbar_container::place(const point& origin, const point& size)
404 {
405  // Inherited.
406  container_base::place(origin, size);
407 
408  // Set content size
409  assert(content_ && content_grid_);
410 
411  const point content_origin = content_->get_origin();
412 
413  const point best_size = content_grid_->get_best_size();
414  const point content_size(content_->get_width(), content_->get_height());
415 
416  const point content_grid_size(std::max(best_size.x, content_size.x), std::max(best_size.y, content_size.y));
417 
418  set_content_size(content_origin, content_grid_size);
419 
420  // Set vertical scrollbar
425  content_grid_->get_height(),
426  content_->get_height(),
427  content_grid_.get()
428  );
429 
430  // Set horizontal scrollbar
435  content_grid_->get_width(),
436  content_->get_width(),
437  content_grid_.get()
438  );
439 
440  // Update the buttons.
442 
443  // Now set the visible part of the content.
445  content_grid_->set_visible_rectangle(content_visible_area_);
446 }
447 
449 {
450  // Inherited.
452 
453  // Set content size
454  assert(content_ && content_grid_);
455 
456  const point content_origin = content_->get_origin();
457 
458  content_grid_->set_origin(content_origin);
459 
460  // Changing the origin also invalidates the visible area.
461  content_grid_->set_visible_rectangle(content_visible_area_);
462 }
463 
464 void scrollbar_container::set_visible_rectangle(const SDL_Rect& rectangle)
465 {
466  // Inherited.
468 
469  // Now get the visible part of the content.
471 
472  content_grid_->set_visible_rectangle(content_visible_area_);
473 }
474 
476 {
477  return state_ != DISABLED;
478 }
479 
481 {
482  return state_;
483 }
484 
485 widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active)
486 {
487  widget* w = scrollbar_container_implementation::find_at<widget>(*this, coordinate, must_be_active);
488  if(w == nullptr) {
489  w = widget::find_at(coordinate, must_be_active);
490  }
491 
492  return w;
493 }
494 
495 const widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active) const
496 {
497  const widget* w = scrollbar_container_implementation::find_at<const widget>(*this, coordinate, must_be_active);
498  if(w == nullptr) {
499  w = widget::find_at(coordinate, must_be_active);
500  }
501 
502  return w;
503 }
504 
505 widget* scrollbar_container::find(const std::string& id, const bool must_be_active)
506 {
507  return scrollbar_container_implementation::find<widget>(*this, id, must_be_active);
508 }
509 
510 const widget* scrollbar_container::find(const std::string& id, const bool must_be_active) const
511 {
512  return scrollbar_container_implementation::find<const widget>(*this, id, must_be_active);
513 }
514 
516 {
517  assert(content_grid_);
518  return container_base::disable_click_dismiss() || content_grid_->disable_click_dismiss();
519 }
520 
522 {
523  return std::make_unique<gui2::iteration::scrollbar_container>(*this);
524 }
525 
526 bool scrollbar_container::content_resize_request(const bool force_sizing)
527 {
528  /**
529  * @todo Try to handle AUTO_VISIBLE_FIRST_RUN here as well.
530  *
531  * Handling it here makes the code a bit more complex but allows to not
532  * reserve space for scrollbars, which will look nicer in the MP lobby.
533  * But the extra complexity is no 1.8 material.
534  */
535 
536  assert(content_ && content_grid_);
537 
538  point best_size = content_grid_->recalculate_best_size();
540 
541  DBG_GUI_L << LOG_HEADER << " wanted size " << best_size << " available size " << size << ".";
542 
543  if(size == point()) {
544  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
545  return false;
546  }
547 
548  if(best_size.x <= size.x && best_size.y <= size.y) {
549  const point content_size = content_grid_->get_size();
550 
551  if(content_size.x > size.x || content_size.y > size.y) {
552  DBG_GUI_L << LOG_HEADER << " will fit, only needs a resize.";
553  goto resize;
554  }
555 
556  if(force_sizing) {
557  DBG_GUI_L << LOG_HEADER << " fits, but resize forced.";
558  goto resize;
559  }
560 
561  DBG_GUI_L << LOG_HEADER << " fits, nothing to do.";
562  return true;
563  }
564 
565  if(best_size.x > size.x) {
566  DBG_GUI_L << LOG_HEADER << " content too wide.";
567 
569  (
572  )
573  ) {
574  DBG_GUI_L << LOG_HEADER << " can't use horizontal scrollbar, request placement.";
575 
578  return false;
579  }
580  }
581 
582  if(best_size.y > size.y) {
583  DBG_GUI_L << LOG_HEADER << " content too high.";
584 
586  (
589  )
590  ) {
591  DBG_GUI_L << LOG_HEADER << " can't use vertical scrollbar, request placement.";
592 
595  return false;
596  }
597  }
598 
599 resize:
600  DBG_GUI_L << LOG_HEADER << " handle resizing.";
601 
602  place(get_origin(), get_size());
603  return true;
604 }
605 
606 bool scrollbar_container::content_resize_request(const int width_modification,
607  const int height_modification,
608  const int width_modification_pos,
609  const int height_modification_pos)
610 {
611  DBG_GUI_L << LOG_HEADER << " wanted width modification " << width_modification << " wanted height modification "
612  << height_modification << ".";
613 
614  if(get_size() == point()) {
615  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
616  return false;
617  }
618 
619  window* window = get_window();
620  assert(window);
621 
622  if(window->get_need_layout()) {
623  DBG_GUI_L << LOG_HEADER << " window already needs a layout phase, bailing out.";
624  return false;
625  }
626 
627  assert(content_ && content_grid_);
628 
629  const bool result =
630  content_resize_width(width_modification, width_modification_pos) &&
631  content_resize_height(height_modification, height_modification_pos);
632 
633  scrollbar_moved();
634 
635  /*
636  * The subroutines set the new size of the scrollbar but don't
637  * update the button status.
638  */
639  if(result) {
641  }
642 
643  DBG_GUI_L << LOG_HEADER << " result " << result << ".";
644  return result;
645 }
646 
647 bool scrollbar_container::content_resize_width(const int width_modification, const int width_modification_pos)
648 {
649  if(width_modification == 0) {
650  return true;
651  }
652 
653  const int new_width = content_grid_->get_width() + width_modification;
654  DBG_GUI_L << LOG_HEADER << " current width " << content_grid_->get_width() << " wanted width " << new_width;
655 
656  if(new_width < 0) {
657  return false;
658  }
659 
660  if(static_cast<unsigned>(new_width) <= content_->get_width()) {
661  DBG_GUI_L << " width fits in container, test height.";
662 
664  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
665  content_->get_width());
666  return true;
667  }
668 
671  (
674  )
675  ) {
676  DBG_GUI_L << " can't use horizontal scrollbar, ask window.";
677 
678  window* window = get_window();
679  assert(window);
680 
682  return false;
683  }
684 
685  DBG_GUI_L << " use the horizontal scrollbar, test height.";
687  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
688  content_->get_width());
689 
690  return true;
691 }
692 
693 bool scrollbar_container::content_resize_height(const int height_modification, const int height_modification_pos)
694 {
695  if(height_modification == 0) {
696  return true;
697  }
698 
699  const int new_height = content_grid_->get_height() + height_modification;
700 
701  DBG_GUI_L << LOG_HEADER << " current height " << content_grid_->get_height() << " wanted height " << new_height;
702 
703  if(new_height < 0) {
704  return false;
705  }
706 
707  if(static_cast<unsigned>(new_height) <= content_->get_height()) {
708  DBG_GUI_L << " height in container, resize allowed.";
709 
711  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
712  return true;
713  }
714 
717  (
720  )
721  ) {
722  DBG_GUI_L << " can't use vertical scrollbar, ask window.";
723 
724  window* window = get_window();
725  assert(window);
726 
728  return false;
729  }
730 
731  DBG_GUI_L << " use the vertical scrollbar, resize allowed.";
732 
734  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
735 
736  return true;
737 }
738 
740 {
741  /***** Setup vertical scrollbar *****/
742  vertical_scrollbar_grid_ = find_widget<grid>(this, "_vertical_scrollbar_grid", false, true);
743 
745  find_widget<scrollbar_base>(vertical_scrollbar_grid_, "_vertical_scrollbar", false, true);
746 
749 
750  /***** Setup horizontal scrollbar *****/
751  horizontal_scrollbar_grid_ = find_widget<grid>(this, "_horizontal_scrollbar_grid", false, true);
752 
754  find_widget<scrollbar_base>(horizontal_scrollbar_grid_, "_horizontal_scrollbar", false, true);
755 
758 
759  /***** Setup the scrollbar buttons *****/
760  for(const auto& item : scroll_lookup()) {
761  // Vertical.
762  clickable_item* button = find_widget<clickable_item>(vertical_scrollbar_grid_, item.first, false, false);
763 
764  if(button) {
766  std::bind(&scrollbar_container::scroll_vertical_scrollbar, this, item.second));
767  }
768 
769  // Horizontal.
770  button = find_widget<clickable_item>(horizontal_scrollbar_grid_, item.first, false, false);
771 
772  if(button) {
774  std::bind(&scrollbar_container::scroll_horizontal_scrollbar, this, item.second));
775  }
776  }
777 
778  /***** Setup the content *****/
779  auto content = build_single_widget_instance<spacer>();
780  content_ = content.get();
781 
782  // TODO: possibly move this unique_ptr casting functionality to a helper function.
783  content_grid_.reset(static_cast<grid*>(get_grid().swap_child("_content_grid", std::move(content), true).release()));
784  assert(content_grid_);
785 
786  content_grid_->set_parent(this);
787 
788  /***** Let our subclasses initialize themselves. *****/
790 }
791 
793 {
796  }
797 }
798 
800 {
803  }
804 }
805 
807 {
809 
810  // Inherited.
812 
813  content_grid_->draw_children();
814 }
815 
817 {
818  // Inherited.
820 
821  assert(content_grid_);
822  content_grid_->layout_children();
823 }
824 
826 {
827  content_grid_->place(origin, size);
828 }
829 
831 {
832  assert(content_);
834 
835  // Set the bottom right location first if it doesn't fit the top left
836  // will look good. First calculate the left and top position depending on
837  // the current position.
838 
839  const int left_position = horizontal_scrollbar_->get_item_position() + (rect.x - content_->get_x());
840  const int top_position = vertical_scrollbar_->get_item_position() + (rect.y - content_->get_y());
841 
842  // bottom.
843  const int wanted_bottom = rect.y + rect.h;
844  const int current_bottom = content_->get_y() + content_->get_height();
845 
846  int distance = wanted_bottom - current_bottom;
847  if(distance > 0) {
849  }
850 
851  // right.
852  const int wanted_right = rect.x + rect.w;
853  const int current_right = content_->get_x() + content_->get_width();
854 
855  distance = wanted_right - current_right;
856  if(distance > 0) {
858  }
859 
860  // top.
861  if(top_position < static_cast<int>(vertical_scrollbar_->get_item_position())) {
862  vertical_scrollbar_->set_item_position(top_position);
863  }
864 
865  // left.
866  if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
868  }
869 
870  // Update.
871  scrollbar_moved();
872 }
873 
875 {
876  if(true) { /** @todo scrollbar visibility. */
877  /***** set scroll up button status *****/
878  for(const auto& name : button_up_names) {
879  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
880 
881  if(button) {
883  }
884  }
885 
886  /***** set scroll down status *****/
887  for(const auto& name : button_down_names) {
888  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
889 
890  if(button) {
892  }
893  }
894 
895  /***** Set the status if the scrollbars *****/
897  }
898 
899  if(true) { /** @todo scrollbar visibility. */
900  /***** Set scroll left button status *****/
901  for(const auto& name : button_up_names) {
902  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
903 
904  if(button) {
906  }
907  }
908 
909  /***** Set scroll right button status *****/
910  for(const auto& name : button_down_names) {
911  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
912 
913  if(button) {
915  }
916  }
917 
918  /***** Set the status if the scrollbars *****/
920  }
921 }
922 
924 {
925  assert(vertical_scrollbar_);
926 
927  return vertical_scrollbar_->at_end();
928 }
929 
931 {
932  assert(vertical_scrollbar_);
933 
935 }
936 
938 {
939  assert(vertical_scrollbar_);
940 
942  scrollbar_moved();
943 }
944 
946 {
947  assert(horizontal_scrollbar_);
948 
950 }
951 
953 {
954  assert(horizontal_scrollbar_);
955 
957  scrollbar_moved();
958 }
959 
961 {
962  assert(vertical_scrollbar_);
963 
964  vertical_scrollbar_->scroll(scroll);
965  scrollbar_moved();
966 }
967 
969 {
970  assert(horizontal_scrollbar_);
971 
972  horizontal_scrollbar_->scroll(scroll);
973  scrollbar_moved();
974 }
975 
977 {
978  assert(vertical_scrollbar_);
979 
981  move_viewport(0, pixels);
982 }
983 
985 {
986  assert(horizontal_scrollbar_);
987 
989  move_viewport(pixels, 0);
990 }
991 
992 void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
993 {
995 
998  scrollbar_moved();
999 
1000  handled = true;
1001 }
1002 
1003 void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
1004 {
1005  assert(vertical_scrollbar_);
1006 
1008  scrollbar_moved();
1009 
1010  handled = true;
1011 }
1012 
1013 void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
1014 {
1015  assert(vertical_scrollbar_);
1016 
1018  scrollbar_moved();
1019 
1020  handled = true;
1021 }
1022 
1023 void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1024 
1025 {
1026  assert(vertical_scrollbar_);
1027 
1029  scrollbar_moved();
1030 
1031  handled = true;
1032 }
1033 
1034 void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1035 {
1036  assert(vertical_scrollbar_);
1037 
1039  scrollbar_moved();
1040 
1041  handled = true;
1042 }
1043 
1044 void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1045 {
1046  assert(vertical_scrollbar_);
1047 
1049  scrollbar_moved();
1050 
1051  handled = true;
1052 }
1053 
1054 void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1055 {
1056  assert(horizontal_scrollbar_);
1057 
1059  scrollbar_moved();
1060 
1061  handled = true;
1062 }
1063 
1064 void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1065 {
1066  assert(horizontal_scrollbar_);
1067 
1069  scrollbar_moved();
1070 
1071  handled = true;
1072 }
1073 
1075 {
1076  // Init.
1078 
1079  /*** Update the content location. ***/
1080  const int x_offset = horizontal_scrollbar_mode_ == ALWAYS_INVISIBLE
1081  ? 0
1083 
1084  const int y_offset = vertical_scrollbar_mode_ == ALWAYS_INVISIBLE
1085  ? 0
1087 
1088  move_viewport(x_offset, y_offset);
1089 }
1090 
1091 void scrollbar_container::move_viewport(const int pixels_x, const int pixels_y)
1092 {
1093  // Init.
1094  assert(content_ && content_grid_);
1095 
1096  const point content_origin {content_->get_x() - pixels_x, content_->get_y() - pixels_y};
1097 
1098  content_grid_->set_origin(content_origin);
1099  content_grid_->set_visible_rectangle(content_visible_area_);
1101 
1102  // Update scrollbar.
1104 }
1105 
1106 const std::string& scrollbar_container::type()
1107 {
1108  static const std::string type = "scrollbar_container";
1109  return type;
1110 }
1111 
1112 const std::string& scrollbar_container::get_control_type() const
1113 {
1114  return type();
1115 }
1116 
1118  const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier)
1119 {
1120  DBG_GUI_E << LOG_HEADER << event << ".";
1121 
1122  switch(key) {
1123  case SDLK_HOME:
1124  handle_key_home(modifier, handled);
1125  break;
1126 
1127  case SDLK_END:
1128  handle_key_end(modifier, handled);
1129  break;
1130 
1131  case SDLK_PAGEUP:
1132  handle_key_page_up(modifier, handled);
1133  break;
1134 
1135  case SDLK_PAGEDOWN:
1136  handle_key_page_down(modifier, handled);
1137  break;
1138 
1139  case SDLK_UP:
1140  handle_key_up_arrow(modifier, handled);
1141  break;
1142 
1143  case SDLK_DOWN:
1144  handle_key_down_arrow(modifier, handled);
1145  break;
1146 
1147  case SDLK_LEFT:
1148  handle_key_left_arrow(modifier, handled);
1149  break;
1150 
1151  case SDLK_RIGHT:
1152  handle_key_right_arrow(modifier, handled);
1153  break;
1154  default:
1155  /* ignore */
1156  break;
1157  }
1158 }
1159 
1161 {
1162  DBG_GUI_E << LOG_HEADER << event << ".";
1163 
1165 
1168  scrollbar_moved();
1169  handled = true;
1170  }
1171 }
1172 
1174 {
1175  DBG_GUI_E << LOG_HEADER << event << ".";
1176 
1178 
1181  scrollbar_moved();
1182  handled = true;
1183  }
1184 }
1185 
1187 {
1188  DBG_GUI_E << LOG_HEADER << event << ".";
1189 
1191 
1194  scrollbar_moved();
1195  handled = true;
1196  }
1197 }
1198 
1200 {
1201  DBG_GUI_E << LOG_HEADER << event << ".";
1202 
1204 
1207  scrollbar_moved();
1208  handled = true;
1209  }
1210 }
1211 
1212 void
1214  bool& handled,
1215  const point& position,
1216  const point& distance)
1217 {
1218  (void) position;
1219  DBG_GUI_E << LOG_HEADER << event << ".";
1220 
1221  bool is_scrollbar_moved = false;
1222 
1224 
1226  horizontal_scrollbar_->scroll_by(-distance.x);
1227  is_scrollbar_moved = true;
1228  }
1229  }
1230 
1232 
1234  vertical_scrollbar_->scroll_by(-distance.y);
1235  is_scrollbar_moved = true;
1236  }
1237  }
1238 
1239  if (is_scrollbar_moved) {
1240  scrollbar_moved();
1241  handled = true;
1242  }
1243 }
1244 
1245 
1246 
1247 } // 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:53
void set_layout_size(const point &size)
Definition: widget.cpp:336
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:193
void set_visible(const visibility visible)
Definition: widget.cpp:469
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:454
visibility get_visible() const
Definition: widget.cpp:496
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:301
int get_x() const
Definition: widget.cpp:316
unsigned get_width() const
Definition: widget.cpp:326
int get_y() const
Definition: widget.cpp:321
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:306
unsigned get_height() const
Definition: widget.cpp:331
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:311
virtual widget * find_at(const point &coordinate, const bool must_be_active)
Returns the widget at the wanted coordinates.
Definition: widget.cpp:539
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:773
bool get_need_layout() const
Definition: window.hpp:419
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:278
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:42
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)
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
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:90