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