The Battle for Wesnoth  1.15.12+dev
listbox.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 
17 #include "gui/widgets/listbox.hpp"
18 
19 #include "gettext.hpp"
22 #include "gui/core/log.hpp"
27 #include "gui/widgets/pane.hpp"
29 #include "gui/widgets/settings.hpp"
31 #include "gui/widgets/viewport.hpp"
33 #include "gui/widgets/window.hpp"
34 #include "sdl/rect.hpp"
35 #include <functional>
36 #include <optional>
37 
38 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
39 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
40 
42 
43 namespace gui2
44 {
45 // ------------ WIDGET -----------{
46 
47 REGISTER_WIDGET(listbox)
48 REGISTER_WIDGET3(listbox_definition, horizontal_listbox, nullptr)
49 REGISTER_WIDGET3(listbox_definition, grid_listbox, nullptr)
50 
51 listbox::listbox(const implementation::builder_styled_widget& builder,
52  const generator_base::placement placement,
53  builder_grid_ptr list_builder,
54  const bool has_minimum,
55  const bool has_maximum,
56  const bool select)
57  : scrollbar_container(builder, type())
58  , generator_(generator_base::build(has_minimum, has_maximum, placement, select))
59  , is_horizontal_(placement == generator_base::horizontal_list)
60  , list_builder_(list_builder)
61  , need_layout_(false)
62  , orders_()
63  , callback_order_change_()
64 {
65 }
66 
68 {
69  assert(generator_);
70  grid& row = generator_->create_item(index, *list_builder_, item, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
71 
72  resize_content(row);
73 
74  return row;
75 }
76 
77 grid& listbox::add_row(const std::map<std::string /* widget id */, string_map>& data, const int index)
78 {
79  assert(generator_);
80  grid& row = generator_->create_item(index, *list_builder_, data, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
81 
82  resize_content(row);
83 
84  return row;
85 }
86 
87 void listbox::remove_row(const unsigned row, unsigned count)
88 {
89  assert(generator_);
90 
91  if(row >= get_item_count()) {
92  return;
93  }
94 
95  if(!count || count + row > get_item_count()) {
96  count = get_item_count() - row;
97  }
98 
99  int height_reduced = 0;
100  int width_reduced = 0;
101 
102  // TODO: Fix this for horizontal listboxes
103  // Note the we have to use content_grid_ and cannot use "_list_grid" which is what generator_ uses.
104  int row_pos_y = is_horizontal_ ? -1 : generator_->item(row).get_y() - content_grid_->get_y();
105  int row_pos_x = is_horizontal_ ? -1 : 0;
106 
107  for(; count; --count) {
109  if(is_horizontal_) {
110  width_reduced += generator_->item(row).get_width();
111  } else {
112  height_reduced += generator_->item(row).get_height();
113  }
114  }
115 
116  generator_->delete_item(row);
117  }
118 
119  if((height_reduced != 0 || width_reduced != 0) && get_item_count() != 0) {
120  resize_content(-width_reduced, -height_reduced, row_pos_x, row_pos_y);
121  } else {
123  }
124 }
125 
127 {
128  generator_->clear();
130 }
131 
132 unsigned listbox::get_item_count() const
133 {
134  assert(generator_);
135  return generator_->get_item_count();
136 }
137 
138 void listbox::set_row_active(const unsigned row, const bool active)
139 {
140  assert(generator_);
141  generator_->item(row).set_active(active);
142 }
143 
144 void listbox::set_row_shown(const unsigned row, const bool shown)
145 {
146  assert(generator_);
147 
148  window* window = get_window();
149  assert(window);
150 
151  const int selected_row = get_selected_row();
152 
153  bool resize_needed = false;
154 
155  // Local scope for invalidate_layout_blocker
156  {
157  window::invalidate_layout_blocker invalidate_layout_blocker(*window);
158 
159  generator_->set_item_shown(row, shown);
160 
161  point best_size = generator_->calculate_best_size();
162  generator_->place(generator_->get_origin(), {std::max(best_size.x, content_visible_area().w), best_size.y});
163 
164  resize_needed = !content_resize_request();
165  }
166 
167  if(resize_needed) {
168  window->invalidate_layout();
169  } else {
170  content_grid_->set_visible_rectangle(content_visible_area());
171  set_is_dirty(true);
172  }
173 
174  if(selected_row != get_selected_row()) {
175  fire(event::NOTIFY_MODIFIED, *this, nullptr);
176  }
177 }
178 
179 void listbox::set_row_shown(const boost::dynamic_bitset<>& shown)
180 {
181  assert(generator_);
182  assert(shown.size() == get_item_count());
183 
184  if(generator_->get_items_shown() == shown) {
185  LOG_GUI_G << LOG_HEADER << " returning early" << std::endl;
186  return;
187  }
188 
189  window* window = get_window();
190  assert(window);
191 
192  const int selected_row = get_selected_row();
193 
194  bool resize_needed = false;
195 
196  // Local scope for invalidate_layout_blocker
197  {
198  window::invalidate_layout_blocker invalidate_layout_blocker(*window);
199 
200  for(std::size_t i = 0; i < shown.size(); ++i) {
201  generator_->set_item_shown(i, shown[i]);
202  }
203 
204  point best_size = generator_->calculate_best_size();
205  generator_->place(generator_->get_origin(), {std::max(best_size.x, content_visible_area().w), best_size.y});
206 
207  resize_needed = !content_resize_request();
208  }
209 
210  if(resize_needed) {
211  window->invalidate_layout();
212  } else {
213  content_grid_->set_visible_rectangle(content_visible_area());
214  set_is_dirty(true);
215  }
216 
217  if(selected_row != get_selected_row()) {
218  fire(event::NOTIFY_MODIFIED, *this, nullptr);
219  }
220 }
221 
222 boost::dynamic_bitset<> listbox::get_rows_shown() const
223 {
224  return generator_->get_items_shown();
225 }
226 
228 {
229  for(std::size_t i = 0; i < get_item_count(); i++) {
230  if(generator_->get_item_shown(i)) {
231  return true;
232  }
233  }
234 
235  return false;
236 }
237 
238 const grid* listbox::get_row_grid(const unsigned row) const
239 {
240  assert(generator_);
241  // rename this function and can we return a reference??
242  return &generator_->item(row);
243 }
244 
245 grid* listbox::get_row_grid(const unsigned row)
246 {
247  assert(generator_);
248  return &generator_->item(row);
249 }
250 
251 bool listbox::select_row(const unsigned row, const bool select)
252 {
253  if(row >= get_item_count()) {
254  throw std::invalid_argument("invalid listbox index");
255  }
256  assert(generator_);
257 
258  unsigned int before = generator_->get_selected_item_count();
259  generator_->select_item(row, select);
260 
261  return before != generator_->get_selected_item_count();
262 }
263 
264 bool listbox::select_row_at(const unsigned row, const bool select)
265 {
266  assert(generator_);
267  return select_row(generator_->get_item_at_ordered(row), select);
268 }
269 
270 bool listbox::row_selected(const unsigned row)
271 {
272  assert(generator_);
273  return generator_->is_selected(row);
274 }
275 
277 {
278  assert(generator_);
279  return generator_->get_selected_item();
280 }
281 
283 {
284  assert(generator_);
285 
286  /** @todo Hack to capture the keyboard focus. */
287  get_window()->keyboard_capture(this);
288 
289  for(std::size_t i = 0; i < generator_->get_item_count(); ++i) {
290  if(generator_->item(i).has_widget(caller)) {
291  toggle_button* checkbox = dynamic_cast<toggle_button*>(&caller);
292 
293  if(checkbox != nullptr) {
294  generator_->select_item(i, checkbox->get_value_bool());
295  } else {
297  }
298 
299  // TODO: enable this code once toggle_panel::set_value dispatches
300  // NOTIFY_MODIFED events. See comment in said function for more details.
301 #if 0
302  selectable_item& selectable = dynamic_cast<selectable_item&>(caller);
303 
304  generator_->select_item(i, selectable.get_value_bool());
305 #endif
306 
307  fire(event::NOTIFY_MODIFIED, *this, nullptr);
308  break;
309  }
310  }
311 
312  const int selected_item = generator_->get_selected_item();
313  if(selected_item == -1) {
314  return;
315  }
316 
317  const SDL_Rect& visible = content_visible_area();
318  SDL_Rect rect = generator_->item(selected_item).get_rectangle();
319 
320  if(sdl::rects_overlap(visible, rect)) {
321  rect.x = visible.x;
322  rect.w = visible.w;
323 
324  show_content_rect(rect);
325  }
326 }
327 
328 void listbox::set_self_active(const bool /*active*/)
329 {
330  /* DO NOTHING */
331 }
332 
334 {
336  return true;
337  }
338 
339  if(get_size() == point()) {
340  return false;
341  }
342 
343  if(content_resize_request(true)) {
344  content_grid_->set_visible_rectangle(content_visible_area());
345  set_is_dirty(true);
346  return true;
347  }
348 
349  return false;
350 }
351 
352 void listbox::place(const point& origin, const point& size)
353 {
354  std::optional<unsigned> vertical_scrollbar_position, horizontal_scrollbar_position;
355 
356  // Check if this is the first time placing the list box
357  if(get_origin() != point {-1, -1}) {
358  vertical_scrollbar_position = get_vertical_scrollbar_item_position();
359  horizontal_scrollbar_position = get_horizontal_scrollbar_item_position();
360  }
361 
362  // Inherited.
363  scrollbar_container::place(origin, size);
364 
365  const int selected_item = generator_->get_selected_item();
366  if(vertical_scrollbar_position && horizontal_scrollbar_position) {
367  LOG_GUI_L << LOG_HEADER << " restoring scroll position" << std::endl;
368 
369  set_vertical_scrollbar_item_position(*vertical_scrollbar_position);
370  set_horizontal_scrollbar_item_position(*horizontal_scrollbar_position);
371  } else if(selected_item != -1) {
372  LOG_GUI_L << LOG_HEADER << " making the initially selected item visible" << std::endl;
373 
374  const SDL_Rect& visible = content_visible_area();
375  SDL_Rect rect = generator_->item(selected_item).get_rectangle();
376 
377  rect.x = visible.x;
378  rect.w = visible.w;
379 
380  show_content_rect(rect);
381  }
382 }
383 
384 void listbox::resize_content(const int width_modification,
385  const int height_modification,
386  const int width_modification_pos,
387  const int height_modification_pos)
388 {
389  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
390  << width_modification << " height_modification " << height_modification << ".\n";
391 
393  width_modification, height_modification, width_modification_pos, height_modification_pos))
394  {
395  // Calculate new size.
397  size.x += width_modification;
398  size.y += height_modification;
399 
400  // Set new size.
401  content_grid()->set_size(size);
402 
403  // Set status.
404  need_layout_ = true;
405 
406  // If the content grows assume it "overwrites" the old content.
407  if(width_modification < 0 || height_modification < 0) {
408  set_is_dirty(true);
409  }
410 
411  DBG_GUI_L << LOG_HEADER << " succeeded.\n";
412  } else {
413  DBG_GUI_L << LOG_HEADER << " failed.\n";
414  }
415 }
416 
418 {
419  if(row.get_visible() == visibility::invisible) {
420  return;
421  }
422 
423  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " row size " << row.get_best_size()
424  << ".\n";
425 
426  const point content = content_grid()->get_size();
427  point size = row.get_best_size();
428 
429  if(size.x < content.x) {
430  size.x = 0;
431  } else {
432  size.x -= content.x;
433  }
434 
435  resize_content(size.x, size.y);
436 }
437 
439 {
440  layout_children(false);
441 }
442 
443 void listbox::child_populate_dirty_list(window& caller, const std::vector<widget*>& call_stack)
444 {
445  // Inherited.
447 
448  assert(generator_);
449  std::vector<widget*> child_call_stack = call_stack;
450  generator_->populate_dirty_list(caller, child_call_stack);
451 }
452 
454 {
455  // Get the size from the base class, then add any extra space for the header and footer.
457 
458  if(const grid* header = find_widget<const grid>(&get_grid(), "_header_grid", false, false)) {
459  result.y += header->get_best_size().y;
460  }
461 
462  if(const grid* footer = find_widget<const grid>(&get_grid(), "_footer_grid", false, false)) {
463  result.y += footer->get_best_size().y;
464  }
465 
466  return result;
467 }
468 
470 {
471  const SDL_Rect& visible = content_visible_area();
473 
474  // When scrolling make sure the new items are visible...
475  if(direction == KEY_VERTICAL) {
476  // ...but leave the horizontal scrollbar position.
477  rect.x = visible.x;
478  rect.w = visible.w;
479  } else {
480  // ...but leave the vertical scrollbar position.
481  rect.y = visible.y;
482  rect.h = visible.h;
483  }
484 
485  show_content_rect(rect);
486 
487  fire(event::NOTIFY_MODIFIED, *this, nullptr);
488 }
489 
490 void listbox::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
491 {
492  assert(generator_);
493 
494  generator_->handle_key_up_arrow(modifier, handled);
495 
496  if(handled) {
498  } else {
499  // Inherited.
500  scrollbar_container::handle_key_up_arrow(modifier, handled);
501  }
502 }
503 
504 void listbox::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
505 {
506  assert(generator_);
507 
508  generator_->handle_key_down_arrow(modifier, handled);
509 
510  if(handled) {
512  } else {
513  // Inherited.
514  scrollbar_container::handle_key_up_arrow(modifier, handled);
515  }
516 }
517 
518 void listbox::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
519 {
520  assert(generator_);
521 
522  generator_->handle_key_left_arrow(modifier, handled);
523 
524  // Inherited.
525  if(handled) {
527  } else {
529  }
530 }
531 
532 void listbox::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
533 {
534  assert(generator_);
535 
536  generator_->handle_key_right_arrow(modifier, handled);
537 
538  // Inherited.
539  if(handled) {
541  } else {
543  }
544 }
545 
547  builder_grid_const_ptr footer,
548  const std::vector<std::map<std::string, string_map>>& list_data)
549 {
550  // "Inherited."
552 
553  assert(generator_);
554 
555  if(header) {
556  swap_grid(&get_grid(), content_grid(), header->build(), "_header_grid");
557  }
558 
559  grid& p = find_widget<grid>(this, "_header_grid", false);
560 
561  for(unsigned i = 0, max = std::max(p.get_cols(), p.get_rows()); i < max; ++i) {
562  //
563  // TODO: I had to change this to case to a toggle_button in order to use a signal handler.
564  // Should probably look into a way to make it more general like it was before (used to be
565  // cast to selectable_item).
566  //
567  // - vultraz, 2017-08-23
568  //
569  if(toggle_button* selectable = find_widget<toggle_button>(&p, "sort_" + std::to_string(i), false, false)) {
570  // Register callback to sort the list.
571  connect_signal_notify_modified(*selectable, std::bind(&listbox::order_by_column, this, i, std::placeholders::_1));
572 
573  if(orders_.size() < max) {
574  orders_.resize(max);
575  }
576 
577  orders_[i].first = selectable;
578  }
579  }
580 
581  if(footer) {
582  swap_grid(&get_grid(), content_grid(), footer->build(), "_footer_grid");
583  }
584 
585  generator_->create_items(-1, *list_builder_, list_data, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
586  swap_grid(nullptr, content_grid(), generator_, "_list_grid");
587 }
588 
589 void listbox::order_by_column(unsigned column, widget& widget)
590 {
591  selectable_item& selectable = dynamic_cast<selectable_item&>(widget);
592  if(column >= orders_.size()) {
593  return;
594  }
595 
596  for(auto& pair : orders_) {
597  if(pair.first != nullptr && pair.first != &selectable) {
599  }
600  }
601 
602  SORT_ORDER order {static_cast<SORT_ORDER::type>(selectable.get_value())};
603 
604  if(static_cast<unsigned int>(order.v) > orders_[column].second.size()) {
605  return;
606  }
607 
608  if(order == SORT_ORDER::NONE) {
609  order_by(std::less<unsigned>());
610  } else {
611  order_by(orders_[column].second[order.v - 1]);
612  }
613 
614  if(callback_order_change_ != nullptr) {
615  callback_order_change_(column, order);
616  }
617 }
618 
620 {
621  generator_->set_order(func);
622 
623  set_is_dirty(true);
624  need_layout_ = true;
625 }
626 
627 void listbox::set_column_order(unsigned col, const generator_sort_array& func)
628 {
629  if(col >= orders_.size()) {
630  orders_.resize(col + 1);
631  }
632 
633  orders_[col].second = func;
634 }
635 
637 {
638  set_column_order(col, {{
639  [f](int lhs, int rhs) { return translation::icompare(f(lhs), f(rhs)) < 0; },
640  [f](int lhs, int rhs) { return translation::icompare(f(lhs), f(rhs)) > 0; }
641  }});
642 }
643 
644 void listbox::set_active_sorting_option(const order_pair& sort_by, const bool select_first)
645 {
646  // TODO: should this be moved to a public header_grid() getter function?
647  grid& header_grid = find_widget<grid>(this, "_header_grid", false);
648 
649  selectable_item& w = find_widget<selectable_item>(&header_grid, "sort_" + std::to_string(sort_by.first), false);
650 
651  // Set the sorting toggle widgets' value (in this case, its state) to the given sorting
652  // order. This is necessary since the widget's value is used to determine the order in
653  // @ref order_by_column in lieu of a direction being passed directly.
654  w.set_value(static_cast<int>(sort_by.second.v));
655 
656  order_by_column(sort_by.first, dynamic_cast<widget&>(w));
657 
658  if(select_first && generator_->get_item_count() > 0) {
659  select_row_at(0);
660  }
661 }
662 
664 {
665  for(unsigned int column = 0; column < orders_.size(); ++column) {
666  selectable_item* w = orders_[column].first;
667 
668  if(w && w->get_value() != SORT_ORDER::NONE) {
669  return std::pair(column, static_cast<SORT_ORDER::type>(w->get_value()));
670  }
671  }
672 
673  return std::pair(-1, SORT_ORDER::NONE);
674 }
675 
677 {
678  for(auto& pair : orders_) {
679  if(pair.first != nullptr) {
680  pair.first->set_value(SORT_ORDER::NONE);
681  }
682  }
683 }
684 
685 void listbox::set_content_size(const point& origin, const point& size)
686 {
687  /** @todo This function needs more testing. */
688  assert(content_grid());
689 
690  const int best_height = content_grid()->get_best_size().y;
691  const point s(size.x, size.y < best_height ? size.y : best_height);
692 
693  content_grid()->place(origin, s);
694 }
695 
696 void listbox::layout_children(const bool force)
697 {
698  assert(content_grid());
699 
700  if(need_layout_ || force) {
702 
703  const SDL_Rect& visible = content_visible_area_;
704 
706 
707  need_layout_ = false;
708  set_is_dirty(true);
709  }
710 }
711 
712 // }---------- DEFINITION ---------{
713 
716 {
717  DBG_GUI_P << "Parsing listbox " << id << '\n';
718 
719  load_resolutions<resolution>(cfg);
720 }
721 
723  : resolution_definition(cfg)
724  , grid(nullptr)
725 {
726  // Note the order should be the same as the enum state_t in listbox.hpp.
727  state.emplace_back(cfg.child("state_enabled"));
728  state.emplace_back(cfg.child("state_disabled"));
729 
730  const config& child = cfg.child("grid");
731  VALIDATE(child, _("No grid defined."));
732 
733  grid = std::make_shared<builder_grid>(child);
734 }
735 
736 namespace implementation
737 {
738 static std::vector<std::map<std::string, string_map>> parse_list_data(const config& data, const unsigned int req_cols)
739 {
740  std::vector<std::map<std::string, string_map>> list_data;
741 
742  for(const auto& row : data.child_range("row")) {
743  auto cols = row.child_range("column");
744 
745  VALIDATE(static_cast<unsigned>(cols.size()) == req_cols,
746  _("'list_data' must have the same number of columns as the 'list_definition'.")
747  );
748 
749  for(const auto& c : cols) {
750  list_data.emplace_back();
751 
752  for(const auto& i : c.attribute_range()) {
753  list_data.back()[""][i.first] = i.second;
754  }
755 
756  for(const auto& w : c.child_range("widget")) {
757  VALIDATE(w.has_attribute("id"), missing_mandatory_wml_key("[list_data][row][column][widget]", "id"));
758 
759  for(const auto& i : w.attribute_range()) {
760  list_data.back()[w["id"]][i.first] = i.second;
761  }
762  }
763  }
764  }
765 
766  return list_data;
767 }
768 
769 builder_listbox::builder_listbox(const config& cfg)
770  : builder_styled_widget(cfg)
771  , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
772  , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
773  , header(nullptr)
774  , footer(nullptr)
775  , list_builder(nullptr)
776  , list_data()
777  , has_minimum_(cfg["has_minimum"].to_bool(true))
778  , has_maximum_(cfg["has_maximum"].to_bool(true))
779 {
780  if(const config& h = cfg.child("header")) {
781  header = std::make_shared<builder_grid>(h);
782  }
783 
784  if(const config& f = cfg.child("footer")) {
785  footer = std::make_shared<builder_grid>(f);
786  }
787 
788  const config& l = cfg.child("list_definition");
789 
790  VALIDATE(l, _("No list defined."));
791 
792  list_builder = std::make_shared<builder_grid>(l);
793  assert(list_builder);
794 
795  VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row."));
796 
797  if(cfg.has_child("list_data")) {
798  list_data = parse_list_data(cfg.child("list_data"), list_builder->cols);
799  }
800 }
801 
803 {
805 
808 
809  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.\n";
810 
811  const auto conf = widget->cast_config_to<listbox_definition>();
812  assert(conf);
813 
814  widget->init_grid(*conf->grid);
815 
816  widget->finalize(header, footer, list_data);
817 
818  return widget;
819 }
820 
822  : builder_styled_widget(cfg)
823  , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
824  , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
825  , list_builder(nullptr)
826  , list_data()
827  , has_minimum_(cfg["has_minimum"].to_bool(true))
828  , has_maximum_(cfg["has_maximum"].to_bool(true))
829 {
830  const config& l = cfg.child("list_definition");
831 
832  VALIDATE(l, _("No list defined."));
833 
834  list_builder = std::make_shared<builder_grid>(l);
835  assert(list_builder);
836 
837  VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row."));
838 
839  if(cfg.has_child("list_data")) {
840  list_data = parse_list_data(cfg.child("list_data"), list_builder->cols);
841  }
842 }
843 
845 {
847 
850 
851  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.\n";
852 
853  const auto conf = widget->cast_config_to<listbox_definition>();
854  assert(conf);
855 
856  widget->init_grid(*conf->grid);
857 
858  widget->finalize(nullptr, nullptr, list_data);
859 
860  return widget;
861 }
862 
864  : builder_styled_widget(cfg)
865  , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
866  , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
867  , list_builder(nullptr)
868  , list_data()
869  , has_minimum_(cfg["has_minimum"].to_bool(true))
870  , has_maximum_(cfg["has_maximum"].to_bool(true))
871 {
872  const config& l = cfg.child("list_definition");
873 
874  VALIDATE(l, _("No list defined."));
875 
876  list_builder = std::make_shared<builder_grid>(l);
877  assert(list_builder);
878 
879  VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row."));
880 
881  if(cfg.has_child("list_data")) {
882  list_data = parse_list_data(cfg.child("list_data"), list_builder->cols);
883  }
884 }
885 
887 {
889 
892 
893  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.\n";
894 
895  const auto conf = widget->cast_config_to<listbox_definition>();
896  assert(conf);
897 
898  widget->init_grid(*conf->grid);
899 
900  widget->finalize(nullptr, nullptr, list_data);
901 
902  return widget;
903 }
904 
905 } // namespace implementation
906 
907 // }------------ END --------------
908 
909 } // namespace gui2
const order_pair get_active_sorting_option()
Definition: listbox.cpp:663
Define the common log macros for the gui toolkit.
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)=0
Left arrow key pressed.
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
Base class of a resolution, contains the common keys for a resolution.
void keyboard_capture(widget *widget)
Definition: window.cpp:1273
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)=0
Up arrow key pressed.
virtual unsigned get_item_at_ordered(unsigned index_ordered) const =0
If a sort-order is being applied, maps from sorted to unsorted indicies.
#define DBG_GUI_P
Definition: log.hpp:65
void set_active_sorting_option(const order_pair &sort_by, const bool select_first=false)
Sorts the listbox by a pre-set sorting option.
Definition: listbox.cpp:644
Defines the exception classes for the layout algorithm.
Small abstract helper class.
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
Definition: grid.cpp:603
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:414
#define LOG_GUI_L
Definition: log.hpp:55
Helper class to block invalidate_layout.
Definition: window.hpp:196
std::vector< state_definition > state
void set_horizontal_scrollbar_mode(const scrollbar_mode scrollbar_mode)
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: listbox.cpp:453
#define DBG_GUI_L
Definition: log.hpp:54
virtual unsigned get_item_count() const =0
Returns the number of items.
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)=0
Right arrow key pressed.
void finalize_setup()
The builder needs to call us so we do our setup.
void update_visible_area_on_key_event(const KEY_SCROLL_DIRECTION direction)
Helper to update visible area after a key event.
Definition: listbox.cpp:469
virtual widget * build() const override
Definition: listbox.cpp:802
virtual void set_item_shown(const unsigned index, const bool show)=0
Shows or hides an item.
void mark_as_unsorted()
Deactivates all sorting toggle buttons at the top, making the list look like it&#39;s not sorted...
Definition: listbox.cpp:676
std::pair< int, preferences::SORT_ORDER > order_pair
Definition: listbox.hpp:284
preferences::SORT_ORDER SORT_ORDER
Definition: listbox.cpp:41
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:497
bool rects_overlap(const SDL_Rect &rect1, const SDL_Rect &rect2)
Tests whether two rectangles overlap.
Definition: rect.cpp:33
const grid & get_grid() const
virtual void place(const point &origin, const point &size) override=0
See widget::place.
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:545
visibility get_visible() const
Definition: widget.cpp:502
virtual void place(const point &origin, const point &size) override
See widget::place.
#define LOG_HEADER
Definition: listbox.cpp:39
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:406
virtual void set_order(const order_func &order)=0
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
virtual bool has_widget(const widget &widget) const override
See widget::has_widget.
Definition: grid.cpp:666
This file contains the window object, this object is a top level container which has the event manage...
child_itors child_range(config_key_type key)
Definition: config.cpp:356
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:309
Base class for all widgets.
Definition: widget.hpp:49
std::function< bool(unsigned, unsigned)> order_func
Definition: generator.hpp:248
unsigned get_height() const
Definition: widget.cpp:329
void register_translatable_sorting_option(const int col, translatable_sorter_func_t f)
Registers a special sorting function specifically for translatable values.
Definition: listbox.cpp:636
virtual grid & item(const unsigned index)=0
Gets the grid of an item.
#define h
void swap_grid(grid *g, grid *content_grid, widget *widget, const std::string &id)
Swaps an item in a grid for another one.
std::unique_ptr< grid > content_grid_
The grid that holds the content.
bool row_selected(const unsigned row)
Check if a row is selected.
Definition: listbox.cpp:270
bool need_layout_
Definition: listbox.hpp:372
virtual unsigned get_selected_item_count() const =0
Returns the number of selected items.
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:276
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:504
static std::string _(const char *str)
Definition: gettext.hpp:92
void set_row_active(const unsigned row, const bool active)
Makes a row active or inactive.
Definition: listbox.cpp:138
unsigned get_width() const
Definition: widget.cpp:324
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:251
virtual widget * build() const override
Definition: listbox.cpp:886
torder_list orders_
Definition: listbox.hpp:375
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
int x
x coordinate.
Definition: point.hpp:44
bool select_row_at(const unsigned row, const bool select=true)
Selects a row at the given position, regardless of sorting order.
Definition: listbox.cpp:264
Generic file dialog.
Definition: field-fwd.hpp:22
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:88
virtual point calculate_best_size() const override=0
See widget::calculate_best_size.
The listbox class.
Definition: listbox.hpp:42
void finalize(builder_grid_const_ptr header, builder_grid_const_ptr footer, const std::vector< std::map< std::string, string_map >> &list_data)
Finishes the building initialization of the widget.
Definition: listbox.cpp:546
Base container class.
Definition: grid.hpp:30
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:590
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
std::string definition
Parameters for the styled_widget.
void populate_dirty_list(window &caller, std::vector< widget *> &call_stack)
Adds a widget to the dirty list if it is dirty.
Definition: widget.cpp:416
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:591
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:186
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:192
Abstract base class for the generator.
Definition: generator.hpp:37
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
listbox_definition(const config &cfg)
Definition: listbox.cpp:714
std::vector< std::map< std::string, string_map > > list_data
Listbox data.
Definition: listbox.hpp:510
virtual void set_content_size(const point &origin, const point &size) override
Inherited from scrollbar_container.
Definition: listbox.cpp:685
This file contains the settings handling of the widget library.
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:126
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:465
builder_grid_const_ptr list_builder_
Contains the builder for the new items.
Definition: listbox.hpp:370
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
Definition: listbox.cpp:443
void init_grid(const builder_grid &grid_builder)
Initializes and builds the grid.
void toggle_item(const unsigned index)
Toggles the selection state of an item.
Definition: generator.hpp:95
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:46
virtual void create_items(const int index, const builder_grid &list_builder, const std::vector< string_map > &data, const std::function< void(widget &)> &callback)=0
Creates one or more new item(s).
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:132
void resize_content(const int width_modification, const int height_modification, const int width_modification_pos=-1, const int height_modification_pos=-1)
Resizes the content.
Definition: listbox.cpp:384
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
bool any_rows_shown() const
Definition: listbox.cpp:227
int get_y() const
Definition: widget.cpp:319
void order_by_column(unsigned column, widget &widget)
Definition: listbox.cpp:589
bool update_content_size()
Request to update the size of the content after changing the content.
Definition: listbox.cpp:333
virtual widget * build() const override
Definition: listbox.cpp:844
virtual bool get_item_shown(const unsigned index) const =0
Returns whether the item is shown.
void list_item_clicked(widget &caller)
Function to call after the user clicked on a row.
Definition: listbox.cpp:282
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: grid.cpp:479
void order_by(const generator_base::order_func &func)
Definition: listbox.cpp:619
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: listbox.cpp:328
std::vector< std::map< std::string, string_map > > list_data
Listbox data.
Definition: listbox.hpp:555
Base class for creating containers with one or two scrollbar(s).
virtual int get_selected_item() const =0
Returns the selected item.
std::size_t i
Definition: function.cpp:940
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:532
#define LOG_GUI_G
Definition: log.hpp:41
virtual void clear()=0
Deletes all items.
Default, unset return value.
Definition: retval.hpp:31
std::shared_ptr< const typename T::resolution > cast_config_to() const
Casts the current resolution definition config to the respective type of a derived widget...
std::function< void(unsigned, preferences::SORT_ORDER)> callback_order_change_
Definition: listbox.hpp:377
void set_vertical_scrollbar_item_position(const unsigned position)
Move the vertical scrollbar to a position.
mock_party p
virtual grid & create_item(const int index, const builder_grid &list_builder, const string_map &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
The user set the widget invisible, that means:
window * get_window()
Get the parent window.
Definition: widget.cpp:116
static map_location::DIRECTION s
const SDL_Rect & content_visible_area() const
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
std::array< generator_base::order_func, 2 > generator_sort_array
Definition: generator.hpp:389
Holds a 2D point.
Definition: point.hpp:23
grid & add_row(const string_map &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:67
void set_active(const bool active)
Activates all children.
Definition: grid.cpp:162
int w
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
virtual void set_value(unsigned value, bool fire_event=false)=0
Select the styled_widget.
scrollbar_mode get_scrollbar_mode(const std::string &scrollbar_mode)
Returns the scrollbar mode flags.
Definition: helper.cpp:120
std::shared_ptr< const builder_grid > builder_grid_const_ptr
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition: listbox.cpp:222
virtual void layout_children() override
See widget::layout_children.
Definition: listbox.cpp:438
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:304
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:238
std::vector< std::map< std::string, string_map > > list_data
Listbox data.
Definition: listbox.hpp:601
Contains the SDL_Rect helper code.
virtual void delete_item(const unsigned index)=0
Deletes an item.
#define f
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
static std::vector< std::map< std::string, string_map > > parse_list_data(const config &data, const unsigned int req_cols)
Definition: listbox.cpp:738
int icompare(const std::string &s1, const std::string &s2)
Case-insensitive lexicographical comparison.
Definition: gettext.cpp:474
SDL_Rect content_visible_area_
Cache for the visible area for the content.
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)=0
Down arrow key pressed.
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:87
const bool is_horizontal_
Definition: listbox.hpp:367
#define REGISTER_WIDGET3(type, id, key)
Registers a widget.
unsigned get_vertical_scrollbar_item_position() const
Returns current position of the vertical scrollbar.
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:68
void set_column_order(unsigned col, const generator_sort_array &func)
Definition: listbox.cpp:627
virtual void set_size(const point &size)
Sets the size of the widget.
Definition: widget.cpp:226
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:796
std::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:490
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
generator_base * generator_
Contains a pointer to the generator.
Definition: listbox.hpp:365
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
mock_char c
std::function< std::string(const int)> translatable_sorter_func_t
Definition: listbox.hpp:279
unsigned get_horizontal_scrollbar_item_position() const
Returns current position of the horizontal scrollbar.
int y
y coordinate.
Definition: point.hpp:47
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:544
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:64
#define DBG_GUI_G
Definition: log.hpp:40
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:144
std::shared_ptr< builder_grid > builder_grid_ptr
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:518
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:496
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: listbox.cpp:352
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:299
Class for a toggle button.
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
boost::dynamic_bitset get_items_shown() const
Returns the visibility of all the items as a bit set.
Definition: generator.hpp:119
virtual unsigned get_value() const =0
Is the styled_widget selected?
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:409
void set_horizontal_scrollbar_item_position(const unsigned position)
Move the horizontal scrollbar to a position.