The Battle for Wesnoth  1.17.0-dev
scrollbar_container.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
23 #include "gui/core/log.hpp"
25 #include "gui/widgets/spacer.hpp"
26 #include "gui/widgets/window.hpp"
27 #include "sdl/rect.hpp"
28 
29 #include <algorithm>
30 #include <functional>
31 
32 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
33 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
34 
35 namespace gui2
36 {
37 namespace
38 {
39 
40 static const std::string button_up_names[]
41  { "_begin", "_line_up", "_half_page_up", "_page_up" };
42 
43 static const std::string button_down_names[]
44  { "_end", "_line_down", "_half_page_down", "_page_down" };
45 
46 /**
47  * Returns a map with the names of all buttons and the scrollbar jump they're
48  * supposed to execute.
49  */
50 const std::map<std::string, scrollbar_base::scroll_mode>& scroll_lookup()
51 {
52  static std::map<std::string, scrollbar_base::scroll_mode> lookup;
53  if(lookup.empty()) {
54  lookup["_begin"] = scrollbar_base::BEGIN;
55  lookup["_line_up"] = scrollbar_base::ITEM_BACKWARDS;
56  lookup["_half_page_up"] = scrollbar_base::HALF_JUMP_BACKWARDS;
57  lookup["_page_up"] = scrollbar_base::JUMP_BACKWARDS;
58 
59  lookup["_end"] = scrollbar_base::END;
60  lookup["_line_down"] = scrollbar_base::ITEM_FORWARD;
61  lookup["_half_page_down"] = scrollbar_base::HALF_JUMP_FORWARD;
62  lookup["_page_down"] = scrollbar_base::JUMP_FORWARD;
63  }
64 
65  return lookup;
66 }
67 
68 } // namespace
69 
71  const implementation::builder_styled_widget& builder, const std::string& control_type)
72  : container_base(builder, control_type)
73  , state_(ENABLED)
74  , vertical_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
75  , horizontal_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
76  , vertical_scrollbar_grid_(nullptr)
77  , horizontal_scrollbar_grid_(nullptr)
78  , vertical_scrollbar_(nullptr)
79  , horizontal_scrollbar_(nullptr)
80  , content_grid_(nullptr)
81  , content_(nullptr)
82  , content_visible_area_()
83 {
84  connect_signal<event::SDL_KEY_DOWN>(
85  std::bind(&scrollbar_container::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6));
86 
87  connect_signal<event::SDL_WHEEL_UP>(
88  std::bind(&scrollbar_container::signal_handler_sdl_wheel_up, this, std::placeholders::_2, std::placeholders::_3),
90 
91  connect_signal<event::SDL_WHEEL_DOWN>(
92  std::bind(&scrollbar_container::signal_handler_sdl_wheel_down, this, std::placeholders::_2, std::placeholders::_3),
94 
95  connect_signal<event::SDL_WHEEL_LEFT>(
96  std::bind(&scrollbar_container::signal_handler_sdl_wheel_left, this, std::placeholders::_2, std::placeholders::_3),
98 
99  connect_signal<event::SDL_WHEEL_RIGHT>(
100  std::bind(&scrollbar_container::signal_handler_sdl_wheel_right, this, std::placeholders::_2, std::placeholders::_3),
102 
103  connect_signal<event::SDL_TOUCH_MOTION>(
105  this,
106  std::placeholders::_2,
107  std::placeholders::_3,
108  std::placeholders::_5,
109  std::placeholders::_6),
111 }
112 
113 void scrollbar_container::layout_initialize(const bool full_initialization)
114 {
115  // Inherited.
116  container_base::layout_initialize(full_initialization);
117 
118  if(full_initialization) {
119  assert(vertical_scrollbar_grid_);
120 
121  switch(vertical_scrollbar_mode_) {
122  case ALWAYS_VISIBLE:
124  break;
125 
126  case AUTO_VISIBLE:
128  break;
129 
130  default:
132  }
133 
135 
137  case ALWAYS_VISIBLE:
139  break;
140 
141  case AUTO_VISIBLE:
143  break;
144 
145  default:
147  }
148  }
149 
150  assert(content_grid_);
151  content_grid_->layout_initialize(full_initialization);
152 }
153 
154 void scrollbar_container::request_reduce_height(const unsigned maximum_height)
155 {
156  DBG_GUI_L << LOG_HEADER << " requested height " << maximum_height << ".\n";
157 
158  /*
159  * First ask the content to reduce it's height. This seems to work for now,
160  * but maybe some sizing hints will be required later.
161  */
162  /** @todo Evaluate whether sizing hints are required. */
163  assert(content_grid_);
164  const unsigned offset =
167  : 0;
168 
169  content_grid_->request_reduce_height(maximum_height - offset);
170 
171  // Did we manage to achieve the wanted size?
173  if(static_cast<unsigned>(size.y) <= maximum_height) {
174  DBG_GUI_L << LOG_HEADER << " child honored request, height " << size.y << ".\n";
175  return;
176  }
177 
179  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.\n";
180  return;
181  }
182 
183  assert(vertical_scrollbar_grid_);
185 
186  // Always set the bar visible, is a nop is already visible.
188 
189  const point scrollbar_size = vertical_scrollbar_grid_->get_best_size();
190 
191  // If showing the scrollbar increased the height, hide and abort.
192  if(resized && scrollbar_size.y > size.y) {
194  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
195  << " increased the height to " << scrollbar_size.y << ".\n";
196  return;
197  }
198 
199  if(maximum_height > static_cast<unsigned>(scrollbar_size.y)) {
200  size.y = maximum_height;
201  } else {
202  size.y = scrollbar_size.y;
203  }
204 
205  // FIXME adjust for the step size of the scrollbar
206 
207  set_layout_size(size);
208  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.y << ".\n";
209 
210  if(resized) {
211  DBG_GUI_L << LOG_HEADER << " resize modified the width, throw notification.\n";
212 
214  }
215 }
216 
217 void scrollbar_container::request_reduce_width(const unsigned maximum_width)
218 {
219  DBG_GUI_L << LOG_HEADER << " requested width " << maximum_width << ".\n";
220 
221  if(static_cast<unsigned>(get_grid().get_best_size().x) > maximum_width) {
222  get_grid().request_reduce_width(maximum_width);
223  }
224 
225  // First ask our content, it might be able to wrap which looks better as
226  // a scrollbar.
227  assert(content_grid_);
228  const unsigned offset =
231  : 0;
232 
233  content_grid_->request_reduce_width(maximum_width - offset);
234 
235  // Did we manage to achieve the wanted size?
237  if(static_cast<unsigned>(size.x) <= maximum_width) {
238  DBG_GUI_L << LOG_HEADER << " child honored request, width " << size.x << ".\n";
239  return;
240  }
241 
243  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.\n";
244  return;
245  }
246 
247  // Always set the bar visible, is a nop when it's already visible.
250  size = get_best_size();
251 
252  point scrollbar_size = horizontal_scrollbar_grid_->get_best_size();
253 
254  /*
255  * If the vertical bar is not invisible it's size needs to be added to the
256  * minimum size.
257  */
259  scrollbar_size.x += vertical_scrollbar_grid_->get_best_size().x;
260  }
261 
262  // If showing the scrollbar increased the width, hide and abort.
263  if(horizontal_scrollbar_mode_ == AUTO_VISIBLE_FIRST_RUN && scrollbar_size.x > size.x) {
265  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
266  << " increased the width to " << scrollbar_size.x << ".\n";
267  return;
268  }
269 
270  if(maximum_width > static_cast<unsigned>(scrollbar_size.x)) {
271  size.x = maximum_width;
272  } else {
273  size.x = scrollbar_size.x;
274  }
275 
276  size.x = std::max(size.x, get_grid().get_best_size().x);
277 
278  // FIXME adjust for the step size of the scrollbar
279 
280  set_layout_size(size);
281  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.x << ".\n";
282 }
283 
285 {
286  return content_grid_ ? content_grid_->can_wrap() : false;
287 }
288 
290 {
292 
293  /***** get vertical scrollbar size *****/
295  ? point()
297 
298  /***** get horizontal scrollbar size *****/
300  ? point()
302 
303  /***** get content size *****/
304  assert(content_grid_);
305  const point content = content_grid_->get_best_size();
306 
307  point result(
308  vertical_scrollbar.x + std::max(horizontal_scrollbar.x, content.x),
309  horizontal_scrollbar.y + std::max(vertical_scrollbar.y, content.y));
310 
311  DBG_GUI_L << LOG_HEADER << " vertical_scrollbar " << vertical_scrollbar << " horizontal_scrollbar "
312  << horizontal_scrollbar << " content " << content << " result " << result << ".\n";
313 
314  return result;
315 }
316 
317 static void set_scrollbar_mode(grid* scrollbar_grid,
318  scrollbar_base* scrollbar,
320  const unsigned items,
321  const unsigned visible_items,
323 {
324  assert(scrollbar_grid && scrollbar);
325 
326  if(scrollbar_mode == scrollbar_container::ALWAYS_INVISIBLE) {
328  return;
329  }
330 
331  scrollbar->set_item_count(items);
332  scrollbar->set_item_position(0);
333  scrollbar->set_visible_items(visible_items);
334 
335  if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE) {
336  const bool scrollbar_needed = items > visible_items;
337  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
338  } else if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE_FIRST_RUN) {
339  if(items <= visible_items && content_grid != nullptr
340  && scrollbar_grid->get_visible() == widget::visibility::visible
341  ) {
343  // Give newly freed space to the items.
344  content_grid->layout_initialize(false);
345  }
346  }
347 }
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 
382  if(scrollbar_mode == scrollbar_container::ALWAYS_INVISIBLE) {
384  return;
385  }
386 
387  scrollbar->set_item_count(items_after);
388  scrollbar->set_item_position(inserted_before_visible_area
389  ? previous_item_position + items_after - items_before
390  : previous_item_position);
391 
392  // scrollbar->set_item_position(0);
393  scrollbar->set_visible_items(visible_items);
394 
395  if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE) {
396  const bool scrollbar_needed = items_after > visible_items;
397  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
398  }
399 }
400 
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& 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& 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 
519 bool scrollbar_container::content_resize_request(const bool force_sizing)
520 {
521  /**
522  * @todo Try to handle AUTO_VISIBLE_FIRST_RUN here as well.
523  *
524  * Handling it here makes the code a bit more complex but allows to not
525  * reserve space for scrollbars, which will look nicer in the MP lobby.
526  * But the extra complexity is no 1.8 material.
527  */
528 
529  assert(content_ && content_grid_);
530 
531  point best_size = content_grid_->recalculate_best_size();
533 
534  DBG_GUI_L << LOG_HEADER << " wanted size " << best_size << " available size " << size << ".\n";
535 
536  if(size == point()) {
537  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.\n";
538  return false;
539  }
540 
541  if(best_size.x <= size.x && best_size.y <= size.y) {
542  const point content_size = content_grid_->get_size();
543 
544  if(content_size.x > size.x || content_size.y > size.y) {
545  DBG_GUI_L << LOG_HEADER << " will fit, only needs a resize.\n";
546  goto resize;
547  }
548 
549  if(force_sizing) {
550  DBG_GUI_L << LOG_HEADER << " fits, but resize forced.\n";
551  goto resize;
552  }
553 
554  DBG_GUI_L << LOG_HEADER << " fits, nothing to do.\n";
555  return true;
556  }
557 
558  if(best_size.x > size.x) {
559  DBG_GUI_L << LOG_HEADER << " content too wide.\n";
560 
562  (
565  )
566  ) {
567  DBG_GUI_L << LOG_HEADER << " can't use horizontal scrollbar, request placement.\n";
568 
570  fire(event::REQUEST_PLACEMENT, *this, message);
571  return false;
572  }
573  }
574 
575  if(best_size.y > size.y) {
576  DBG_GUI_L << LOG_HEADER << " content too high.\n";
577 
579  (
582  )
583  ) {
584  DBG_GUI_L << LOG_HEADER << " can't use vertical scrollbar, request placement.\n";
585 
587  fire(event::REQUEST_PLACEMENT, *this, message);
588  return false;
589  }
590  }
591 
592 resize:
593  DBG_GUI_L << LOG_HEADER << " handle resizing.\n";
594 
595  place(get_origin(), get_size());
596  return true;
597 }
598 
599 bool scrollbar_container::content_resize_request(const int width_modification,
600  const int height_modification,
601  const int width_modification_pos,
602  const int height_modification_pos)
603 {
604  DBG_GUI_L << LOG_HEADER << " wanted width modification " << width_modification << " wanted height modification "
605  << height_modification << ".\n";
606 
607  if(get_size() == point()) {
608  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.\n";
609  return false;
610  }
611 
612  window* window = get_window();
613  assert(window);
614 
615  if(window->get_need_layout()) {
616  DBG_GUI_L << LOG_HEADER << " window already needs a layout phase, bailing out.\n";
617  return false;
618  }
619 
620  assert(content_ && content_grid_);
621 
622  const bool result =
623  content_resize_width(width_modification, width_modification_pos) &&
624  content_resize_height(height_modification, height_modification_pos);
625 
626  scrollbar_moved();
627 
628  /*
629  * The subroutines set the new size of the scrollbar but don't
630  * update the button status.
631  */
632  if(result) {
634  }
635 
636  DBG_GUI_L << LOG_HEADER << " result " << result << ".\n";
637  return result;
638 }
639 
640 bool scrollbar_container::content_resize_width(const int width_modification, const int width_modification_pos)
641 {
642  if(width_modification == 0) {
643  return true;
644  }
645 
646  const int new_width = content_grid_->get_width() + width_modification;
647  DBG_GUI_L << LOG_HEADER << " current width " << content_grid_->get_width() << " wanted width " << new_width;
648 
649  if(new_width < 0) {
650  return false;
651  }
652 
653  if(static_cast<unsigned>(new_width) <= content_->get_width()) {
654  DBG_GUI_L << " width fits in container, test height.\n";
655 
657  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
658  content_->get_width());
659  return true;
660  }
661 
664  (
667  )
668  ) {
669  DBG_GUI_L << " can't use horizontal scrollbar, ask window.\n";
670 
671  window* window = get_window();
672  assert(window);
673 
674  window->invalidate_layout();
675  return false;
676  }
677 
678  DBG_GUI_L << " use the horizontal scrollbar, test height.\n";
680  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
681  content_->get_width());
682 
683  return true;
684 }
685 
686 bool scrollbar_container::content_resize_height(const int height_modification, const int height_modification_pos)
687 {
688  if(height_modification == 0) {
689  return true;
690  }
691 
692  const int new_height = content_grid_->get_height() + height_modification;
693 
694  DBG_GUI_L << LOG_HEADER << " current height " << content_grid_->get_height() << " wanted height " << new_height;
695 
696  if(new_height < 0) {
697  return false;
698  }
699 
700  if(static_cast<unsigned>(new_height) <= content_->get_height()) {
701  DBG_GUI_L << " height in container, resize allowed.\n";
702 
704  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
705  return true;
706  }
707 
710  (
713  )
714  ) {
715  DBG_GUI_L << " can't use vertical scrollbar, ask window.\n";
716 
717  window* window = get_window();
718  assert(window);
719 
720  window->invalidate_layout();
721  return false;
722  }
723 
724  DBG_GUI_L << " use the vertical scrollbar, resize allowed.\n";
725 
727  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
728 
729  return true;
730 }
731 
733 {
734  /***** Setup vertical scrollbar *****/
735  vertical_scrollbar_grid_ = find_widget<grid>(this, "_vertical_scrollbar_grid", false, true);
736 
738  find_widget<scrollbar_base>(vertical_scrollbar_grid_, "_vertical_scrollbar", false, true);
739 
740  connect_signal_notify_modified(*vertical_scrollbar_,
742 
743  /***** Setup horizontal scrollbar *****/
744  horizontal_scrollbar_grid_ = find_widget<grid>(this, "_horizontal_scrollbar_grid", false, true);
745 
747  find_widget<scrollbar_base>(horizontal_scrollbar_grid_, "_horizontal_scrollbar", false, true);
748 
749  connect_signal_notify_modified(*horizontal_scrollbar_,
751 
752  /***** Setup the scrollbar buttons *****/
753  for(const auto& item : scroll_lookup()) {
754  // Vertical.
755  clickable_item* button = find_widget<clickable_item>(vertical_scrollbar_grid_, item.first, false, false);
756 
757  if(button) {
758  button->connect_click_handler(
759  std::bind(&scrollbar_container::scroll_vertical_scrollbar, this, item.second));
760  }
761 
762  // Horizontal.
763  button = find_widget<clickable_item>(horizontal_scrollbar_grid_, item.first, false, false);
764 
765  if(button) {
766  button->connect_click_handler(
767  std::bind(&scrollbar_container::scroll_horizontal_scrollbar, this, item.second));
768  }
769  }
770 
771  /***** Setup the content *****/
772  content_ = build_single_widget_instance<spacer>();
773 
774  // TODO: possibly move this unique_ptr casting functionality to a helper function.
775  content_grid_.reset(dynamic_cast<grid*>(get_grid().swap_child("_content_grid", content_, true).release()));
776  assert(content_grid_);
777 
778  content_grid_->set_parent(this);
779 
780  /***** Let our subclasses initialize themselves. *****/
782 }
783 
785 {
786  if(vertical_scrollbar_mode_ != scrollbar_mode) {
788  }
789 }
790 
792 {
793  if(horizontal_scrollbar_mode_ != scrollbar_mode) {
795  }
796 }
797 
798 void scrollbar_container::impl_draw_children(surface& frame_buffer, int x_offset, int y_offset)
799 {
801 
802  // Inherited.
803  container_base::impl_draw_children(frame_buffer, x_offset, y_offset);
804 
805  content_grid_->draw_children(frame_buffer, x_offset, y_offset);
806 }
807 
809 {
810  // Inherited.
812 
813  assert(content_grid_);
814  content_grid_->layout_children();
815 }
816 
817 void scrollbar_container::child_populate_dirty_list(window& caller, const std::vector<widget*>& call_stack)
818 {
819  // Inherited.
820  container_base::child_populate_dirty_list(caller, call_stack);
821 
822  assert(content_grid_);
823  std::vector<widget*> child_call_stack(call_stack);
824  content_grid_->populate_dirty_list(caller, child_call_stack);
825 }
826 
828 {
829  content_grid_->place(origin, size);
830 }
831 
832 void scrollbar_container::show_content_rect(const SDL_Rect& rect)
833 {
834  assert(content_);
836 
837  // Set the bottom right location first if it doesn't fit the top left
838  // will look good. First calculate the left and top position depending on
839  // the current position.
840 
841  const int left_position = horizontal_scrollbar_->get_item_position() + (rect.x - content_->get_x());
842  const int top_position = vertical_scrollbar_->get_item_position() + (rect.y - content_->get_y());
843 
844  // bottom.
845  const int wanted_bottom = rect.y + rect.h;
846  const int current_bottom = content_->get_y() + content_->get_height();
847 
848  int distance = wanted_bottom - current_bottom;
849  if(distance > 0) {
851  }
852 
853  // right.
854  const int wanted_right = rect.x + rect.w;
855  const int current_right = content_->get_x() + content_->get_width();
856 
857  distance = wanted_right - current_right;
858  if(distance > 0) {
860  }
861 
862  // top.
863  if(top_position < static_cast<int>(vertical_scrollbar_->get_item_position())) {
864  vertical_scrollbar_->set_item_position(top_position);
865  }
866 
867  // left.
868  if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
870  }
871 
872  // Update.
873  scrollbar_moved();
874 }
875 
877 {
878  if(true) { /** @todo scrollbar visibility. */
879  /***** set scroll up button status *****/
880  for(const auto& name : button_up_names) {
881  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
882 
883  if(button) {
885  }
886  }
887 
888  /***** set scroll down status *****/
889  for(const auto& name : button_down_names) {
890  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
891 
892  if(button) {
894  }
895  }
896 
897  /***** Set the status if the scrollbars *****/
899  }
900 
901  if(true) { /** @todo scrollbar visibility. */
902  /***** Set scroll left button status *****/
903  for(const auto& name : button_up_names) {
904  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
905 
906  if(button) {
908  }
909  }
910 
911  /***** Set scroll right button status *****/
912  for(const auto& name : button_down_names) {
913  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
914 
915  if(button) {
917  }
918  }
919 
920  /***** Set the status if the scrollbars *****/
922  }
923 }
924 
926 {
927  assert(vertical_scrollbar_);
928 
929  return vertical_scrollbar_->at_end();
930 }
931 
933 {
934  assert(vertical_scrollbar_);
935 
937 }
938 
940 {
941  assert(vertical_scrollbar_);
942 
944  scrollbar_moved();
945 }
946 
948 {
949  assert(horizontal_scrollbar_);
950 
952 }
953 
955 {
956  assert(horizontal_scrollbar_);
957 
959  scrollbar_moved();
960 }
961 
963 {
964  assert(vertical_scrollbar_);
965 
966  vertical_scrollbar_->scroll(scroll);
967  scrollbar_moved();
968 }
969 
971 {
972  assert(horizontal_scrollbar_);
973 
974  horizontal_scrollbar_->scroll(scroll);
975  scrollbar_moved();
976 }
977 
978 void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
979 {
981 
984  scrollbar_moved();
985 
986  handled = true;
987 }
988 
989 void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
990 {
991  assert(vertical_scrollbar_);
992 
994  scrollbar_moved();
995 
996  handled = true;
997 }
998 
999 void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
1000 {
1001  assert(vertical_scrollbar_);
1002 
1004  scrollbar_moved();
1005 
1006  handled = true;
1007 }
1008 
1009 void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1010 
1011 {
1012  assert(vertical_scrollbar_);
1013 
1015  scrollbar_moved();
1016 
1017  handled = true;
1018 }
1019 
1020 void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1021 {
1022  assert(vertical_scrollbar_);
1023 
1025  scrollbar_moved();
1026 
1027  handled = true;
1028 }
1029 
1030 void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1031 {
1032  assert(vertical_scrollbar_);
1033 
1035  scrollbar_moved();
1036 
1037  handled = true;
1038 }
1039 
1040 void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1041 {
1042  assert(horizontal_scrollbar_);
1043 
1045  scrollbar_moved();
1046 
1047  handled = true;
1048 }
1049 
1050 void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1051 {
1052  assert(horizontal_scrollbar_);
1053 
1055  scrollbar_moved();
1056 
1057  handled = true;
1058 }
1059 
1061 {
1062  // Init.
1063  assert(content_ && content_grid_);
1065 
1066  /*** Update the content location. ***/
1067  const int x_offset = horizontal_scrollbar_mode_ == ALWAYS_INVISIBLE
1068  ? 0
1070 
1071  const int y_offset = vertical_scrollbar_mode_ == ALWAYS_INVISIBLE
1072  ? 0
1074 
1075  const point content_origin {content_->get_x() - x_offset, content_->get_y() - y_offset};
1076 
1077  content_grid_->set_origin(content_origin);
1078  content_grid_->set_visible_rectangle(content_visible_area_);
1079  content_grid_->set_is_dirty(true);
1080 
1081  // Update scrollbar.
1083 }
1084 
1085 const std::string& scrollbar_container::type()
1086 {
1087  static const std::string type = "scrollbar_container";
1088  return type;
1089 }
1090 
1091 const std::string& scrollbar_container::get_control_type() const
1092 {
1093  return type();
1094 }
1095 
1097  const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier)
1098 {
1099  DBG_GUI_E << LOG_HEADER << event << ".\n";
1100 
1101  switch(key) {
1102  case SDLK_HOME:
1103  handle_key_home(modifier, handled);
1104  break;
1105 
1106  case SDLK_END:
1107  handle_key_end(modifier, handled);
1108  break;
1109 
1110  case SDLK_PAGEUP:
1111  handle_key_page_up(modifier, handled);
1112  break;
1113 
1114  case SDLK_PAGEDOWN:
1115  handle_key_page_down(modifier, handled);
1116  break;
1117 
1118  case SDLK_UP:
1119  handle_key_up_arrow(modifier, handled);
1120  break;
1121 
1122  case SDLK_DOWN:
1123  handle_key_down_arrow(modifier, handled);
1124  break;
1125 
1126  case SDLK_LEFT:
1127  handle_key_left_arrow(modifier, handled);
1128  break;
1129 
1130  case SDLK_RIGHT:
1131  handle_key_right_arrow(modifier, handled);
1132  break;
1133  default:
1134  /* ignore */
1135  break;
1136  }
1137 }
1138 
1140 {
1141  DBG_GUI_E << LOG_HEADER << event << ".\n";
1142 
1144 
1147  scrollbar_moved();
1148  handled = true;
1149  }
1150 }
1151 
1153 {
1154  DBG_GUI_E << LOG_HEADER << event << ".\n";
1155 
1157 
1160  scrollbar_moved();
1161  handled = true;
1162  }
1163 }
1164 
1166 {
1167  DBG_GUI_E << LOG_HEADER << event << ".\n";
1168 
1170 
1173  scrollbar_moved();
1174  handled = true;
1175  }
1176 }
1177 
1179 {
1180  DBG_GUI_E << LOG_HEADER << event << ".\n";
1181 
1183 
1186  scrollbar_moved();
1187  handled = true;
1188  }
1189 }
1190 
1191 void
1193  bool& handled,
1194  const point& position,
1195  const point& distance)
1196 {
1197  (void) position;
1198  DBG_GUI_E << LOG_HEADER << event << ".\n";
1199 
1200  bool is_scrollbar_moved = false;
1201 
1203 
1205  horizontal_scrollbar_->scroll_by(-distance.x);
1206  is_scrollbar_moved = true;
1207  }
1208  }
1209 
1211 
1213  vertical_scrollbar_->scroll_by(-distance.y);
1214  is_scrollbar_moved = true;
1215  }
1216  }
1217 
1218  if (is_scrollbar_moved) {
1219  scrollbar_moved();
1220  handled = true;
1221  }
1222 }
1223 
1224 
1225 
1226 } // namespace gui2
Define the common log macros for the gui toolkit.
Base class for a scroll bar.
Definition: scrollbar.hpp:41
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
virtual unsigned get_state() const override
See styled_widget::get_state.
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)
Defines the exception classes for the layout algorithm.
Go one item towards the begin.
Definition: scrollbar.hpp:57
int get_x() const
Definition: widget.cpp:315
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: scrollbar.cpp:135
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
Like AUTO_VISIBLE, but when not needed upon the initial layout phase, the bars are not shown and no s...
void set_visible_items(const unsigned visible_items)
Definition: scrollbar.hpp:155
void set_horizontal_scrollbar_mode(const scrollbar_mode scrollbar_mode)
#define DBG_GUI_L
Definition: log.hpp:55
A horizontal scrollbar is a widget that shows a horizontal scrollbar.
void set_layout_size(const point &size)
Definition: widget.cpp:335
void finalize_setup()
The builder needs to call us so we do our setup.
virtual void handle_key_end(SDL_Keymod modifier, bool &handled)
End key pressed.
Go to begin position.
Definition: scrollbar.hpp:56
virtual void place(const point &origin, const point &size) override
See widget::place.
virtual void handle_key_page_up(SDL_Keymod modifier, bool &handled)
Page up key pressed.
const grid & get_grid() const
void signal_handler_sdl_wheel_up(const event::ui_event event, bool &handled)
visibility get_visible() const
Definition: widget.cpp:503
Main class to show messages to the user.
Definition: message.hpp:35
virtual void place(const point &origin, const point &size) override
See widget::place.
scrollbar_mode vertical_scrollbar_mode_
The mode of how to show the scrollbar.
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)
Down arrow key pressed.
const std::string & id() const
Definition: widget.cpp:110
This file contains the window object, this object is a top level container which has the event manage...
void set_vertical_scrollbar_mode(const scrollbar_mode scrollbar_mode)
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:310
Base class for all widgets.
Definition: widget.hpp:49
void set_item_position(const unsigned item_position)
Note the position isn&#39;t guaranteed to be the wanted position the step size is honored.
Definition: scrollbar.cpp:152
bool all_items_visible() const
Are all items visible?
Definition: scrollbar.hpp:95
#define LOG_SCOPE_HEADER
void scroll_by(const int pixels)
Definition: scrollbar.cpp:66
unsigned get_height() const
Definition: widget.cpp:330
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:54
void signal_handler_sdl_wheel_left(const event::ui_event event, bool &handled)
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
virtual void impl_draw_children(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_children.
SDL_Rect intersect_rects(const SDL_Rect &rect1, const SDL_Rect &rect2)
Calculates the intersection of two rectangles.
Definition: rect.cpp:40
std::unique_ptr< grid > content_grid_
The grid that holds the content.
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: grid.cpp:232
bool content_resize_height(const int height_modification, const int width_modification_pos)
Helper for content_resize_request.
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)
spacer * content_
Dummy spacer to hold the contents location.
const std::vector< std::string > items
state_t state_
Current state of the widget.
static bool is_inserted_before(unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
bool at_end() const
Is the positioner at the and of the scrollbar?
Definition: scrollbar.hpp:89
bool at_begin() const
Is the positioner at the beginning of the scrollbar?
Definition: scrollbar.hpp:79
Go half the visible items towards the end.
Definition: scrollbar.hpp:63
unsigned get_width() const
Definition: widget.cpp:325
virtual void finalize_subclass()
Function for the subclasses to do their setup.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
virtual void set_origin(const point &origin) override
See widget::set_origin.
int x
x coordinate.
Definition: point.hpp:45
Generic file dialog.
Definition: field-fwd.hpp:23
Go to the end position.
Definition: scrollbar.hpp:61
The message callbacks hold a reference to a message.
Definition: message.hpp:45
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
Definition: grid.cpp:186
void signal_handler_sdl_wheel_right(const event::ui_event event, bool &handled)
Base container class.
Definition: grid.hpp:31
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
Request to place the widget.
Definition: handler.hpp:95
The scrollbar is shown when the number of items is larger as the visible items.
Exception thrown when the width has been modified during resizing.
#define LOG_HEADER
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:187
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:476
virtual void connect_click_handler(const event::signal_function &signal)=0
Connects a signal handler for a &#39;click&#39; event.
void scroll_horizontal_scrollbar(const scrollbar_base::scroll_mode scroll)
Scrolls the horizontal scrollbar.
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: scrollbar.cpp:71
scrollbar_container(const implementation::builder_styled_widget &builder, const std::string &control_type)
void signal_handler_sdl_touch_motion(const event::ui_event event, bool &handled, const point &position, const point &distance)
void set_scrollbar_button_status()
Sets the status of the scrollbar buttons.
unsigned get_item_position() const
Definition: scrollbar.hpp:145
static thread_local std::deque< std::string > call_stack
For printing error messages when WFL parsing or evaluation fails, this contains the names of the WFL ...
Definition: function.cpp:47
This file contains the definitions for the gui2::event::message class.
virtual void impl_draw_children(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_children.
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Go one item towards the end.
Definition: scrollbar.hpp:62
virtual bool can_wrap() const override
See widget::can_wrap.
#define log_scope2(domain, description)
Definition: log.hpp:219
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
int get_y() const
Definition: widget.cpp:320
virtual void layout_children() override
See widget::layout_children.
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)
Right arrow key pressed.
virtual const std::string & get_control_type() const override
See styled_widget::get_control_type.
void vertical_scrollbar_moved()
Callback when the scrollbar moves (NOTE maybe only one callback needed).
#define DBG_GUI_E
Definition: log.hpp:35
void set_vertical_scrollbar_item_position(const unsigned position)
Move the vertical scrollbar to a position.
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
Go the visible items towards the begin.
Definition: scrollbar.hpp:60
The user set the widget invisible, that means:
scrollbar_base * vertical_scrollbar_
These are valid after finalize_setup().
window * get_window()
Get the parent window.
Definition: widget.cpp:117
The scrollbar is never shown even notwhen needed.
void scroll_vertical_scrollbar(const scrollbar_base::scroll_mode scroll)
Scrolls the vertical scrollbar.
grid * vertical_scrollbar_grid_
These are valid after finalize_setup().
unsigned get_item_count() const
Definition: scrollbar.hpp:135
Holds a 2D point.
Definition: point.hpp:24
int w
scroll_mode
scroll &#39;step size&#39;.
Definition: scrollbar.hpp:55
Base class for all visible items.
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
A generic container base class.
virtual void handle_key_home(SDL_Keymod modifier, bool &handled)
Home key pressed.
Helper for header for the scrollbar_container.
void scrollbar_moved()
Helper function which needs to be called after the scollbar moved.
The scrollbar is always shown, whether needed or not.
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:305
scrollbar_mode
The way to handle the showing or hiding of the scrollbar.
void signal_handler_sdl_wheel_down(const event::ui_event event, bool &handled)
scrollbar_base * horizontal_scrollbar_
Contains the SDL_Rect helper code.
The user sets the widget visible, that means:
bool content_resize_width(const int width_modification, const int width_modification_pos)
Helper for content_resize_request.
virtual void set_origin(const point &origin) override
See widget::set_origin.
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key, SDL_Keymod modifier)
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
virtual void set_content_size(const point &origin, const point &size)
Sets the size of the content grid.
The user sets the widget hidden, that means:
virtual void set_active(const bool active)=0
Sets the styled_widget&#39;s state.
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
SDL_Rect content_visible_area_
Cache for the visible area for the content.
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
unsigned get_step_size() const
Definition: scrollbar.hpp:161
bool get_need_layout() const
Definition: window.hpp:380
Simple push button.
Definition: button.hpp:36
Go half the visible items towards the begin.
Definition: scrollbar.hpp:58
unsigned get_vertical_scrollbar_item_position() const
Returns current position of the vertical scrollbar.
virtual void request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:69
Small concept class.
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:799
virtual void layout_children() override
See widget::layout_children.
virtual widget * find_at(const point &coordinate, const bool must_be_active)
Returns the widget at the wanted coordinates.
Definition: widget.cpp:573
The definition of a vertical scrollbar.
virtual bool get_active() const override
See styled_widget::get_active.
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
void set_item_count(const unsigned item_count)
Definition: scrollbar.hpp:130
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
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.
unsigned get_horizontal_scrollbar_item_position() const
Returns current position of the horizontal scrollbar.
int y
y coordinate.
Definition: point.hpp:48
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:300
ui_event
The event send to the dispatcher.
Definition: handler.hpp:48
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
void set_horizontal_scrollbar_item_position(const unsigned position)
Move the horizontal scrollbar to a position.