The Battle for Wesnoth  1.19.7+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"
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 #include <string_view>
33 
34 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
35 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
36 
37 namespace gui2
38 {
39 
40 namespace
41 {
42 
43 using namespace std::literals;
44 constexpr std::array button_up_names { "_begin"sv, "_line_up"sv, "_half_page_up"sv, "_page_up"sv };
45 constexpr 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  */
51 const 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 
114 void scrollbar_container::layout_initialize(const bool full_initialization)
115 {
116  // Inherited.
117  container_base::layout_initialize(full_initialization);
118 
119  if(full_initialization) {
120  assert(vertical_scrollbar_grid_);
121 
122  switch(vertical_scrollbar_mode_) {
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 
155 void 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?
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 
184  assert(vertical_scrollbar_grid_);
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 
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 
218 void 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?
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 
253  point scrollbar_size = horizontal_scrollbar_grid_->get_best_size();
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 
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 
318 static 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 
329  return;
330  }
331 
332  scrollbar->set_item_count(items);
333  scrollbar->set_visible_items(visible_items);
334 
336  const bool scrollbar_needed = items > visible_items;
337  scrollbar_grid->set_hidden(scrollbar_needed);
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 }
348 static bool is_inserted_before(
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 
362 static 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 
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 
396  const bool scrollbar_needed = items_after > visible_items;
397  scrollbar_grid->set_hidden(scrollbar_needed);
398  }
399 }
400 
401 void 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
423  content_grid_->get_height(),
424  content_->get_height(),
425  content_grid_.get()
426  );
427 
428  // Set horizontal scrollbar
433  content_grid_->get_width(),
434  content_->get_width(),
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 
462 void scrollbar_container::set_visible_rectangle(const SDL_Rect& rectangle)
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 
483 widget* 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 
493 const 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 
503 widget* 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 
508 const 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 
524 bool scrollbar_container::content_resize_request(const bool force_sizing)
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();
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 
597 resize:
598  DBG_GUI_L << LOG_HEADER << " handle resizing.";
599 
600  place(get_origin(), get_size());
601  return true;
602 }
603 
604 bool 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 
617  window* window = get_window();
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 
631  scrollbar_moved();
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 
645 bool 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,
663  content_->get_width());
664  return true;
665  }
666 
669  (
672  )
673  ) {
674  DBG_GUI_L << " can't use horizontal scrollbar, ask window.";
675 
676  window* window = get_window();
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,
686  content_->get_width());
687 
688  return true;
689 }
690 
691 bool 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 
722  window* window = get_window();
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 
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 
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 
791 {
794  }
795 }
796 
798 {
801  }
802 }
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 
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())) {
860  vertical_scrollbar_->set_item_position(top_position);
861  }
862 
863  // left.
864  if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
866  }
867 
868  // Update.
869  scrollbar_moved();
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 
922 {
923  assert(vertical_scrollbar_);
924 
925  return vertical_scrollbar_->at_end();
926 }
927 
929 {
930  assert(vertical_scrollbar_);
931 
933 }
934 
936 {
937  assert(vertical_scrollbar_);
938 
940  scrollbar_moved();
941 }
942 
944 {
945  assert(horizontal_scrollbar_);
946 
948 }
949 
951 {
952  assert(horizontal_scrollbar_);
953 
955  scrollbar_moved();
956 }
957 
959 {
960  assert(vertical_scrollbar_);
961 
962  vertical_scrollbar_->scroll(scroll);
963  scrollbar_moved();
964 }
965 
967 {
968  assert(horizontal_scrollbar_);
969 
970  horizontal_scrollbar_->scroll(scroll);
971  scrollbar_moved();
972 }
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 
990 void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
991 {
993 
996  scrollbar_moved();
997 
998  handled = true;
999 }
1000 
1001 void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
1002 {
1003  assert(vertical_scrollbar_);
1004 
1006  scrollbar_moved();
1007 
1008  handled = true;
1009 }
1010 
1011 void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
1012 {
1013  assert(vertical_scrollbar_);
1014 
1016  scrollbar_moved();
1017 
1018  handled = true;
1019 }
1020 
1021 void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1022 
1023 {
1024  assert(vertical_scrollbar_);
1025 
1027  scrollbar_moved();
1028 
1029  handled = true;
1030 }
1031 
1032 void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1033 {
1034  assert(vertical_scrollbar_);
1035 
1037  scrollbar_moved();
1038 
1039  handled = true;
1040 }
1041 
1042 void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1043 {
1044  assert(vertical_scrollbar_);
1045 
1047  scrollbar_moved();
1048 
1049  handled = true;
1050 }
1051 
1052 void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1053 {
1054  assert(horizontal_scrollbar_);
1055 
1057  scrollbar_moved();
1058 
1059  handled = true;
1060 }
1061 
1062 void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1063 {
1064  assert(horizontal_scrollbar_);
1065 
1067  scrollbar_moved();
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 
1089 void 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 
1104 const std::string& scrollbar_container::type()
1105 {
1106  static const std::string type = "scrollbar_container";
1107  return type;
1108 }
1109 
1110 const std::string& scrollbar_container::get_control_type() const
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 
1159 {
1160  DBG_GUI_E << LOG_HEADER << event << ".";
1161 
1163 
1166  scrollbar_moved();
1167  handled = true;
1168  }
1169 }
1170 
1172 {
1173  DBG_GUI_E << LOG_HEADER << event << ".";
1174 
1176 
1179  scrollbar_moved();
1180  handled = true;
1181  }
1182 }
1183 
1185 {
1186  DBG_GUI_E << LOG_HEADER << event << ".";
1187 
1189 
1192  scrollbar_moved();
1193  handled = true;
1194  }
1195 }
1196 
1198 {
1199  DBG_GUI_E << LOG_HEADER << event << ".";
1200 
1202 
1205  scrollbar_moved();
1206  handled = true;
1207  }
1208 }
1209 
1210 void
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) {
1238  scrollbar_moved();
1239  handled = true;
1240  }
1241 }
1242 
1243 namespace implementation
1244 {
1246  : builder_styled_widget(cfg)
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:172
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.
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)
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.
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
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:
void set_hidden(bool hidden)
Sets widget to visible if hidden is true, else hidden.
Definition: widget.hpp:643
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
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:753
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:416
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:277
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
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.
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t size(std::string_view 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