The Battle for Wesnoth  1.19.7+dev
listbox.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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"
21 #include "gui/core/log.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "sdl/rect.hpp"
31 #include "wml_exception.hpp"
32 #include <functional>
33 #include "utils/optional_fwd.hpp"
34 
35 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
36 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
37 
38 namespace gui2
39 {
40 // ------------ WIDGET -----------{
41 
42 REGISTER_WIDGET(listbox)
43 REGISTER_WIDGET3(listbox_definition, horizontal_listbox, nullptr)
44 REGISTER_WIDGET3(listbox_definition, grid_listbox, nullptr)
45 
46 listbox::listbox(const implementation::builder_listbox_base& builder)
47  : scrollbar_container(builder, type())
48  , generator_(nullptr)
49  , placement_(builder.placement)
50  , list_builder_(builder.list_builder)
51  , orders_()
52  , callback_order_change_()
53 {
54  const auto conf = cast_config_to<listbox_definition>();
55  assert(conf);
56 
57  // FIXME: replacements (rightfully) clobber IDs. Is there a way around that?
59 
60  if(auto header = builder.header) {
61  header->id = "_header_grid";
62  replacements.try_emplace("_header_grid_placeholder", std::move(header));
63  }
64 
65  if(auto footer = builder.footer) {
66  footer->id = "_footer_grid";
67  replacements.try_emplace("_footer_grid_placeholder", std::move(footer));
68  }
69 
70  conf->grid->build(get_grid(), replacements);
71 
72  // "Inherited."
74 
76  builder.has_minimum,
77  builder.has_maximum,
78  builder.placement,
79  builder.allow_selection);
80 
81  // Save our *non-owning* pointer before this gets moved into the grid.
82  generator_ = generator.get();
83  assert(generator_);
84 
85  generator->create_items(-1, *list_builder_, builder.list_data,
86  std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
87 
88  // TODO: can we use the replacements system here?
89  swap_grid(nullptr, content_grid(), std::move(generator), "_list_grid");
90 }
91 
92 grid& listbox::add_row(const widget_item& item, const int index)
93 {
94  assert(generator_);
95  grid& row = generator_->create_item(index, *list_builder_, item, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
96 
97  resize_content(row);
98 
99  return row;
100 }
101 
103 {
104  assert(generator_);
105  grid& row = generator_->create_item(index, *list_builder_, data, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
106 
107  resize_content(row);
108 
109  return row;
110 }
111 
112 void listbox::remove_row(const unsigned row, unsigned count)
113 {
114  assert(generator_);
115 
116  if(row >= get_item_count()) {
117  return;
118  }
119 
120  if(!count || count + row > get_item_count()) {
121  count = get_item_count() - row;
122  }
123 
124  int height_reduced = 0;
125  int width_reduced = 0;
126 
127  const bool is_horizontal = placement_ == generator_base::horizontal_list;
128 
129  // TODO: Fix this for horizontal listboxes
130  // Note the we have to use content_grid_ and cannot use "_list_grid" which is what generator_ uses.
131  int row_pos_y = is_horizontal ? -1 : generator_->item(row).get_y() - content_grid_->get_y();
132  int row_pos_x = is_horizontal ? -1 : 0;
133 
134  for(; count; --count) {
136  if(is_horizontal) {
137  width_reduced += generator_->item(row).get_width();
138  } else {
139  height_reduced += generator_->item(row).get_height();
140  }
141  }
142 
143  generator_->delete_item(row);
144  }
145 
146  if((height_reduced != 0 || width_reduced != 0) && get_item_count() != 0) {
147  resize_content(-width_reduced, -height_reduced, row_pos_x, row_pos_y);
148  } else {
150  }
151 }
152 
154 {
155  generator_->clear();
157 }
158 
159 unsigned listbox::get_item_count() const
160 {
161  assert(generator_);
162  return generator_->get_item_count();
163 }
164 
165 void listbox::set_row_active(const unsigned row, const bool active)
166 {
167  assert(generator_);
168  generator_->item(row).set_active(active);
169 }
170 
171 void listbox::set_row_shown(const unsigned row, const bool shown)
172 {
173  assert(generator_);
174 
175  window* window = get_window();
176  assert(window);
177 
178  const int selected_row = get_selected_row();
179 
180  bool resize_needed = false;
181 
182  // Local scope for invalidate_layout_blocker
183  {
184  window::invalidate_layout_blocker invalidate_layout_blocker(*window);
185 
186  generator_->set_item_shown(row, shown);
187 
188  point best_size = generator_->calculate_best_size();
189  generator_->place(generator_->get_origin(), {std::max(best_size.x, content_visible_area().w), best_size.y});
190 
191  resize_needed = !content_resize_request();
192  }
193 
194  if(resize_needed) {
196  } else {
197  content_grid_->set_visible_rectangle(content_visible_area());
198  queue_redraw(); // TODO: draw_manager - does this get the right area?
199  }
200 
201  if(selected_row != get_selected_row()) {
202  fire(event::NOTIFY_MODIFIED, *this, nullptr);
203  }
204 }
205 
206 void listbox::set_row_shown(const boost::dynamic_bitset<>& shown)
207 {
208  assert(generator_);
209  assert(shown.size() == get_item_count());
210 
211  if(generator_->get_items_shown() == shown) {
212  LOG_GUI_G << LOG_HEADER << " returning early";
213  return;
214  }
215 
216  window* window = get_window();
217  assert(window);
218 
219  const int selected_row = get_selected_row();
220 
221  bool resize_needed = false;
222 
223  // Local scope for invalidate_layout_blocker
224  {
225  window::invalidate_layout_blocker invalidate_layout_blocker(*window);
226 
227  for(std::size_t i = 0; i < shown.size(); ++i) {
228  generator_->set_item_shown(i, shown[i]);
229  }
230 
231  point best_size = generator_->calculate_best_size();
232  generator_->place(generator_->get_origin(), {std::max(best_size.x, content_visible_area().w), best_size.y});
233 
234  resize_needed = !content_resize_request(true);
235  }
236 
237  if(resize_needed) {
239  } else {
240  content_grid_->set_visible_rectangle(content_visible_area());
241  queue_redraw(); // TODO: draw_manager - does this get the right area?
242  }
243 
244  if(selected_row != get_selected_row()) {
245  fire(event::NOTIFY_MODIFIED, *this, nullptr);
246  }
247 }
248 
249 std::size_t listbox::filter_rows_by(const std::function<bool(std::size_t)>& filter)
250 {
251  boost::dynamic_bitset<> mask;
252  mask.resize(get_item_count(), true);
253 
254  for(std::size_t i = 0; i < mask.size(); ++i) {
255  mask[i] = std::invoke(filter, i);
256  }
257 
258  set_row_shown(mask);
259  return mask.count();
260 }
261 
262 boost::dynamic_bitset<> listbox::get_rows_shown() const
263 {
264  return generator_->get_items_shown();
265 }
266 
267 const grid* listbox::get_row_grid(const unsigned row) const
268 {
269  assert(generator_);
270  // rename this function and can we return a reference??
271  return &generator_->item(row);
272 }
273 
274 grid* listbox::get_row_grid(const unsigned row)
275 {
276  assert(generator_);
277  return &generator_->item(row);
278 }
279 
280 bool listbox::select_row(const unsigned row, const bool select)
281 {
282  if(row >= get_item_count()) {
283  throw std::invalid_argument("invalid listbox index");
284  }
285  assert(generator_);
286 
287  unsigned int before = generator_->get_selected_item_count();
288  generator_->select_item(row, select);
289 
290  return before != generator_->get_selected_item_count();
291 }
292 
293 bool listbox::select_row_at(const unsigned row, const bool select)
294 {
295  assert(generator_);
296  return select_row(generator_->get_item_at_ordered(row), select);
297 }
298 
299 bool listbox::row_selected(const unsigned row)
300 {
301  assert(generator_);
302  return generator_->is_selected(row);
303 }
304 
306 {
307  assert(generator_);
308  return generator_->get_selected_item();
309 }
310 
312 {
313  assert(generator_);
314 
315  /** @todo Hack to capture the keyboard focus. */
316  get_window()->keyboard_capture(this);
317 
318  for(std::size_t i = 0; i < generator_->get_item_count(); ++i) {
319  if(generator_->item(i).has_widget(caller)) {
320  toggle_button* checkbox = dynamic_cast<toggle_button*>(&caller);
321 
322  if(checkbox != nullptr) {
323  generator_->select_item(i, checkbox->get_value_bool());
324  } else {
326  }
327 
328  // TODO: enable this code once toggle_panel::set_value dispatches
329  // NOTIFY_MODIFED events. See comment in said function for more details.
330 #if 0
331  selectable_item& selectable = dynamic_cast<selectable_item&>(caller);
332 
333  generator_->select_item(i, selectable.get_value_bool());
334 #endif
335 
336  fire(event::NOTIFY_MODIFIED, *this, nullptr);
337  break;
338  }
339  }
340 
341  const int selected_item = generator_->get_selected_item();
342  if(selected_item == -1) {
343  return;
344  }
345 
346  const rect& visible = content_visible_area();
347  rect r = generator_->item(selected_item).get_rectangle();
348 
349  if(visible.overlaps(r)) {
350  r.x = visible.x;
351  r.w = visible.w;
352 
354  }
355 }
356 
357 void listbox::set_self_active(const bool /*active*/)
358 {
359  /* DO NOTHING */
360 }
361 
363 {
365  return true;
366  }
367 
368  if(get_size() == point()) {
369  return false;
370  }
371 
372  if(content_resize_request(true)) {
373  content_grid_->set_visible_rectangle(content_visible_area());
374  queue_redraw(); // TODO: draw_manager - does this get the right area?
375  return true;
376  }
377 
378  return false;
379 }
380 
381 void listbox::place(const point& origin, const point& size)
382 {
383  utils::optional<unsigned> vertical_scrollbar_position, horizontal_scrollbar_position;
384 
385  // Check if this is the first time placing the list box
386  if(get_origin() != point {-1, -1}) {
387  vertical_scrollbar_position = get_vertical_scrollbar_item_position();
388  horizontal_scrollbar_position = get_horizontal_scrollbar_item_position();
389  }
390 
391  // Inherited.
393 
394  const int selected_item = generator_->get_selected_item();
395  if(vertical_scrollbar_position && horizontal_scrollbar_position) {
396  LOG_GUI_L << LOG_HEADER << " restoring scroll position";
397 
398  set_vertical_scrollbar_item_position(*vertical_scrollbar_position);
399  set_horizontal_scrollbar_item_position(*horizontal_scrollbar_position);
400  } else if(selected_item != -1) {
401  LOG_GUI_L << LOG_HEADER << " making the initially selected item visible";
402 
403  const SDL_Rect& visible = content_visible_area();
404  SDL_Rect rect = generator_->item(selected_item).get_rectangle();
405 
406  rect.x = visible.x;
407  rect.w = visible.w;
408 
410  }
411 }
412 
413 void listbox::resize_content(const int width_modification,
414  const int height_modification,
415  const int width_modification_pos,
416  const int height_modification_pos)
417 {
418  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
419  << width_modification << " height_modification " << height_modification << ".";
420 
422  width_modification, height_modification, width_modification_pos, height_modification_pos))
423  {
424  // Calculate new size.
426  size.x += width_modification;
427  size.y += height_modification;
428 
429  // Set new size.
431  update_layout();
432 
433  // If the content grows assume it "overwrites" the old content.
434  if(width_modification < 0 || height_modification < 0) {
435  queue_redraw();
436  }
437 
438  DBG_GUI_L << LOG_HEADER << " succeeded.";
439  } else {
440  DBG_GUI_L << LOG_HEADER << " failed.";
441  }
442 }
443 
445 {
446  if(row.get_visible() == visibility::invisible) {
447  return;
448  }
449 
450  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " row size " << row.get_best_size()
451  << ".";
452 
453  const point content = content_grid()->get_size();
454  point size = row.get_best_size();
455 
456  if(size.x < content.x) {
457  size.x = 0;
458  } else {
459  size.x -= content.x;
460  }
461 
462  resize_content(size.x, size.y);
463 }
464 
466 {
467  // Get the size from the base class, then add any extra space for the header and footer.
469 
470  if(const grid* header = get_grid().find_widget<const grid>("_header_grid", false, false)) {
471  if(header->get_visible() != widget::visibility::invisible) {
472  result.y += header->get_best_size().y;
473  }
474  }
475 
476  if(const grid* footer = get_grid().find_widget<const grid>("_footer_grid", false, false)) {
477  if(footer->get_visible() != widget::visibility::invisible) {
478  result.y += footer->get_best_size().y;
479  }
480  }
481 
482  return result;
483 }
484 
486 {
487  const SDL_Rect& visible = content_visible_area();
489 
490  // When scrolling make sure the new items are visible...
491  if(direction == KEY_VERTICAL) {
492  // ...but leave the horizontal scrollbar position.
493  rect.x = visible.x;
494  rect.w = visible.w;
495  } else {
496  // ...but leave the vertical scrollbar position.
497  rect.y = visible.y;
498  rect.h = visible.h;
499  }
500 
502 
503  fire(event::NOTIFY_MODIFIED, *this, nullptr);
504 }
505 
506 void listbox::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
507 {
508  assert(generator_);
509 
510  generator_->handle_key_up_arrow(modifier, handled);
511 
512  if(handled) {
514  } else {
515  // Inherited.
516  scrollbar_container::handle_key_up_arrow(modifier, handled);
517  }
518 }
519 
520 void listbox::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
521 {
522  assert(generator_);
523 
524  generator_->handle_key_down_arrow(modifier, handled);
525 
526  if(handled) {
528  } else {
529  // Inherited.
530  scrollbar_container::handle_key_up_arrow(modifier, handled);
531  }
532 }
533 
534 void listbox::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
535 {
536  assert(generator_);
537 
538  generator_->handle_key_left_arrow(modifier, handled);
539 
540  // Inherited.
541  if(handled) {
543  } else {
545  }
546 }
547 
548 void listbox::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
549 {
550  assert(generator_);
551 
552  generator_->handle_key_right_arrow(modifier, handled);
553 
554  // Inherited.
555  if(handled) {
557  } else {
559  }
560 }
561 
562 void listbox::initialize_sorter(std::string_view id, generator_sort_array&& array)
563 {
564  auto header = find_widget<grid>("_header_grid", false, false);
565  if(!header) return;
566 
567  auto toggle = header->find_widget<selectable_item>(id, false, false);
568  if(!toggle) return;
569 
570  const std::size_t i = orders_.size();
571  orders_.emplace_back(toggle, std::move(array));
572 
573  // TODO: we can bind the pair directly if we remove the on-order callback
574  connect_signal_notify_modified(dynamic_cast<widget&>(*toggle),
575  std::bind(&listbox::order_by_column, this, i, std::placeholders::_1));
576 }
577 
578 void listbox::order_by_column(unsigned column, widget& widget)
579 {
580  selectable_item& selectable = dynamic_cast<selectable_item&>(widget);
581 
582  for(auto& [w, _] : orders_) {
583  if(w && w != &selectable) {
584  w->set_value(utils::to_underlying(sort_order::type::none));
585  }
586  }
587 
588  auto order = sort_order::get_enum(selectable.get_value()).value_or(sort_order::type::none);
589  if(static_cast<unsigned>(order) > orders_[column].second.size()) {
590  return;
591  }
592 
593  if(order == sort_order::type::none) {
594  order_by(std::less<unsigned>());
595  } else {
596  order_by(orders_[column].second[utils::to_underlying(order) - 1]);
597  }
598 
599  if(callback_order_change_ != nullptr) {
600  callback_order_change_(column, order);
601  }
602 }
603 
605 {
606  generator_->set_order(func);
607 
608  update_layout();
609 }
610 
611 bool listbox::sort_helper::less(const t_string& lhs, const t_string& rhs)
612 {
613  return translation::icompare(lhs, rhs) < 0;
614 }
615 
616 bool listbox::sort_helper::more(const t_string& lhs, const t_string& rhs)
617 {
618  return translation::icompare(lhs, rhs) > 0;
619 }
620 
621 void listbox::set_active_sorter(std::string_view id, sort_order::type order, bool select_first)
622 {
623  for(auto& [w, _] : orders_) {
624  if(!w || dynamic_cast<widget*>(w)->id() != id) continue;
625 
626  // Set the state and fire a modified event to handle updating the list
627  w->set_value(utils::to_underlying(order), true);
628 
629  if(select_first && generator_->get_item_count() > 0) {
630  select_row_at(0);
631  }
632  }
633 }
634 
635 std::pair<widget*, sort_order::type> listbox::get_active_sorter() const
636 {
637  for(const auto& [w, _] : orders_) {
638  if(!w) continue;
639 
640  auto sort = sort_order::get_enum(w->get_value()).value_or(sort_order::type::none);
641  if(sort != sort_order::type::none) {
642  return { dynamic_cast<widget*>(w), sort };
643  }
644  }
645 
646  return { nullptr, sort_order::type::none };
647 }
648 
650 {
651  for(auto& [w, _] : orders_) {
652  if(w) {
653  w->set_value(utils::to_underlying(sort_order::type::none));
654  }
655  }
656 }
657 
658 void listbox::set_content_size(const point& origin, const point& size)
659 {
660  /** @todo This function needs more testing. */
661  assert(content_grid());
662 
663  const int best_height = content_grid()->get_best_size().y;
664  const point s(size.x, size.y < best_height ? size.y : best_height);
665 
666  content_grid()->place(origin, s);
667 }
668 
670 {
671  assert(content_grid());
672 
673  // If we haven't initialized, or have no content, just return.
675  if(size.x <= 0 || size.y <= 0) {
676  return;
677  }
678 
680 
681  const SDL_Rect& visible = content_visible_area_;
683 
684  queue_redraw();
685 }
686 
687 // }---------- DEFINITION ---------{
688 
691 {
692  DBG_GUI_P << "Parsing listbox " << id;
693 
694  load_resolutions<resolution>(cfg);
695 }
696 
698  : resolution_definition(cfg)
699  , grid(nullptr)
700 {
701  // Note the order should be the same as the enum state_t in listbox.hpp.
702  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_enabled")));
703  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_disabled")));
704 
705  auto child = VALIDATE_WML_CHILD(cfg, "grid", missing_mandatory_wml_tag("listbox_definition][resolution", "grid"));
706  grid = std::make_shared<builder_grid>(child);
707 }
708 
709 namespace implementation
710 {
711 static std::vector<widget_data> parse_list_data(const config& data, const unsigned int req_cols)
712 {
713  std::vector<widget_data> list_data;
714 
715  for(const auto& row : data.child_range("row")) {
716  auto cols = row.child_range("column");
717 
718  VALIDATE(static_cast<unsigned>(cols.size()) == req_cols,
719  _("‘list_data’ must have the same number of columns as the ‘list_definition’.")
720  );
721 
722  for(const auto& c : cols) {
723  list_data.emplace_back();
724 
725  for(const auto& [key, value] : c.attribute_range()) {
726  list_data.back()[""][key] = value;
727  }
728 
729  for(const auto& w : c.child_range("widget")) {
730  VALIDATE(w.has_attribute("id"), missing_mandatory_wml_key("[list_data][row][column][widget]", "id"));
731 
732  for(const auto& [key, value] : w.attribute_range()) {
733  list_data.back()[w["id"]][key] = value;
734  }
735  }
736  }
737  }
738 
739  return list_data;
740 }
741 
742 builder_listbox_base::builder_listbox_base(const config& cfg, const generator_base::placement placement)
744  , placement(placement)
745  , header(nullptr)
746  , footer(nullptr)
747  , list_builder(nullptr)
748  , list_data()
749  , has_minimum(cfg["has_minimum"].to_bool(true))
750  , has_maximum(cfg["has_maximum"].to_bool(true))
751  , allow_selection(cfg["allow_selection"].to_bool(true))
752 {
753  auto l = cfg.optional_child("list_definition");
754 
755  VALIDATE(l, _("No list defined."));
756 
757  list_builder = std::make_shared<builder_grid>(*l);
758  assert(list_builder);
759 
760  VALIDATE(list_builder->rows == 1, _("A ‘list_definition’ should contain one row."));
761 
762  if(cfg.has_child("list_data")) {
763  list_data = parse_list_data(cfg.mandatory_child("list_data"), list_builder->cols);
764  }
765 }
766 
767 std::unique_ptr<widget> builder_listbox_base::build() const
768 {
769  auto widget = std::make_unique<listbox>(*this);
770  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.";
771  return widget;
772 }
773 
775  : builder_listbox_base(cfg, generator_base::vertical_list)
776 {
777  if(auto h = cfg.optional_child("header")) {
778  header = std::make_shared<builder_grid>(*h);
779  }
780 
781  if(auto f = cfg.optional_child("footer")) {
782  footer = std::make_shared<builder_grid>(*f);
783  }
784 }
785 
786 } // namespace implementation
787 
788 // }------------ END --------------
789 
790 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:366
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:316
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
const grid & get_grid() const
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:74
Abstract base class for the generator.
Definition: generator.hpp:39
std::function< bool(unsigned, unsigned)> order_func
Definition: generator.hpp:248
virtual void set_order(const order_func &order)=0
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)=0
Left arrow key pressed.
virtual unsigned get_selected_item_count() const =0
Returns the number of selected items.
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)=0
Right arrow key pressed.
virtual void set_item_shown(const unsigned index, const bool show)=0
Shows or hides an item.
virtual void delete_item(const unsigned index)=0
Deletes an item.
boost::dynamic_bitset get_items_shown() const
Returns the visibility of all the items as a bit set.
Definition: generator.hpp:120
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)=0
Up arrow key pressed.
virtual void place(const point &origin, const point &size) override=0
See widget::place.
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 point calculate_best_size() const override=0
See widget::calculate_best_size.
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.
virtual grid & item(const unsigned index)=0
Gets the grid of an item.
virtual unsigned get_item_count() const =0
Returns the number of items.
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:1160
void toggle_item(const unsigned index)
Toggles the selection state of an item.
Definition: generator.hpp:96
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
placement
Determines how the items are placed.
Definition: generator.hpp:48
virtual unsigned get_item_at_ordered(unsigned index_ordered) const =0
If a sort-order is being applied, maps from sorted to unsorted indicies.
virtual void clear()=0
Deletes all items.
virtual int get_selected_item() const =0
Returns the selected item.
Basic template class to generate new items.
virtual void create_items(const int index, const builder_grid &list_builder, const std::vector< widget_data > &data, const std::function< void(widget &)> &callback) override
Inherited from generator_base.
Base container class.
Definition: grid.hpp:32
void set_active(const bool active)
Activates all children.
Definition: grid.cpp:167
virtual bool has_widget(const widget &widget) const override
See widget::has_widget.
Definition: grid.cpp:655
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: grid.cpp:484
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
Definition: grid.cpp:608
The listbox class.
Definition: listbox.hpp:41
void update_layout()
Updates internal layout.
Definition: listbox.cpp:669
void list_item_clicked(widget &caller)
Function to call after the user clicked on a row.
Definition: listbox.cpp:311
void mark_as_unsorted()
Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted.
Definition: listbox.cpp:649
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: listbox.cpp:465
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:548
void set_row_active(const unsigned row, const bool active)
Makes a row active or inactive.
Definition: listbox.cpp:165
std::vector< std::pair< selectable_item *, generator_sort_array > > orders_
Definition: listbox.hpp:387
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:171
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:534
void order_by_column(unsigned column, widget &widget)
Definition: listbox.cpp:578
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:413
void order_by(const generator_base::order_func &func)
Definition: listbox.cpp:604
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: listbox.cpp:357
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:92
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:267
void set_active_sorter(std::string_view id, sort_order::type order, bool select_first=false)
Sorts the listbox by a pre-set sorting option.
Definition: listbox.cpp:621
generator_base::placement placement_
Definition: listbox.hpp:382
virtual void set_content_size(const point &origin, const point &size) override
Inherited from scrollbar_container.
Definition: listbox.cpp:658
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: listbox.cpp:381
void update_visible_area_on_key_event(const KEY_SCROLL_DIRECTION direction)
Helper to update visible area after a key event.
Definition: listbox.cpp:485
void initialize_sorter(std::string_view id, generator_sort_array &&)
Implementation detail of set_single_sorter.
Definition: listbox.cpp:562
generator_base * generator_
Contains a pointer to the generator.
Definition: listbox.hpp:380
std::function< void(unsigned, sort_order::type)> callback_order_change_
Definition: listbox.hpp:389
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:280
bool update_content_size()
Request to update the size of the content after changing the content.
Definition: listbox.cpp:362
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition: listbox.cpp:262
std::size_t filter_rows_by(const std::function< bool(std::size_t)> &filter)
Hides all rows for which the given predicate returns false.
Definition: listbox.cpp:249
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:520
std::pair< widget *, sort_order::type > get_active_sorter() const
Returns a widget pointer to the active sorter, along with its corresponding order.
Definition: listbox.cpp:635
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:112
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:153
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:305
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:159
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:293
builder_grid_const_ptr list_builder_
Contains the builder for the new items.
Definition: listbox.hpp:385
bool row_selected(const unsigned row)
Check if a row is selected.
Definition: listbox.cpp:299
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:506
Base class for creating containers with one or two scrollbar(s).
SDL_Rect content_visible_area_
Cache for the visible area for the content.
void set_horizontal_scrollbar_item_position(const unsigned position)
Move the horizontal scrollbar to a position.
void finalize_setup()
The builder needs to call us so we do our setup.
unsigned get_horizontal_scrollbar_item_position() const
Returns current position of the horizontal scrollbar.
virtual void place(const point &origin, const point &size) override
See widget::place.
unsigned get_vertical_scrollbar_item_position() const
Returns current position of the vertical scrollbar.
void set_vertical_scrollbar_item_position(const unsigned position)
Move the vertical scrollbar to a position.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
std::unique_ptr< grid > content_grid_
The grid that holds the content.
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
const SDL_Rect & content_visible_area() const
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
Small abstract helper class.
virtual unsigned get_value() const =0
Is the styled_widget selected?
Base class for all widgets.
Definition: widget.hpp:55
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:203
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:464
visibility get_visible() const
Definition: widget.cpp:506
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:311
unsigned get_width() const
Definition: widget.cpp:336
int get_y() const
Definition: widget.cpp:331
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:316
unsigned get_height() const
Definition: widget.cpp:341
const std::string & id() const
Definition: widget.cpp:110
window * get_window()
Get the parent window.
Definition: widget.cpp:117
@ invisible
The user set the widget invisible, that means:
rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:321
virtual void set_size(const point &size)
Sets the size of the widget.
Definition: widget.cpp:236
Helper class to block invalidate_layout.
Definition: window.hpp:235
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:761
std::size_t i
Definition: function.cpp:1029
int w
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define DBG_GUI_L
Definition: log.hpp:55
#define LOG_GUI_G
Definition: log.hpp:42
#define DBG_GUI_G
Definition: log.hpp:41
#define DBG_GUI_P
Definition: log.hpp:66
#define LOG_GUI_L
Definition: log.hpp:56
This file contains the window object, this object is a top level container which has the event manage...
#define LOG_HEADER
Definition: listbox.cpp:36
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
@ NOTIFY_MODIFIED
Definition: handler.hpp:158
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
static std::vector< widget_data > parse_list_data(const config &data, const unsigned int req_cols)
Definition: listbox.cpp:711
Generic file dialog.
std::array< generator_base::order_func, 2 > generator_sort_array
Definition: generator.hpp:380
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
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.
Contains the implementation details for lexical_cast and shouldn't be used directly.
int icompare(const std::string &s1, const std::string &s2)
Case-insensitive lexicographical comparison.
Definition: gettext.cpp:519
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
constexpr auto filter
Definition: ranges.hpp:38
constexpr std::underlying_type_t< Enum > to_underlying(Enum e) noexcept
Definition: general.hpp:40
std::string_view data
Definition: picture.cpp:178
Contains the SDL_Rect helper code.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
#define REGISTER_WIDGET3(type, id, key)
Registers a widget.
std::map< std::string, std::shared_ptr< builder_widget > > replacements_map
The replacements type is used to define replacement types.
std::vector< widget_data > list_data
Listbox data.
Definition: listbox.hpp:480
virtual std::unique_ptr< widget > build() const override
Inherited from builder_widget.
Definition: listbox.cpp:767
builder_listbox(const config &cfg)
Definition: listbox.cpp:774
std::string definition
Parameters for the styled_widget.
static bool less(const T &lhs, const T &rhs)
Definition: listbox.hpp:258
static bool more(const T &lhs, const T &rhs)
Definition: listbox.hpp:264
listbox_definition(const config &cfg)
Definition: listbox.cpp:689
std::vector< state_definition > state
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
bool overlaps(const SDL_Rect &r) const
Whether the given rectangle and this rectangle overlap.
Definition: rect.cpp:73
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
mock_char c
static map_location::direction s
std::string missing_mandatory_wml_tag(const std::string &section, const std::string &tag)
Returns a standard message for a missing wml child (tag).
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 (attribute).
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE_WML_CHILD(cfg, key, message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define h
#define f