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