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