The Battle for Wesnoth  1.15.2+dev
scrollbar_container.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
22 #include "gui/core/log.hpp"
24 #include "gui/widgets/spacer.hpp"
25 #include "gui/widgets/window.hpp"
26 #include "sdl/rect.hpp"
27 
28 #include <algorithm>
29 #include "utils/functional.hpp"
30 
31 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
32 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
33 
34 namespace gui2
35 {
36 namespace
37 {
38 
39 static const std::string button_up_names[]
40  { "_begin", "_line_up", "_half_page_up", "_page_up" };
41 
42 static const std::string button_down_names[]
43  { "_end", "_line_down", "_half_page_down", "_page_down" };
44 
45 /**
46  * Returns a map with the names of all buttons and the scrollbar jump they're
47  * supposed to execute.
48  */
49 const std::map<std::string, scrollbar_base::scroll_mode>& scroll_lookup()
50 {
51  static std::map<std::string, scrollbar_base::scroll_mode> lookup;
52  if(lookup.empty()) {
53  lookup["_begin"] = scrollbar_base::BEGIN;
54  lookup["_line_up"] = scrollbar_base::ITEM_BACKWARDS;
55  lookup["_half_page_up"] = scrollbar_base::HALF_JUMP_BACKWARDS;
56  lookup["_page_up"] = scrollbar_base::JUMP_BACKWARDS;
57 
58  lookup["_end"] = scrollbar_base::END;
59  lookup["_line_down"] = scrollbar_base::ITEM_FORWARD;
60  lookup["_half_page_down"] = scrollbar_base::HALF_JUMP_FORWARD;
61  lookup["_page_down"] = scrollbar_base::JUMP_FORWARD;
62  }
63 
64  return lookup;
65 }
66 
67 } // namespace
68 
70  const implementation::builder_styled_widget& builder, const std::string& control_type)
71  : container_base(builder, control_type)
72  , state_(ENABLED)
73  , vertical_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
74  , horizontal_scrollbar_mode_(AUTO_VISIBLE_FIRST_RUN)
75  , vertical_scrollbar_grid_(nullptr)
76  , horizontal_scrollbar_grid_(nullptr)
77  , vertical_scrollbar_(nullptr)
78  , horizontal_scrollbar_(nullptr)
79  , content_grid_(nullptr)
80  , content_(nullptr)
81  , content_visible_area_()
82 {
83  connect_signal<event::SDL_KEY_DOWN>(
84  std::bind(&scrollbar_container::signal_handler_sdl_key_down, this, _2, _3, _5, _6));
85 
86  connect_signal<event::SDL_WHEEL_UP>(
87  std::bind(&scrollbar_container::signal_handler_sdl_wheel_up, this, _2, _3),
89 
90  connect_signal<event::SDL_WHEEL_DOWN>(
93 
94  connect_signal<event::SDL_WHEEL_LEFT>(
97 
98  connect_signal<event::SDL_WHEEL_RIGHT>(
101 
102  connect_signal<event::SDL_TOUCH_MOTION>(
104  this,
105  _2,
106  _3,
107  _5,
108  _6),
110 }
111 
112 void scrollbar_container::layout_initialize(const bool full_initialization)
113 {
114  // Inherited.
115  container_base::layout_initialize(full_initialization);
116 
117  if(full_initialization) {
118  assert(vertical_scrollbar_grid_);
119 
120  switch(vertical_scrollbar_mode_) {
121  case ALWAYS_VISIBLE:
123  break;
124 
125  case AUTO_VISIBLE:
127  break;
128 
129  default:
131  }
132 
134 
136  case ALWAYS_VISIBLE:
138  break;
139 
140  case AUTO_VISIBLE:
142  break;
143 
144  default:
146  }
147  }
148 
149  assert(content_grid_);
150  content_grid_->layout_initialize(full_initialization);
151 }
152 
153 void scrollbar_container::request_reduce_height(const unsigned maximum_height)
154 {
155  DBG_GUI_L << LOG_HEADER << " requested height " << maximum_height << ".\n";
156 
157  /*
158  * First ask the content to reduce it's height. This seems to work for now,
159  * but maybe some sizing hints will be required later.
160  */
161  /** @todo Evaluate whether sizing hints are required. */
162  assert(content_grid_);
163  const unsigned offset =
166  : 0;
167 
168  content_grid_->request_reduce_height(maximum_height - offset);
169 
170  // Did we manage to achieve the wanted size?
172  if(static_cast<unsigned>(size.y) <= maximum_height) {
173  DBG_GUI_L << LOG_HEADER << " child honored request, height " << size.y << ".\n";
174  return;
175  }
176 
178  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.\n";
179  return;
180  }
181 
182  assert(vertical_scrollbar_grid_);
184 
185  // Always set the bar visible, is a nop is already visible.
187 
188  const point scrollbar_size = vertical_scrollbar_grid_->get_best_size();
189 
190  // If showing the scrollbar increased the height, hide and abort.
191  if(resized && scrollbar_size.y > size.y) {
193  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
194  << " increased the height to " << scrollbar_size.y << ".\n";
195  return;
196  }
197 
198  if(maximum_height > static_cast<unsigned>(scrollbar_size.y)) {
199  size.y = maximum_height;
200  } else {
201  size.y = scrollbar_size.y;
202  }
203 
204  // FIXME adjust for the step size of the scrollbar
205 
206  set_layout_size(size);
207  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.y << ".\n";
208 
209  if(resized) {
210  DBG_GUI_L << LOG_HEADER << " resize modified the width, throw notification.\n";
211 
213  }
214 }
215 
216 void scrollbar_container::request_reduce_width(const unsigned maximum_width)
217 {
218  DBG_GUI_L << LOG_HEADER << " requested width " << maximum_width << ".\n";
219 
220  if(static_cast<unsigned>(get_grid().get_best_size().x) > maximum_width) {
221  get_grid().request_reduce_width(maximum_width);
222  }
223 
224  // First ask our content, it might be able to wrap which looks better as
225  // a scrollbar.
226  assert(content_grid_);
227  const unsigned offset =
230  : 0;
231 
232  content_grid_->request_reduce_width(maximum_width - offset);
233 
234  // Did we manage to achieve the wanted size?
236  if(static_cast<unsigned>(size.x) <= maximum_width) {
237  DBG_GUI_L << LOG_HEADER << " child honored request, width " << size.x << ".\n";
238  return;
239  }
240 
242  DBG_GUI_L << LOG_HEADER << " request failed due to scrollbar mode.\n";
243  return;
244  }
245 
246  // Always set the bar visible, is a nop when it's already visible.
249  size = get_best_size();
250 
251  point scrollbar_size = horizontal_scrollbar_grid_->get_best_size();
252 
253  /*
254  * If the vertical bar is not invisible it's size needs to be added to the
255  * minimum size.
256  */
258  scrollbar_size.x += vertical_scrollbar_grid_->get_best_size().x;
259  }
260 
261  // If showing the scrollbar increased the width, hide and abort.
262  if(horizontal_scrollbar_mode_ == AUTO_VISIBLE_FIRST_RUN && scrollbar_size.x > size.x) {
264  DBG_GUI_L << LOG_HEADER << " request failed, showing the scrollbar"
265  << " increased the width to " << scrollbar_size.x << ".\n";
266  return;
267  }
268 
269  if(maximum_width > static_cast<unsigned>(scrollbar_size.x)) {
270  size.x = maximum_width;
271  } else {
272  size.x = scrollbar_size.x;
273  }
274 
275  size.x = std::max(size.x, get_grid().get_best_size().x);
276 
277  // FIXME adjust for the step size of the scrollbar
278 
279  set_layout_size(size);
280  DBG_GUI_L << LOG_HEADER << " resize resulted in " << size.x << ".\n";
281 }
282 
284 {
285  return content_grid_ ? content_grid_->can_wrap() : false;
286 }
287 
289 {
291 
292  /***** get vertical scrollbar size *****/
294  ? point()
296 
297  /***** get horizontal scrollbar size *****/
299  ? point()
301 
302  /***** get content size *****/
303  assert(content_grid_);
304  const point content = content_grid_->get_best_size();
305 
306  point result(
307  vertical_scrollbar.x + std::max(horizontal_scrollbar.x, content.x),
308  horizontal_scrollbar.y + std::max(vertical_scrollbar.y, content.y));
309 
310  DBG_GUI_L << LOG_HEADER << " vertical_scrollbar " << vertical_scrollbar << " horizontal_scrollbar "
311  << horizontal_scrollbar << " content " << content << " result " << result << ".\n";
312 
313  return result;
314 }
315 
316 static void set_scrollbar_mode(grid* scrollbar_grid,
317  scrollbar_base* scrollbar,
319  const unsigned items,
320  const unsigned visible_items,
322 {
323  assert(scrollbar_grid && scrollbar);
324 
325  if(scrollbar_mode == scrollbar_container::ALWAYS_INVISIBLE) {
327  return;
328  }
329 
330  scrollbar->set_item_count(items);
331  scrollbar->set_item_position(0);
332  scrollbar->set_visible_items(visible_items);
333 
334  if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE) {
335  const bool scrollbar_needed = items > visible_items;
336  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
337  } else if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE_FIRST_RUN) {
338  if(items <= visible_items && content_grid != nullptr
339  && scrollbar_grid->get_visible() == widget::visibility::visible
340  ) {
342  // Give newly freed space to the items.
343  content_grid->layout_initialize(false);
344  }
345  }
346 }
347 static bool is_inserted_before(
348  unsigned insertion_pos, unsigned old_item_count, unsigned old_position, unsigned visible_items)
349 {
350  if(old_position == 0) {
351  return false;
352  } else if(old_position + visible_items >= old_item_count) {
353  return true;
354  } else if(insertion_pos <= old_position) {
355  return true;
356  }
357 
358  return false;
359 }
360 
361 static void adjust_scrollbar_mode(grid* scrollbar_grid,
362  scrollbar_base* scrollbar,
364  const unsigned items_before,
365  const unsigned items_after,
366  const int insertion_pos,
367  const unsigned visible_items)
368 {
369  assert(scrollbar_grid && scrollbar);
370  if(items_before != scrollbar->get_item_count()) {
371  return set_scrollbar_mode(scrollbar_grid, scrollbar, scrollbar_mode, items_after, visible_items, nullptr);
372  }
373 
374  // TODO: does this also work well in case the items were removed?
375  const unsigned previous_item_position = scrollbar->get_item_position();
376 
377  // Casts insertion_pos to an unsigned so negative values are interpreted as 'at end'
378  const bool inserted_before_visible_area = is_inserted_before(
379  static_cast<unsigned>(insertion_pos), items_before, previous_item_position, visible_items);
380 
381  if(scrollbar_mode == scrollbar_container::ALWAYS_INVISIBLE) {
383  return;
384  }
385 
386  scrollbar->set_item_count(items_after);
387  scrollbar->set_item_position(inserted_before_visible_area
388  ? previous_item_position + items_after - items_before
389  : previous_item_position);
390 
391  // scrollbar->set_item_position(0);
392  scrollbar->set_visible_items(visible_items);
393 
394  if(scrollbar_mode == scrollbar_container::AUTO_VISIBLE) {
395  const bool scrollbar_needed = items_after > visible_items;
396  scrollbar_grid->set_visible(scrollbar_needed ? widget::visibility::visible : widget::visibility::hidden);
397  }
398 }
399 
400 void scrollbar_container::place(const point& origin, const point& size)
401 {
402  // Inherited.
403  container_base::place(origin, size);
404 
405  // Set content size
406  assert(content_ && content_grid_);
407 
408  const point content_origin = content_->get_origin();
409 
410  const point best_size = content_grid_->get_best_size();
411  const point content_size(content_->get_width(), content_->get_height());
412 
413  const point content_grid_size(std::max(best_size.x, content_size.x), std::max(best_size.y, content_size.y));
414 
415  set_content_size(content_origin, content_grid_size);
416 
417  // Set vertical scrollbar
422  content_grid_->get_height(),
423  content_->get_height(),
424  content_grid_.get()
425  );
426 
427  // Set horizontal scrollbar
432  content_grid_->get_width(),
433  content_->get_width(),
434  content_grid_.get()
435  );
436 
437  // Update the buttons.
439 
440  // Now set the visible part of the content.
442  content_grid_->set_visible_rectangle(content_visible_area_);
443 }
444 
446 {
447  // Inherited.
449 
450  // Set content size
451  assert(content_ && content_grid_);
452 
453  const point content_origin = content_->get_origin();
454 
455  content_grid_->set_origin(content_origin);
456 
457  // Changing the origin also invalidates the visible area.
458  content_grid_->set_visible_rectangle(content_visible_area_);
459 }
460 
461 void scrollbar_container::set_visible_rectangle(const SDL_Rect& rectangle)
462 {
463  // Inherited.
465 
466  // Now get the visible part of the content.
468 
469  content_grid_->set_visible_rectangle(content_visible_area_);
470 }
471 
473 {
474  return state_ != DISABLED;
475 }
476 
478 {
479  return state_;
480 }
481 
482 widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active)
483 {
484  widget* w = scrollbar_container_implementation::find_at<widget>(*this, coordinate, must_be_active);
485  if(w == nullptr) {
486  w = widget::find_at(coordinate, must_be_active);
487  }
488 
489  return w;
490 }
491 
492 const widget* scrollbar_container::find_at(const point& coordinate, const bool must_be_active) const
493 {
494  const widget* w = scrollbar_container_implementation::find_at<const widget>(*this, coordinate, must_be_active);
495  if(w == nullptr) {
496  w = widget::find_at(coordinate, must_be_active);
497  }
498 
499  return w;
500 }
501 
502 widget* scrollbar_container::find(const std::string& id, const bool must_be_active)
503 {
504  return scrollbar_container_implementation::find<widget>(*this, id, must_be_active);
505 }
506 
507 const widget* scrollbar_container::find(const std::string& id, const bool must_be_active) const
508 {
509  return scrollbar_container_implementation::find<const widget>(*this, id, must_be_active);
510 }
511 
513 {
514  assert(content_grid_);
515  return container_base::disable_click_dismiss() || content_grid_->disable_click_dismiss();
516 }
517 
518 bool scrollbar_container::content_resize_request(const bool force_sizing)
519 {
520  /**
521  * @todo Try to handle AUTO_VISIBLE_FIRST_RUN here as well.
522  *
523  * Handling it here makes the code a bit more complex but allows to not
524  * reserve space for scrollbars, which will look nicer in the MP lobby.
525  * But the extra complexity is no 1.8 material.
526  */
527 
528  assert(content_ && content_grid_);
529 
530  point best_size = content_grid_->recalculate_best_size();
532 
533  DBG_GUI_L << LOG_HEADER << " wanted size " << best_size << " available size " << size << ".\n";
534 
535  if(size == point()) {
536  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.\n";
537  return false;
538  }
539 
540  if(best_size.x <= size.x && best_size.y <= size.y) {
541  const point content_size = content_grid_->get_size();
542 
543  if(content_size.x > size.x || content_size.y > size.y) {
544  DBG_GUI_L << LOG_HEADER << " will fit, only needs a resize.\n";
545  goto resize;
546  }
547 
548  if(force_sizing) {
549  DBG_GUI_L << LOG_HEADER << " fits, but resize forced.\n";
550  goto resize;
551  }
552 
553  DBG_GUI_L << LOG_HEADER << " fits, nothing to do.\n";
554  return true;
555  }
556 
557  if(best_size.x > size.x) {
558  DBG_GUI_L << LOG_HEADER << " content too wide.\n";
559 
561  (
564  )
565  ) {
566  DBG_GUI_L << LOG_HEADER << " can't use horizontal scrollbar, request placement.\n";
567 
569  fire(event::REQUEST_PLACEMENT, *this, message);
570  return false;
571  }
572  }
573 
574  if(best_size.y > size.y) {
575  DBG_GUI_L << LOG_HEADER << " content too high.\n";
576 
578  (
581  )
582  ) {
583  DBG_GUI_L << LOG_HEADER << " can't use vertical scrollbar, request placement.\n";
584 
586  fire(event::REQUEST_PLACEMENT, *this, message);
587  return false;
588  }
589  }
590 
591 resize:
592  DBG_GUI_L << LOG_HEADER << " handle resizing.\n";
593 
594  place(get_origin(), get_size());
595  return true;
596 }
597 
598 bool scrollbar_container::content_resize_request(const int width_modification,
599  const int height_modification,
600  const int width_modification_pos,
601  const int height_modification_pos)
602 {
603  DBG_GUI_L << LOG_HEADER << " wanted width modification " << width_modification << " wanted height modification "
604  << height_modification << ".\n";
605 
606  if(get_size() == point()) {
607  DBG_GUI_L << LOG_HEADER << " initial setup not done, bailing out.\n";
608  return false;
609  }
610 
611  window* window = get_window();
612  assert(window);
613 
614  if(window->get_need_layout()) {
615  DBG_GUI_L << LOG_HEADER << " window already needs a layout phase, bailing out.\n";
616  return false;
617  }
618 
619  assert(content_ && content_grid_);
620 
621  const bool result =
622  content_resize_width(width_modification, width_modification_pos) &&
623  content_resize_height(height_modification, height_modification_pos);
624 
625  scrollbar_moved();
626 
627  /*
628  * The subroutines set the new size of the scrollbar but don't
629  * update the button status.
630  */
631  if(result) {
633  }
634 
635  DBG_GUI_L << LOG_HEADER << " result " << result << ".\n";
636  return result;
637 }
638 
639 bool scrollbar_container::content_resize_width(const int width_modification, const int width_modification_pos)
640 {
641  if(width_modification == 0) {
642  return true;
643  }
644 
645  const int new_width = content_grid_->get_width() + width_modification;
646  DBG_GUI_L << LOG_HEADER << " current width " << content_grid_->get_width() << " wanted width " << new_width;
647 
648  if(new_width < 0) {
649  return false;
650  }
651 
652  if(static_cast<unsigned>(new_width) <= content_->get_width()) {
653  DBG_GUI_L << " width fits in container, test height.\n";
654 
656  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
657  content_->get_width());
658  return true;
659  }
660 
663  (
666  )
667  ) {
668  DBG_GUI_L << " can't use horizontal scrollbar, ask window.\n";
669 
670  window* window = get_window();
671  assert(window);
672 
673  window->invalidate_layout();
674  return false;
675  }
676 
677  DBG_GUI_L << " use the horizontal scrollbar, test height.\n";
679  content_grid_->get_width(), content_grid_->get_width() + width_modification, width_modification_pos,
680  content_->get_width());
681 
682  return true;
683 }
684 
685 bool scrollbar_container::content_resize_height(const int height_modification, const int height_modification_pos)
686 {
687  if(height_modification == 0) {
688  return true;
689  }
690 
691  const int new_height = content_grid_->get_height() + height_modification;
692 
693  DBG_GUI_L << LOG_HEADER << " current height " << content_grid_->get_height() << " wanted height " << new_height;
694 
695  if(new_height < 0) {
696  return false;
697  }
698 
699  if(static_cast<unsigned>(new_height) <= content_->get_height()) {
700  DBG_GUI_L << " height in container, resize allowed.\n";
701 
703  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
704  return true;
705  }
706 
709  (
712  )
713  ) {
714  DBG_GUI_L << " can't use vertical scrollbar, ask window.\n";
715 
716  window* window = get_window();
717  assert(window);
718 
719  window->invalidate_layout();
720  return false;
721  }
722 
723  DBG_GUI_L << " use the vertical scrollbar, resize allowed.\n";
724 
726  content_grid_->get_height(), new_height, height_modification_pos, content_->get_height());
727 
728  return true;
729 }
730 
732 {
733  /***** Setup vertical scrollbar *****/
734  vertical_scrollbar_grid_ = find_widget<grid>(this, "_vertical_scrollbar_grid", false, true);
735 
737  find_widget<scrollbar_base>(vertical_scrollbar_grid_, "_vertical_scrollbar", false, true);
738 
739  connect_signal_notify_modified(*vertical_scrollbar_,
741 
742  /***** Setup horizontal scrollbar *****/
743  horizontal_scrollbar_grid_ = find_widget<grid>(this, "_horizontal_scrollbar_grid", false, true);
744 
746  find_widget<scrollbar_base>(horizontal_scrollbar_grid_, "_horizontal_scrollbar", false, true);
747 
748  connect_signal_notify_modified(*horizontal_scrollbar_,
750 
751  /***** Setup the scrollbar buttons *****/
752  for(const auto& item : scroll_lookup()) {
753  // Vertical.
754  clickable_item* button = find_widget<clickable_item>(vertical_scrollbar_grid_, item.first, false, false);
755 
756  if(button) {
757  button->connect_click_handler(
758  std::bind(&scrollbar_container::scroll_vertical_scrollbar, this, item.second));
759  }
760 
761  // Horizontal.
762  button = find_widget<clickable_item>(horizontal_scrollbar_grid_, item.first, false, false);
763 
764  if(button) {
765  button->connect_click_handler(
766  std::bind(&scrollbar_container::scroll_horizontal_scrollbar, this, item.second));
767  }
768  }
769 
770  /***** Setup the content *****/
771  content_ = build_single_widget_instance<spacer>();
772 
773  // TODO: possibly move this unique_ptr casting functionality to a helper function.
774  content_grid_.reset(dynamic_cast<grid*>(get_grid().swap_child("_content_grid", content_, true).release()));
775  assert(content_grid_);
776 
777  content_grid_->set_parent(this);
778 
779  /***** Let our subclasses initialize themselves. *****/
781 }
782 
784 {
785  if(vertical_scrollbar_mode_ != scrollbar_mode) {
787  }
788 }
789 
791 {
792  if(horizontal_scrollbar_mode_ != scrollbar_mode) {
794  }
795 }
796 
797 void scrollbar_container::impl_draw_children(surface& frame_buffer, int x_offset, int y_offset)
798 {
800 
801  // Inherited.
802  container_base::impl_draw_children(frame_buffer, x_offset, y_offset);
803 
804  content_grid_->draw_children(frame_buffer, x_offset, y_offset);
805 }
806 
808 {
809  // Inherited.
811 
812  assert(content_grid_);
813  content_grid_->layout_children();
814 }
815 
816 void scrollbar_container::child_populate_dirty_list(window& caller, const std::vector<widget*>& call_stack)
817 {
818  // Inherited.
819  container_base::child_populate_dirty_list(caller, call_stack);
820 
821  assert(content_grid_);
822  std::vector<widget*> child_call_stack(call_stack);
823  content_grid_->populate_dirty_list(caller, child_call_stack);
824 }
825 
827 {
828  content_grid_->place(origin, size);
829 }
830 
831 void scrollbar_container::show_content_rect(const SDL_Rect& rect)
832 {
833  assert(content_);
835 
836  // Set the bottom right location first if it doesn't fit the top left
837  // will look good. First calculate the left and top position depending on
838  // the current position.
839 
840  const int left_position = horizontal_scrollbar_->get_item_position() + (rect.x - content_->get_x());
841  const int top_position = vertical_scrollbar_->get_item_position() + (rect.y - content_->get_y());
842 
843  // bottom.
844  const int wanted_bottom = rect.y + rect.h;
845  const int current_bottom = content_->get_y() + content_->get_height();
846 
847  int distance = wanted_bottom - current_bottom;
848  if(distance > 0) {
850  }
851 
852  // right.
853  const int wanted_right = rect.x + rect.w;
854  const int current_right = content_->get_x() + content_->get_width();
855 
856  distance = wanted_right - current_right;
857  if(distance > 0) {
859  }
860 
861  // top.
862  if(top_position < static_cast<int>(vertical_scrollbar_->get_item_position())) {
863  vertical_scrollbar_->set_item_position(top_position);
864  }
865 
866  // left.
867  if(left_position < static_cast<int>(horizontal_scrollbar_->get_item_position())) {
869  }
870 
871  // Update.
872  scrollbar_moved();
873 }
874 
876 {
877  if(true) { /** @todo scrollbar visibility. */
878  /***** set scroll up button status *****/
879  for(const auto& name : button_up_names) {
880  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
881 
882  if(button) {
884  }
885  }
886 
887  /***** set scroll down status *****/
888  for(const auto& name : button_down_names) {
889  styled_widget* button = find_widget<styled_widget>(vertical_scrollbar_grid_, name, false, false);
890 
891  if(button) {
893  }
894  }
895 
896  /***** Set the status if the scrollbars *****/
898  }
899 
900  if(true) { /** @todo scrollbar visibility. */
901  /***** Set scroll left button status *****/
902  for(const auto& name : button_up_names) {
903  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
904 
905  if(button) {
907  }
908  }
909 
910  /***** Set scroll right button status *****/
911  for(const auto& name : button_down_names) {
912  styled_widget* button = find_widget<styled_widget>(horizontal_scrollbar_grid_, name, false, false);
913 
914  if(button) {
916  }
917  }
918 
919  /***** Set the status if the scrollbars *****/
921  }
922 }
923 
925 {
926  assert(vertical_scrollbar_);
927 
928  return vertical_scrollbar_->at_end();
929 }
930 
932 {
933  assert(vertical_scrollbar_);
934 
936 }
937 
939 {
940  assert(vertical_scrollbar_);
941 
943  scrollbar_moved();
944 }
945 
947 {
948  assert(horizontal_scrollbar_);
949 
951 }
952 
954 {
955  assert(horizontal_scrollbar_);
956 
958  scrollbar_moved();
959 }
960 
962 {
963  assert(vertical_scrollbar_);
964 
965  vertical_scrollbar_->scroll(scroll);
966  scrollbar_moved();
967 }
968 
970 {
971  assert(horizontal_scrollbar_);
972 
973  horizontal_scrollbar_->scroll(scroll);
974  scrollbar_moved();
975 }
976 
977 void scrollbar_container::handle_key_home(SDL_Keymod /*modifier*/, bool& handled)
978 {
980 
983  scrollbar_moved();
984 
985  handled = true;
986 }
987 
988 void scrollbar_container::handle_key_end(SDL_Keymod /*modifier*/, bool& handled)
989 {
990  assert(vertical_scrollbar_);
991 
993  scrollbar_moved();
994 
995  handled = true;
996 }
997 
998 void scrollbar_container::handle_key_page_up(SDL_Keymod /*modifier*/, bool& handled)
999 {
1000  assert(vertical_scrollbar_);
1001 
1003  scrollbar_moved();
1004 
1005  handled = true;
1006 }
1007 
1008 void scrollbar_container::handle_key_page_down(SDL_Keymod /*modifier*/, bool& handled)
1009 
1010 {
1011  assert(vertical_scrollbar_);
1012 
1014  scrollbar_moved();
1015 
1016  handled = true;
1017 }
1018 
1019 void scrollbar_container::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
1020 {
1021  assert(vertical_scrollbar_);
1022 
1024  scrollbar_moved();
1025 
1026  handled = true;
1027 }
1028 
1029 void scrollbar_container::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
1030 {
1031  assert(vertical_scrollbar_);
1032 
1034  scrollbar_moved();
1035 
1036  handled = true;
1037 }
1038 
1039 void scrollbar_container::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
1040 {
1041  assert(horizontal_scrollbar_);
1042 
1044  scrollbar_moved();
1045 
1046  handled = true;
1047 }
1048 
1049 void scrollbar_container::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
1050 {
1051  assert(horizontal_scrollbar_);
1052 
1054  scrollbar_moved();
1055 
1056  handled = true;
1057 }
1058 
1060 {
1061  // Init.
1062  assert(content_ && content_grid_);
1064 
1065  /*** Update the content location. ***/
1066  const int x_offset = horizontal_scrollbar_mode_ == ALWAYS_INVISIBLE
1067  ? 0
1069 
1070  const int y_offset = vertical_scrollbar_mode_ == ALWAYS_INVISIBLE
1071  ? 0
1073 
1074  const point content_origin {content_->get_x() - x_offset, content_->get_y() - y_offset};
1075 
1076  content_grid_->set_origin(content_origin);
1077  content_grid_->set_visible_rectangle(content_visible_area_);
1078  content_grid_->set_is_dirty(true);
1079 
1080  // Update scrollbar.
1082 }
1083 
1084 const std::string& scrollbar_container::type()
1085 {
1086  static const std::string type = "scrollbar_container";
1087  return type;
1088 }
1089 
1090 const std::string& scrollbar_container::get_control_type() const
1091 {
1092  return type();
1093 }
1094 
1096  const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier)
1097 {
1098  DBG_GUI_E << LOG_HEADER << event << ".\n";
1099 
1100  switch(key) {
1101  case SDLK_HOME:
1102  handle_key_home(modifier, handled);
1103  break;
1104 
1105  case SDLK_END:
1106  handle_key_end(modifier, handled);
1107  break;
1108 
1109  case SDLK_PAGEUP:
1110  handle_key_page_up(modifier, handled);
1111  break;
1112 
1113  case SDLK_PAGEDOWN:
1114  handle_key_page_down(modifier, handled);
1115  break;
1116 
1117  case SDLK_UP:
1118  handle_key_up_arrow(modifier, handled);
1119  break;
1120 
1121  case SDLK_DOWN:
1122  handle_key_down_arrow(modifier, handled);
1123  break;
1124 
1125  case SDLK_LEFT:
1126  handle_key_left_arrow(modifier, handled);
1127  break;
1128 
1129  case SDLK_RIGHT:
1130  handle_key_right_arrow(modifier, handled);
1131  break;
1132  default:
1133  /* ignore */
1134  break;
1135  }
1136 }
1137 
1139 {
1140  DBG_GUI_E << LOG_HEADER << event << ".\n";
1141 
1143 
1146  scrollbar_moved();
1147  handled = true;
1148  }
1149 }
1150 
1152 {
1153  DBG_GUI_E << LOG_HEADER << event << ".\n";
1154 
1156 
1159  scrollbar_moved();
1160  handled = true;
1161  }
1162 }
1163 
1165 {
1166  DBG_GUI_E << LOG_HEADER << event << ".\n";
1167 
1169 
1172  scrollbar_moved();
1173  handled = true;
1174  }
1175 }
1176 
1178 {
1179  DBG_GUI_E << LOG_HEADER << event << ".\n";
1180 
1182 
1185  scrollbar_moved();
1186  handled = true;
1187  }
1188 }
1189 
1190 void
1192  bool& handled,
1193  const point& position,
1194  const point& distance)
1195 {
1196  (void) position;
1197  DBG_GUI_E << LOG_HEADER << event << ".\n";
1198 
1199  bool is_scrollbar_moved = false;
1200 
1202 
1204  horizontal_scrollbar_->scroll_by(-distance.x);
1205  is_scrollbar_moved = true;
1206  }
1207  }
1208 
1210 
1212  vertical_scrollbar_->scroll_by(-distance.y);
1213  is_scrollbar_moved = true;
1214  }
1215  }
1216 
1217  if (is_scrollbar_moved) {
1218  scrollbar_moved();
1219  handled = true;
1220  }
1221 }
1222 
1223 
1224 
1225 } // namespace gui2
Define the common log macros for the gui toolkit.
Base class for a scroll bar.
Definition: scrollbar.hpp:40
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:56
int get_x() const
Definition: widget.cpp:312
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: scrollbar.cpp:134
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:154
void set_horizontal_scrollbar_mode(const scrollbar_mode scrollbar_mode)
#define DBG_GUI_L
Definition: log.hpp:57
A horizontal scrollbar.
void set_layout_size(const point &size)
Definition: widget.cpp:332
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:55
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:500
Main class to show messages to the user.
Definition: message.hpp:34
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:107
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:307
Base class for all widgets.
Definition: widget.hpp:47
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:151
bool all_items_visible() const
Are all items visible?
Definition: scrollbar.hpp:94
#define LOG_SCOPE_HEADER
void scroll_by(const int pixels)
Definition: scrollbar.cpp:65
unsigned get_height() const
Definition: widget.cpp:327
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:56
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:39
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:231
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:88
bool at_begin() const
Is the positioner at the beginning of the scrollbar?
Definition: scrollbar.hpp:78
Go half the visible items towards the end.
Definition: scrollbar.hpp:62
unsigned get_width() const
Definition: widget.cpp:322
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:44
Generic file dialog.
Definition: field-fwd.hpp:22
Go to the end position.
Definition: scrollbar.hpp:60
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:185
void signal_handler_sdl_wheel_right(const event::ui_event event, bool &handled)
Base container class.
Definition: grid.hpp:30
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:86
Request to place the widget.
Definition: handler.hpp:102
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:248
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:190
void set_visible(const visibility visible)
Definition: widget.cpp:473
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:70
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:144
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:61
virtual bool can_wrap() const override
See widget::can_wrap.
#define log_scope2(domain, description)
Definition: log.hpp:187
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:317
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:34
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:59
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:114
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:134
Holds a 2D point.
Definition: point.hpp:23
int w
scroll_mode
scroll &#39;step size&#39;.
Definition: scrollbar.hpp:54
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:302
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:160
bool get_need_layout() const
Definition: window.hpp:386
Simple push button.
Definition: button.hpp:35
Go half the visible items towards the begin.
Definition: scrollbar.hpp:57
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:130
Small concept class.
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:806
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:570
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:129
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:47
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:63
static std::deque< std::string > call_stack
Definition: function.cpp:39
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:297
ui_event
The event send to the dispatcher.
Definition: handler.hpp:55
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:371
void set_horizontal_scrollbar_item_position(const unsigned position)
Move the horizontal scrollbar to a position.