The Battle for Wesnoth  1.19.8+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  widget& w = dynamic_cast<widget&>(*toggle);
574 
575  // If the toggle was hidden previously, show it
576  w.set_visible(widget::visibility::visible);
577 
578  // TODO: we can bind the pair directly if we remove the on-order callback
580  std::bind(&listbox::order_by_column, this, i, std::placeholders::_1));
581 }
582 
583 void listbox::order_by_column(unsigned column, widget& widget)
584 {
585  selectable_item& selectable = dynamic_cast<selectable_item&>(widget);
586 
587  for(auto& [w, _] : orders_) {
588  if(w && w != &selectable) {
589  w->set_value(utils::to_underlying(sort_order::type::none));
590  }
591  }
592 
593  auto order = sort_order::get_enum(selectable.get_value()).value_or(sort_order::type::none);
594  if(static_cast<unsigned>(order) > orders_[column].second.size()) {
595  return;
596  }
597 
598  if(order == sort_order::type::none) {
599  order_by(std::less<unsigned>());
600  } else {
601  order_by(orders_[column].second[utils::to_underlying(order) - 1]);
602  }
603 
604  if(callback_order_change_ != nullptr) {
605  callback_order_change_(column, order);
606  }
607 }
608 
610 {
611  generator_->set_order(func);
612 
613  update_layout();
614 }
615 
616 bool listbox::sort_helper::less(const t_string& lhs, const t_string& rhs)
617 {
618  return translation::icompare(lhs, rhs) < 0;
619 }
620 
621 bool listbox::sort_helper::more(const t_string& lhs, const t_string& rhs)
622 {
623  return translation::icompare(lhs, rhs) > 0;
624 }
625 
626 void listbox::set_active_sorter(std::string_view id, sort_order::type order, bool select_first)
627 {
628  for(auto& [w, _] : orders_) {
629  if(!w || dynamic_cast<widget*>(w)->id() != id) continue;
630 
631  // Set the state and fire a modified event to handle updating the list
632  w->set_value(utils::to_underlying(order), true);
633 
634  if(select_first && generator_->get_item_count() > 0) {
635  select_row_at(0);
636  }
637  }
638 }
639 
640 std::pair<widget*, sort_order::type> listbox::get_active_sorter() const
641 {
642  for(const auto& [w, _] : orders_) {
643  if(!w) continue;
644 
645  auto sort = sort_order::get_enum(w->get_value()).value_or(sort_order::type::none);
646  if(sort != sort_order::type::none) {
647  return { dynamic_cast<widget*>(w), sort };
648  }
649  }
650 
651  return { nullptr, sort_order::type::none };
652 }
653 
655 {
656  for(auto& [w, _] : orders_) {
657  if(w) {
658  w->set_value(utils::to_underlying(sort_order::type::none));
659  }
660  }
661 }
662 
663 void listbox::set_content_size(const point& origin, const point& size)
664 {
665  /** @todo This function needs more testing. */
666  assert(content_grid());
667 
668  const int best_height = content_grid()->get_best_size().y;
669  const point s(size.x, size.y < best_height ? size.y : best_height);
670 
671  content_grid()->place(origin, s);
672 }
673 
675 {
676  assert(content_grid());
677 
678  // If we haven't initialized, or have no content, just return.
680  if(size.x <= 0 || size.y <= 0) {
681  return;
682  }
683 
685 
686  const SDL_Rect& visible = content_visible_area_;
688 
689  queue_redraw();
690 }
691 
692 // }---------- DEFINITION ---------{
693 
696 {
697  DBG_GUI_P << "Parsing listbox " << id;
698 
699  load_resolutions<resolution>(cfg);
700 }
701 
703  : resolution_definition(cfg)
704  , grid(nullptr)
705 {
706  // Note the order should be the same as the enum state_t in listbox.hpp.
707  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_enabled")));
708  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_disabled")));
709 
710  auto child = VALIDATE_WML_CHILD(cfg, "grid", missing_mandatory_wml_tag("listbox_definition][resolution", "grid"));
711  grid = std::make_shared<builder_grid>(child);
712 }
713 
714 namespace implementation
715 {
716 static std::vector<widget_data> parse_list_data(const config& data, const unsigned int req_cols)
717 {
718  std::vector<widget_data> list_data;
719 
720  for(const auto& row : data.child_range("row")) {
721  auto cols = row.child_range("column");
722 
723  VALIDATE(static_cast<unsigned>(cols.size()) == req_cols,
724  _("‘list_data’ must have the same number of columns as the ‘list_definition’.")
725  );
726 
727  for(const auto& c : cols) {
728  list_data.emplace_back();
729 
730  for(const auto& [key, value] : c.attribute_range()) {
731  list_data.back()[""][key] = value;
732  }
733 
734  for(const auto& w : c.child_range("widget")) {
735  VALIDATE(w.has_attribute("id"), missing_mandatory_wml_key("[list_data][row][column][widget]", "id"));
736 
737  for(const auto& [key, value] : w.attribute_range()) {
738  list_data.back()[w["id"]][key] = value;
739  }
740  }
741  }
742  }
743 
744  return list_data;
745 }
746 
747 builder_listbox_base::builder_listbox_base(const config& cfg, const generator_base::placement placement)
749  , placement(placement)
750  , header(nullptr)
751  , footer(nullptr)
752  , list_builder(nullptr)
753  , list_data()
754  , has_minimum(cfg["has_minimum"].to_bool(true))
755  , has_maximum(cfg["has_maximum"].to_bool(true))
756  , allow_selection(cfg["allow_selection"].to_bool(true))
757 {
758  auto l = cfg.optional_child("list_definition");
759 
760  VALIDATE(l, _("No list defined."));
761 
762  list_builder = std::make_shared<builder_grid>(*l);
763  assert(list_builder);
764 
765  VALIDATE(list_builder->rows == 1, _("A ‘list_definition’ should contain one row."));
766 
767  if(cfg.has_child("list_data")) {
768  list_data = parse_list_data(cfg.mandatory_child("list_data"), list_builder->cols);
769  }
770 }
771 
772 std::unique_ptr<widget> builder_listbox_base::build() const
773 {
774  auto widget = std::make_unique<listbox>(*this);
775  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.";
776  return widget;
777 }
778 
780  : builder_listbox_base(cfg, generator_base::vertical_list)
781 {
782  if(auto h = cfg.optional_child("header")) {
783  header = std::make_shared<builder_grid>(*h);
784  }
785 
786  if(auto f = cfg.optional_child("footer")) {
787  footer = std::make_shared<builder_grid>(*f);
788  }
789 }
790 
791 } // namespace implementation
792 
793 // }------------ END --------------
794 
795 } // 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:362
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
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:380
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 indices.
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:674
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:654
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:583
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:609
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:626
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:663
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:640
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
@ visible
The user sets the widget visible, that means:
@ 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:234
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:1193
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:752
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:209
@ 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:716
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:772
builder_listbox(const config &cfg)
Definition: listbox.cpp:779
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:694
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:49
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