The Battle for Wesnoth  1.17.17+dev
scrollbar_container.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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 namespace
39 {
40 
41 static const std::string button_up_names[]
42  { "_begin", "_line_up", "_half_page_up", "_page_up" };
43 
44 static const std::string button_down_names[]
45  { "_end", "_line_down", "_half_page_down", "_page_down" };
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_styled_widget& builder, const std::string& control_type)
73  : container_base(builder, control_type)
74  , state_(ENABLED)
75  , vertical_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
76  , horizontal_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
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_item_position(0);
334  scrollbar->set_visible_items(visible_items);
335 
337  const bool scrollbar_needed = items > visible_items;
338  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
340  if(items <= visible_items && content_grid != nullptr
341  && scrollbar_grid->get_visible() == widget::visibility::visible
342  ) {
344  // Give newly freed space to the items.
345  content_grid->layout_initialize(false);
346  }
347  }
348 }
349 static bool is_inserted_before(
350  unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
351 {
352  if(old_position == 0) {
353  return false;
354  } else if(old_position + visible_items >= old_item_count) {
355  return true;
356  } else if(insertion_pos <= old_position) {
357  return true;
358  }
359 
360  return false;
361 }
362 
363 static void adjust_scrollbar_mode(grid* scrollbar_grid,
364  scrollbar_base* scrollbar,
366  const unsigned items_before,
367  const unsigned items_after,
368  const int insertion_pos,
369  const unsigned visible_items)
370 {
371  assert(scrollbar_grid && scrollbar);
372  if(items_before != scrollbar->get_item_count()) {
373  return set_scrollbar_mode(scrollbar_grid, scrollbar, scrollbar_mode, items_after, visible_items, nullptr);
374  }
375 
376  // TODO: does this also work well in case the items were removed?
377  const unsigned previous_item_position = scrollbar->get_item_position();
378 
379  // Casts insertion_pos to an unsigned so negative values are interpreted as 'at end'
380  const bool inserted_before_visible_area = is_inserted_before(
381  static_cast<unsigned>(insertion_pos), items_before, previous_item_position, visible_items);
382 
385  return;
386  }
387 
388  scrollbar->set_item_count(items_after);
389  scrollbar->set_item_position(inserted_before_visible_area
390  ? previous_item_position + items_after - items_before
391  : previous_item_position);
392 
393  // scrollbar->set_item_position(0);
394  scrollbar->set_visible_items(visible_items);
395 
397  const bool scrollbar_needed = items_after > visible_items;
398  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
399  }
400 }
401 
402 void scrollbar_container::place(const point& origin, const point& size)
403 {
404  // Inherited.
405  container_base::place(origin, size);
406 
407  // Set content size
408  assert(content_ && content_grid_);
409 
410  const point content_origin = content_->get_origin();
411 
412  const point best_size = content_grid_->get_best_size();
413  const point content_size(content_->get_width(), content_->get_height());
414 
415  const point content_grid_size(std::max(best_size.x, content_size.x), std::max(best_size.y, content_size.y));
416 
417  set_content_size(content_origin, content_grid_size);
418 
419  // Set vertical scrollbar
424  content_grid_->get_height(),
425  content_->get_height(),
426  content_grid_.get()
427  );
428 
429  // Set horizontal scrollbar
434  content_grid_->get_width(),
435  content_->get_width(),
436  content_grid_.get()
437  );
438 
439  // Update the buttons.
441 
442  // Now set the visible part of the content.
444  content_grid_->set_visible_rectangle(content_visible_area_);
445 }
446 
448 {
449  // Inherited.
451 
452  // Set content size
453  assert(content_ && content_grid_);
454 
455  const point content_origin = content_->get_origin();
456 
457  content_grid_->set_origin(content_origin);
458 
459  // Changing the origin also invalidates the visible area.
460  content_grid_->set_visible_rectangle(content_visible_area_);
461 }
462 
463 void scrollbar_container::set_visible_rectangle(const SDL_Rect& rectangle)
464 {
465  // Inherited.
467 
468  // Now get the visible part of the content.
470 
471  content_grid_->set_visible_rectangle(content_visible_area_);
472 }
473 
475 {
476  return state_ != DISABLED;
477 }
478 
480 {
481  return state_;
482 }
483 
484 widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active)
485 {
486  widget* w = scrollbar_container_implementation::find_at<widget>(*this, coordinate, must_be_active);
487  if(w == nullptr) {
488  w = widget::find_at(coordinate, must_be_active);
489  }
490 
491  return w;
492 }
493 
494 const widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active) const
495 {
496  const widget* w = scrollbar_container_implementation::find_at<const widget>(*this, coordinate, must_be_active);
497  if(w == nullptr) {
498  w = widget::find_at(coordinate, must_be_active);
499  }
500 
501  return w;
502 }
503 
504 widget* scrollbar_container::find(const std::string& id, const bool must_be_active)
505 {
506  return scrollbar_container_implementation::find<widget>(*this, id, must_be_active);
507 }
508 
509 const widget* scrollbar_container::find(const std::string& id, const bool must_be_active) const
510 {
511  return scrollbar_container_implementation::find<const widget>(*this, id, must_be_active);
512 }
513 
515 {
516  assert(content_grid_);
517  return container_base::disable_click_dismiss() || content_grid_->disable_click_dismiss();
518 }
519 
521 {
522  return std::make_unique<gui2::iteration::scrollbar_container>(*this);
523 }
524 
525 bool scrollbar_container::content_resize_request(const bool force_sizing)
526 {
527  /**
528  * @todo Try to handle AUTO_VISIBLE_FIRST_RUN here as well.
529  *
530  * Handling it here makes the code a bit more complex but allows to not
531  * reserve space for scrollbars, which will look nicer in the MP lobby.
532  * But the extra complexity is no 1.8 material.
533  */
534 
535  assert(content_ && content_grid_);
536 
537  point best_size = content_grid_->recalculate_best_size();
539 
540  DBG_GUI_L << LOG_HEADER << " wanted size " << best_size << " available size " << size << ".";
541 
542  if(size == point()) {
543  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
544  return false;
545  }
546 
547  if(best_size.x <= size.x && best_size.y <= size.y) {
548  const point content_size = content_grid_->get_size();
549 
550  if(content_size.x > size.x || content_size.y > size.y) {
551  DBG_GUI_L << LOG_HEADER << " will fit, only needs a resize.";
552  goto resize;
553  }
554 
555  if(force_sizing) {
556  DBG_GUI_L << LOG_HEADER << " fits, but resize forced.";
557  goto resize;
558  }
559 
560  DBG_GUI_L << LOG_HEADER << " fits, nothing to do.";
561  return true;
562  }
563 
564  if(best_size.x > size.x) {
565  DBG_GUI_L << LOG_HEADER << " content too wide.";
566 
568  (
571  )
572  ) {
573  DBG_GUI_L << LOG_HEADER << " can't use horizontal scrollbar, request placement.";
574 
577  return false;
578  }
579  }
580 
581  if(best_size.y > size.y) {
582  DBG_GUI_L << LOG_HEADER << " content too high.";
583 
585  (
588  )
589  ) {
590  DBG_GUI_L << LOG_HEADER << " can't use vertical scrollbar, request placement.";
591 
594  return false;
595  }
596  }
597 
598 resize:
599  DBG_GUI_L << LOG_HEADER << " handle resizing.";
600 
601  place(get_origin(), get_size());
602  return true;
603 }
604 
605 bool scrollbar_container::content_resize_request(const int width_modification,
606  const int height_modification,
607  const int width_modification_pos,
608  const int height_modification_pos)
609 {
610  DBG_GUI_L << LOG_HEADER << " wanted width modification " << width_modification << " wanted height modification "
611  << height_modification << ".";
612 
613  if(get_size() == point()) {
614  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.";
615  return false;
616  }
617 
618  window* window = get_window();
619  assert(window);
620 
621  if(window->get_need_layout()) {
622  DBG_GUI_L << LOG_HEADER << " window already needs a layout phase, bailing out.";
623  return false;
624  }
625 
626  assert(content_ && content_grid_);
627 
628  const bool result =
629  content_resize_width(width_modification, width_modification_pos) &&
630  content_resize_height(height_modification, height_modification_pos);
631 
632  scrollbar_moved();
633 
634  /*
635  * The subroutines set the new size of the scrollbar but don't
636  * update the button status.
637  */
638  if(result) {
640  }
641 
642  DBG_GUI_L << LOG_HEADER << " result " << result << ".";
643  return result;
644 }
645 
646 bool scrollbar_container::content_resize_width(const int width_modification, const int width_modification_pos)
647 {
648  if(width_modification == 0) {
649  return true;
650  }
651 
652  const int new_width = content_grid_->get_width() + width_modification;
653  DBG_GUI_L << LOG_HEADER << " current width " << content_grid_->get_width() << " wanted width " << new_width;
654 
655  if(new_width < 0) {
656  return false;
657  }
658 
659  if(static_cast<unsigned>(new_width) <= content_->get_width()) {
660  DBG_GUI_L << " width fits in container, test height.";
661 
663  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
664  content_->get_width());
665  return true;
666  }
667 
670  (
673  )
674  ) {
675  DBG_GUI_L << " can't use horizontal scrollbar, ask window.";
676 
677  window* window = get_window();
678  assert(window);
679 
681  return false;
682  }
683 
684  DBG_GUI_L << " use the horizontal scrollbar, test height.";
686  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
687  content_->get_width());
688 
689  return true;
690 }
691 
692 bool scrollbar_container::content_resize_height(const int height_modification, const int height_modification_pos)
693 {
694  if(height_modification == 0) {
695  return true;
696  }
697 
698  const int new_height = content_grid_->get_height() + height_modification;
699 
700  DBG_GUI_L << LOG_HEADER << " current height " << content_grid_->get_height() << " wanted height " << new_height;
701 
702  if(new_height < 0) {
703  return false;
704  }
705 
706  if(static_cast<unsigned>(new_height) <= content_->get_height()) {
707  DBG_GUI_L << " height in container, resize allowed.";
708 
710  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
711  return true;
712  }
713 
716  (
719  )
720  ) {
721  DBG_GUI_L << " can't use vertical scrollbar, ask window.";
722 
723  window* window = get_window();
724  assert(window);
725 
727  return false;
728  }
729 
730  DBG_GUI_L << " use the vertical scrollbar, resize allowed.";
731 
733  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
734 
735  return true;
736 }
737 
739 {
740  /***** Setup vertical scrollbar *****/
741  vertical_scrollbar_grid_ = find_widget<grid>(this, "_vertical_scrollbar_grid", false, true);
742 
744  find_widget<scrollbar_base>(vertical_scrollbar_grid_, "_vertical_scrollbar", false, true);
745 
748 
749  /***** Setup horizontal scrollbar *****/
750  horizontal_scrollbar_grid_ = find_widget<grid>(this, "_horizontal_scrollbar_grid", false, true);
751 
753  find_widget<scrollbar_base>(horizontal_scrollbar_grid_, "_horizontal_scrollbar", false, true);
754 
757 
758  /***** Setup the scrollbar buttons *****/
759  for(const auto& item : scroll_lookup()) {
760  // Vertical.
761  clickable_item* button = find_widget<clickable_item>(vertical_scrollbar_grid_, item.first, false, false);
762 
763  if(button) {
765  std::bind(&scrollbar_container::scroll_vertical_scrollbar, this, item.second));
766  }
767 
768  // Horizontal.
769  button = find_widget<clickable_item>(horizontal_scrollbar_grid_, item.first, false, false);
770 
771  if(button) {
773  std::bind(&scrollbar_container::scroll_horizontal_scrollbar, this, item.second));
774  }
775  }
776 
777  /***** Setup the content *****/
778  auto content = build_single_widget_instance<spacer>();
779  content_ = content.get();
780 
781  // TODO: possibly move this unique_ptr casting functionality to a helper function.
782  content_grid_.reset(static_cast<grid*>(get_grid().swap_child("_content_grid", std::move(content), true).release()));
783  assert(content_grid_);
784 
785  content_grid_->set_parent(this);
786 
787  /***** Let our subclasses initialize themselves. *****/
789 }
790 
792 {
795  }
796 }
797 
799 {
802  }
803 }
804 
806 {
808 
809  // Inherited.
811 
812  content_grid_->draw_children();
813 }
814 
816 {
817  // Inherited.
819 
820  assert(content_grid_);
821  content_grid_->layout_children();
822 }
823 
825 {
826  content_grid_->place(origin, size);
827 }
828 
830 {
831  assert(content_);
833 
834  // Set the bottom right location first if it doesn't fit the top left
835  // will look good. First calculate the left and top position depending on
836  // the current position.
837 
838  const int left_position = horizontal_scrollbar_->get_item_position() + (rect.x - content_->get_x());
839  const int top_position = vertical_scrollbar_->get_item_position() + (rect.y - content_->get_y());
840 
841  // bottom.
842  const int wanted_bottom = rect.y + rect.h;
843  const int current_bottom = content_->get_y() + content_->get_height();
844 
845  int distance = wanted_bottom - current_bottom;
846  if(distance > 0) {
848  }
849 
850  // right.
851  const int wanted_right = rect.x + rect.w;
852  const int current_right = content_->get_x() + content_->get_width();
853 
854  distance = wanted_right - current_right;
855  if(distance > 0) {
857  }
858 
859  // top.
860  if(top_position < static_cast<int>(vertical_scrollbar_->get_item_position())) {
861  vertical_scrollbar_->set_item_position(top_position);
862  }
863 
864  // left.
865  if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
867  }
868 
869  // Update.
870  scrollbar_moved();
871 }
872 
874 {
875  if(true) { /** @todo scrollbar visibility. */
876  /***** set scroll up button status *****/
877  for(const auto& name : button_up_names) {
878  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
879 
880  if(button) {
882  }
883  }
884 
885  /***** set scroll down status *****/
886  for(const auto& name : button_down_names) {
887  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
888 
889  if(button) {
891  }
892  }
893 
894  /***** Set the status if the scrollbars *****/
896  }
897 
898  if(true) { /** @todo scrollbar visibility. */
899  /***** Set scroll left button status *****/
900  for(const auto& name : button_up_names) {
901  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
902 
903  if(button) {
905  }
906  }
907 
908  /***** Set scroll right button status *****/
909  for(const auto& name : button_down_names) {
910  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
911 
912  if(button) {
914  }
915  }
916 
917  /***** Set the status if the scrollbars *****/
919  }
920 }
921 
923 {
924  assert(vertical_scrollbar_);
925 
926  return vertical_scrollbar_->at_end();
927 }
928 
930 {
931  assert(vertical_scrollbar_);
932 
934 }
935 
937 {
938  assert(vertical_scrollbar_);
939 
941  scrollbar_moved();
942 }
943 
945 {
946  assert(horizontal_scrollbar_);
947 
949 }
950 
952 {
953  assert(horizontal_scrollbar_);
954 
956  scrollbar_moved();
957 }
958 
960 {
961  assert(vertical_scrollbar_);
962 
963  vertical_scrollbar_->scroll(scroll);
964  scrollbar_moved();
965 }
966 
968 {
969  assert(horizontal_scrollbar_);
970 
971  horizontal_scrollbar_->scroll(scroll);
972  scrollbar_moved();
973 }
974 
975 void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
976 {
978 
981  scrollbar_moved();
982 
983  handled = true;
984 }
985 
986 void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
987 {
988  assert(vertical_scrollbar_);
989 
991  scrollbar_moved();
992 
993  handled = true;
994 }
995 
996 void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
997 {
998  assert(vertical_scrollbar_);
999 
1001  scrollbar_moved();
1002 
1003  handled = true;
1004 }
1005 
1006 void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1007 
1008 {
1009  assert(vertical_scrollbar_);
1010 
1012  scrollbar_moved();
1013 
1014  handled = true;
1015 }
1016 
1017 void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1018 {
1019  assert(vertical_scrollbar_);
1020 
1022  scrollbar_moved();
1023 
1024  handled = true;
1025 }
1026 
1027 void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1028 {
1029  assert(vertical_scrollbar_);
1030 
1032  scrollbar_moved();
1033 
1034  handled = true;
1035 }
1036 
1037 void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1038 {
1039  assert(horizontal_scrollbar_);
1040 
1042  scrollbar_moved();
1043 
1044  handled = true;
1045 }
1046 
1047 void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1048 {
1049  assert(horizontal_scrollbar_);
1050 
1052  scrollbar_moved();
1053 
1054  handled = true;
1055 }
1056 
1058 {
1059  // Init.
1060  assert(content_ && content_grid_);
1062 
1063  /*** Update the content location. ***/
1064  const int x_offset = horizontal_scrollbar_mode_ == ALWAYS_INVISIBLE
1065  ? 0
1067 
1068  const int y_offset = vertical_scrollbar_mode_ == ALWAYS_INVISIBLE
1069  ? 0
1071 
1072  const point content_origin {content_->get_x() - x_offset, content_->get_y() - y_offset};
1073 
1074  content_grid_->set_origin(content_origin);
1075  content_grid_->set_visible_rectangle(content_visible_area_);
1077 
1078  // Update scrollbar.
1080 }
1081 
1082 const std::string& scrollbar_container::type()
1083 {
1084  static const std::string type = "scrollbar_container";
1085  return type;
1086 }
1087 
1088 const std::string& scrollbar_container::get_control_type() const
1089 {
1090  return type();
1091 }
1092 
1094  const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier)
1095 {
1096  DBG_GUI_E << LOG_HEADER << event << ".";
1097 
1098  switch(key) {
1099  case SDLK_HOME:
1100  handle_key_home(modifier, handled);
1101  break;
1102 
1103  case SDLK_END:
1104  handle_key_end(modifier, handled);
1105  break;
1106 
1107  case SDLK_PAGEUP:
1108  handle_key_page_up(modifier, handled);
1109  break;
1110 
1111  case SDLK_PAGEDOWN:
1112  handle_key_page_down(modifier, handled);
1113  break;
1114 
1115  case SDLK_UP:
1116  handle_key_up_arrow(modifier, handled);
1117  break;
1118 
1119  case SDLK_DOWN:
1120  handle_key_down_arrow(modifier, handled);
1121  break;
1122 
1123  case SDLK_LEFT:
1124  handle_key_left_arrow(modifier, handled);
1125  break;
1126 
1127  case SDLK_RIGHT:
1128  handle_key_right_arrow(modifier, handled);
1129  break;
1130  default:
1131  /* ignore */
1132  break;
1133  }
1134 }
1135 
1137 {
1138  DBG_GUI_E << LOG_HEADER << event << ".";
1139 
1141 
1144  scrollbar_moved();
1145  handled = true;
1146  }
1147 }
1148 
1150 {
1151  DBG_GUI_E << LOG_HEADER << event << ".";
1152 
1154 
1157  scrollbar_moved();
1158  handled = true;
1159  }
1160 }
1161 
1163 {
1164  DBG_GUI_E << LOG_HEADER << event << ".";
1165 
1167 
1170  scrollbar_moved();
1171  handled = true;
1172  }
1173 }
1174 
1176 {
1177  DBG_GUI_E << LOG_HEADER << event << ".";
1178 
1180 
1183  scrollbar_moved();
1184  handled = true;
1185  }
1186 }
1187 
1188 void
1190  bool& handled,
1191  const point& position,
1192  const point& distance)
1193 {
1194  (void) position;
1195  DBG_GUI_E << LOG_HEADER << event << ".";
1196 
1197  bool is_scrollbar_moved = false;
1198 
1200 
1202  horizontal_scrollbar_->scroll_by(-distance.x);
1203  is_scrollbar_moved = true;
1204  }
1205  }
1206 
1208 
1210  vertical_scrollbar_->scroll_by(-distance.y);
1211  is_scrollbar_moved = true;
1212  }
1213  }
1214 
1215  if (is_scrollbar_moved) {
1216  scrollbar_moved();
1217  handled = true;
1218  }
1219 }
1220 
1221 
1222 
1223 } // namespace gui2
Simple push button.
Definition: button.hpp:37
virtual void connect_click_handler(const event::signal &signal) override
Inherited from clickable_item.
Definition: button.hpp:53
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:65
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:76
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
A horizontal scrollbar is a widget that shows a horizontal scrollbar.
Base class for a scroll bar.
Definition: scrollbar.hpp:42
bool at_begin() const
Is the positioner at the beginning of the scrollbar?
Definition: scrollbar.hpp:79
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:145
bool at_end() const
Is the positioner at the and of the scrollbar?
Definition: scrollbar.hpp:89
bool all_items_visible() const
Are all items visible?
Definition: scrollbar.hpp:95
unsigned get_step_size() const
Definition: scrollbar.hpp:161
void set_item_count(const unsigned item_count)
Definition: scrollbar.hpp:130
unsigned get_item_count() const
Definition: scrollbar.hpp:135
void set_visible_items(const unsigned visible_items)
Definition: scrollbar.hpp:155
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: scrollbar.cpp:71
scroll_mode
scroll 'step size'.
Definition: scrollbar.hpp:55
@ ITEM_FORWARD
Go one item towards the end.
Definition: scrollbar.hpp:62
@ ITEM_BACKWARDS
Go one item towards the begin.
Definition: scrollbar.hpp:57
@ END
Go to the end position.
Definition: scrollbar.hpp:61
@ HALF_JUMP_FORWARD
Go half the visible items towards the end.
Definition: scrollbar.hpp:63
@ BEGIN
Go to begin position.
Definition: scrollbar.hpp:56
@ JUMP_BACKWARDS
Go the visible items towards the begin.
Definition: scrollbar.hpp:60
@ HALF_JUMP_BACKWARDS
Go half the visible items towards the begin.
Definition: scrollbar.hpp:58
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.
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 moved.
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.
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.
Base class for all visible items.
The definition of a vertical scrollbar.
Base class for all widgets.
Definition: widget.hpp:54
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:194
void set_visible(const visibility visible)
Definition: widget.cpp:456
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:442
visibility get_visible() const
Definition: widget.cpp:483
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:118
@ 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:530
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:67
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:682
bool get_need_layout() const
Definition: window.hpp:380
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:241
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:193
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:205
scrollbar_container::scrollbar_mode scrollbar_mode
Definition: helper.hpp:31
std::unique_ptr< class walker_base > walker_ptr
Definition: widget.hpp:43
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:414
const std::vector< std::string > items
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:87
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:92