The Battle for Wesnoth  1.19.13+dev
listbox.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
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  grid& row = generator_->create_item(index, *list_builder_, item, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
95 
96  resize_content(row);
97  return row;
98 }
99 
101 {
102  grid& row = generator_->create_item(index, *list_builder_, data, std::bind(&listbox::list_item_clicked, this, std::placeholders::_1));
103 
104  resize_content(row);
105  return row;
106 }
107 
108 void listbox::remove_row(const unsigned row, unsigned count)
109 {
110  if(row >= get_item_count()) {
111  return;
112  }
113 
114  if(!count || count + row > get_item_count()) {
115  count = get_item_count() - row;
116  }
117 
118  int height_reduced = 0;
119  int width_reduced = 0;
120 
121  const bool is_horizontal = placement_ == generator_base::horizontal_list;
122 
123  // TODO: Fix this for horizontal listboxes
124  // Note the we have to use content_grid_ and cannot use "_list_grid" which is what generator_ uses.
125  int row_pos_y = is_horizontal ? -1 : generator_->item(row).get_y() - content_grid_->get_y();
126  int row_pos_x = is_horizontal ? -1 : 0;
127 
128  for(; count; --count) {
130  if(is_horizontal) {
131  width_reduced += generator_->item(row).get_width();
132  } else {
133  height_reduced += generator_->item(row).get_height();
134  }
135  }
136 
137  generator_->delete_item(row);
138  }
139 
140  if((height_reduced != 0 || width_reduced != 0) && get_item_count() != 0) {
141  resize_content(-width_reduced, -height_reduced, row_pos_x, row_pos_y);
142  } else {
144  }
145 }
146 
148 {
149  generator_->clear();
151 }
152 
153 unsigned listbox::get_item_count() const
154 {
155  return generator_->get_item_count();
156 }
157 
158 void listbox::set_row_active(const unsigned row, const bool active)
159 {
160  generator_->item(row).set_active(active);
161 }
162 
163 void listbox::set_row_shown(const unsigned row, const bool shown)
164 {
165  window* window = get_window();
166  assert(window);
167 
168  const int selected_row = get_selected_row();
169 
170  bool resize_needed = false;
171 
172  // Local scope for invalidate_layout_blocker
173  {
174  window::invalidate_layout_blocker invalidate_layout_blocker(*window);
175 
176  generator_->set_item_shown(row, shown);
177 
178  point best_size = generator_->calculate_best_size();
179  generator_->place(generator_->get_origin(), {std::max(best_size.x, content_visible_area().w), best_size.y});
180 
181  resize_needed = !content_resize_request();
182  }
183 
184  if(resize_needed) {
186  } else {
187  content_grid_->set_visible_rectangle(content_visible_area());
188  queue_redraw(); // TODO: draw_manager - does this get the right area?
189  }
190 
191  if(selected_row != get_selected_row()) {
192  fire(event::NOTIFY_MODIFIED, *this, nullptr);
193  }
194 }
195 
196 void listbox::set_row_shown(const boost::dynamic_bitset<>& shown)
197 {
198  assert(shown.size() == get_item_count());
199 
200  if(generator_->get_items_shown() == shown) {
201  LOG_GUI_G << LOG_HEADER << " returning early";
202  return;
203  }
204 
205  window* window = get_window();
206  assert(window);
207 
208  const int selected_row = get_selected_row();
209 
210  bool resize_needed = false;
211 
212  // Local scope for invalidate_layout_blocker
213  {
214  window::invalidate_layout_blocker invalidate_layout_blocker(*window);
215 
216  for(std::size_t i = 0; i < shown.size(); ++i) {
217  generator_->set_item_shown(i, shown[i]);
218  }
219 
220  point best_size = generator_->calculate_best_size();
221  generator_->place(generator_->get_origin(), {std::max(best_size.x, content_visible_area().w), best_size.y});
222 
223  resize_needed = !content_resize_request(true);
224  }
225 
226  if(resize_needed) {
228  } else {
229  content_grid_->set_visible_rectangle(content_visible_area());
230  queue_redraw(); // TODO: draw_manager - does this get the right area?
231  }
232 
233  if(selected_row != get_selected_row()) {
234  fire(event::NOTIFY_MODIFIED, *this, nullptr);
235  }
236 }
237 
238 std::size_t listbox::filter_rows_by(const std::function<bool(std::size_t)>& filter)
239 {
240  boost::dynamic_bitset<> mask;
241  mask.resize(get_item_count(), true);
242 
243  for(std::size_t i = 0; i < mask.size(); ++i) {
244  mask[i] = std::invoke(filter, i);
245  }
246 
247  set_row_shown(mask);
248  return mask.count();
249 }
250 
251 boost::dynamic_bitset<> listbox::get_rows_shown() const
252 {
253  return generator_->get_items_shown();
254 }
255 
256 const grid* listbox::get_row_grid(const unsigned row) const
257 {
258  // rename this function and can we return a reference??
259  return &generator_->item(row);
260 }
261 
262 grid* listbox::get_row_grid(const unsigned row)
263 {
264  return &generator_->item(row);
265 }
266 
267 bool listbox::select_row(const unsigned row, const bool select)
268 {
269  if(row >= get_item_count()) {
270  throw std::invalid_argument("invalid listbox index");
271  }
272 
273  unsigned int before = generator_->get_selected_item_count();
274  generator_->select_item(row, select);
275 
276  return before != generator_->get_selected_item_count();
277 }
278 
279 bool listbox::select_row_at(const unsigned row, const bool select)
280 {
281  return select_row(generator_->get_item_at_ordered(row), select);
282 }
283 
284 bool listbox::row_selected(const unsigned row)
285 {
286  return generator_->is_selected(row);
287 }
288 
290 {
291  return generator_->get_selected_item();
292 }
293 
295 {
296  /** @todo Hack to capture the keyboard focus. */
297  get_window()->keyboard_capture(this);
298 
299  for(std::size_t i = 0; i < generator_->get_item_count(); ++i) {
300  if(generator_->item(i).has_widget(caller)) {
301  toggle_button* checkbox = dynamic_cast<toggle_button*>(&caller);
302 
303  if(checkbox != nullptr) {
304  generator_->select_item(i, checkbox->get_value_bool());
305  } else {
307  }
308 
309  // TODO: enable this code once toggle_panel::set_value dispatches
310  // NOTIFY_MODIFED events. See comment in said function for more details.
311 #if 0
312  selectable_item& selectable = dynamic_cast<selectable_item&>(caller);
313 
314  generator_->select_item(i, selectable.get_value_bool());
315 #endif
316 
317  fire(event::NOTIFY_MODIFIED, *this, nullptr);
318  break;
319  }
320  }
321 
322  const int selected_item = generator_->get_selected_item();
323  if(selected_item == -1) {
324  return;
325  }
326 
327  const rect& visible = content_visible_area();
328  rect r = generator_->item(selected_item).get_rectangle();
329 
330  if(visible.overlaps(r)) {
331  r.x = visible.x;
332  r.w = visible.w;
333 
335  }
336 }
337 
338 void listbox::set_self_active(const bool /*active*/)
339 {
340  /* DO NOTHING */
341 }
342 
344 {
346  return true;
347  }
348 
349  if(get_size() == point()) {
350  return false;
351  }
352 
353  if(content_resize_request(true)) {
354  content_grid_->set_visible_rectangle(content_visible_area());
355  queue_redraw(); // TODO: draw_manager - does this get the right area?
356  return true;
357  }
358 
359  return false;
360 }
361 
362 void listbox::place(const point& origin, const point& size)
363 {
364  utils::optional<unsigned> vertical_scrollbar_position, horizontal_scrollbar_position;
365 
366  // Check if this is the first time placing the list box
367  if(get_origin() != point {-1, -1}) {
368  vertical_scrollbar_position = get_vertical_scrollbar_item_position();
369  horizontal_scrollbar_position = get_horizontal_scrollbar_item_position();
370  }
371 
372  // Inherited.
374 
375  const int selected_item = generator_->get_selected_item();
376  if(vertical_scrollbar_position && horizontal_scrollbar_position) {
377  LOG_GUI_L << LOG_HEADER << " restoring scroll position";
378 
379  set_vertical_scrollbar_item_position(*vertical_scrollbar_position);
380  set_horizontal_scrollbar_item_position(*horizontal_scrollbar_position);
381  } else if(selected_item != -1) {
382  LOG_GUI_L << LOG_HEADER << " making the initially selected item visible";
383 
384  const rect& visible = content_visible_area();
385  rect rect = generator_->item(selected_item).get_rectangle();
386 
387  rect.x = visible.x;
388  rect.w = visible.w;
389 
391  }
392 }
393 
394 void listbox::resize_content(const int width_modification,
395  const int height_modification,
396  const int width_modification_pos,
397  const int height_modification_pos)
398 {
399  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
400  << width_modification << " height_modification " << height_modification << ".";
401 
403  width_modification, height_modification, width_modification_pos, height_modification_pos))
404  {
405  // Calculate new size.
407  size.x += width_modification;
408  size.y += height_modification;
409 
410  // Set new size.
412  update_layout();
413 
414  // If the content grows assume it "overwrites" the old content.
415  if(width_modification < 0 || height_modification < 0) {
416  queue_redraw();
417  }
418 
419  DBG_GUI_L << LOG_HEADER << " succeeded.";
420  } else {
421  DBG_GUI_L << LOG_HEADER << " failed.";
422  }
423 }
424 
426 {
427  if(row.get_visible() == visibility::invisible) {
428  return;
429  }
430 
431  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " row size " << row.get_best_size()
432  << ".";
433 
434  const point content = content_grid()->get_size();
435  point size = row.get_best_size();
436 
437  if(size.x < content.x) {
438  size.x = 0;
439  } else {
440  size.x -= content.x;
441  }
442 
443  resize_content(size.x, size.y);
444 }
445 
447 {
448  // Get the size from the base class, then add any extra space for the header and footer.
450 
451  if(const grid* header = get_grid().find_widget<const grid>("_header_grid", false, false)) {
452  if(header->get_visible() != widget::visibility::invisible) {
453  result.y += header->get_best_size().y;
454  }
455  }
456 
457  if(const grid* footer = get_grid().find_widget<const grid>("_footer_grid", false, false)) {
458  if(footer->get_visible() != widget::visibility::invisible) {
459  result.y += footer->get_best_size().y;
460  }
461  }
462 
463  return result;
464 }
465 
467 {
468  const rect& visible = content_visible_area();
470 
471  // When scrolling make sure the new items are visible...
472  if(direction == KEY_VERTICAL) {
473  // ...but leave the horizontal scrollbar position.
474  rect.x = visible.x;
475  rect.w = visible.w;
476  } else {
477  // ...but leave the vertical scrollbar position.
478  rect.y = visible.y;
479  rect.h = visible.h;
480  }
481 
483 
484  fire(event::NOTIFY_MODIFIED, *this, nullptr);
485 }
486 
487 void listbox::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
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  generator_->handle_key_down_arrow(modifier, handled);
502 
503  if(handled) {
505  } else {
506  // Inherited.
507  scrollbar_container::handle_key_up_arrow(modifier, handled);
508  }
509 }
510 
511 void listbox::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
512 {
513  generator_->handle_key_left_arrow(modifier, handled);
514 
515  // Inherited.
516  if(handled) {
518  } else {
520  }
521 }
522 
523 void listbox::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
524 {
525  generator_->handle_key_right_arrow(modifier, handled);
526 
527  // Inherited.
528  if(handled) {
530  } else {
532  }
533 }
534 
535 void listbox::initialize_sorter(std::string_view id, generator_sort_array&& array)
536 {
537  auto header = find_widget<grid>("_header_grid", false, false);
538  if(!header) return;
539 
540  auto toggle = header->find_widget<selectable_item>(id, false, false);
541  if(!toggle) return;
542 
543  const std::size_t i = orders_.size();
544  orders_.emplace_back(toggle, std::move(array));
545 
546  widget& w = dynamic_cast<widget&>(*toggle);
547 
548  // If the toggle was hidden previously, show it
549  w.set_visible(widget::visibility::visible);
550 
551  // TODO: we can bind the pair directly if we remove the on-order callback
553  std::bind(&listbox::order_by_column, this, i, std::placeholders::_1));
554 }
555 
556 void listbox::order_by_column(unsigned column, widget& widget)
557 {
558  selectable_item& selectable = dynamic_cast<selectable_item&>(widget);
559 
560  for(auto& [w, _] : orders_) {
561  if(w && w != &selectable) {
562  w->set_value(utils::to_underlying(sort_order::type::none));
563  }
564  }
565 
566  auto order = sort_order::get_enum(selectable.get_value()).value_or(sort_order::type::none);
567  if(static_cast<unsigned>(order) > orders_[column].second.size()) {
568  return;
569  }
570 
571  if(order == sort_order::type::none) {
572  order_by(std::less<unsigned>());
573  } else {
574  order_by(orders_[column].second[utils::to_underlying(order) - 1]);
575  }
576 
577  if(callback_order_change_ != nullptr) {
578  callback_order_change_(column, order);
579  }
580 }
581 
583 {
584  generator_->set_order(func);
585  update_layout();
586 }
587 
588 bool listbox::sort_helper::less(const t_string& lhs, const t_string& rhs)
589 {
590  return translation::icompare(lhs, rhs) < 0;
591 }
592 
593 bool listbox::sort_helper::more(const t_string& lhs, const t_string& rhs)
594 {
595  return translation::icompare(lhs, rhs) > 0;
596 }
597 
598 void listbox::set_active_sorter(std::string_view id, sort_order::type order, bool select_first)
599 {
600  for(auto& [w, _] : orders_) {
601  if(!w || dynamic_cast<widget*>(w)->id() != id) continue;
602 
603  // Set the state and fire a modified event to handle updating the list
604  w->set_value(utils::to_underlying(order), true);
605 
606  if(select_first && generator_->get_item_count() > 0) {
607  select_row_at(0);
608  }
609  }
610 }
611 
612 std::pair<widget*, sort_order::type> listbox::get_active_sorter() const
613 {
614  for(const auto& [w, _] : orders_) {
615  if(!w) continue;
616 
617  auto sort = sort_order::get_enum(w->get_value()).value_or(sort_order::type::none);
618  if(sort != sort_order::type::none) {
619  return { dynamic_cast<widget*>(w), sort };
620  }
621  }
622 
623  return { nullptr, sort_order::type::none };
624 }
625 
627 {
628  for(auto& [w, _] : orders_) {
629  if(w) {
630  w->set_value(utils::to_underlying(sort_order::type::none));
631  }
632  }
633 }
634 
635 void listbox::set_content_size(const point& origin, const point& size)
636 {
637  /** @todo This function needs more testing. */
638  assert(content_grid());
639 
640  const int best_height = content_grid()->get_best_size().y;
641  const point s(size.x, size.y < best_height ? size.y : best_height);
642 
643  content_grid()->place(origin, s);
644 }
645 
647 {
648  assert(content_grid());
649 
650  // If we haven't initialized, or have no content, just return.
652  if(size.x <= 0 || size.y <= 0) {
653  return;
654  }
655 
657 
658  const rect& visible = content_visible_area_;
660 
661  queue_redraw();
662 }
663 
664 // }---------- DEFINITION ---------{
665 
668 {
669  DBG_GUI_P << "Parsing listbox " << id;
670  load_resolutions<resolution>(cfg);
671 }
672 
674  : resolution_definition(cfg)
675  , grid(nullptr)
676 {
677  // Note the order should be the same as the enum state_t in listbox.hpp.
678  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_enabled")));
679  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_disabled")));
680 
681  auto child = VALIDATE_WML_CHILD(cfg, "grid", missing_mandatory_wml_tag("listbox_definition][resolution", "grid"));
682  grid = std::make_shared<builder_grid>(child);
683 }
684 
685 namespace implementation
686 {
687 static std::vector<widget_data> parse_list_data(const config& data, const unsigned int req_cols)
688 {
689  std::vector<widget_data> list_data;
690 
691  for(const auto& row : data.child_range("row")) {
692  auto cols = row.child_range("column");
693 
694  VALIDATE(static_cast<unsigned>(cols.size()) == req_cols,
695  _("‘list_data’ must have the same number of columns as the ‘list_definition’.")
696  );
697 
698  for(const auto& c : cols) {
699  list_data.emplace_back();
700 
701  for(const auto& [key, value] : c.attribute_range()) {
702  list_data.back()[""][key] = value;
703  }
704 
705  for(const auto& w : c.child_range("widget")) {
706  VALIDATE(w.has_attribute("id"), missing_mandatory_wml_key("[list_data][row][column][widget]", "id"));
707 
708  for(const auto& [key, value] : w.attribute_range()) {
709  list_data.back()[w["id"]][key] = value;
710  }
711  }
712  }
713  }
714 
715  return list_data;
716 }
717 
718 builder_listbox_base::builder_listbox_base(const config& cfg, const generator_base::placement placement)
720  , placement(placement)
721  , header(nullptr)
722  , footer(nullptr)
723  , list_builder(nullptr)
724  , list_data()
725  , has_minimum(cfg["has_minimum"].to_bool(true))
726  , has_maximum(cfg["has_maximum"].to_bool(true))
727  , allow_selection(cfg["allow_selection"].to_bool(true))
728 {
729  auto l = cfg.optional_child("list_definition");
730  VALIDATE(l, _("No list defined."));
731 
732  list_builder = std::make_shared<builder_grid>(*l);
733  assert(list_builder);
734 
735  VALIDATE(list_builder->rows == 1, _("A ‘list_definition’ should contain one row."));
736 
737  if(cfg.has_child("list_data")) {
738  list_data = parse_list_data(cfg.mandatory_child("list_data"), list_builder->cols);
739  }
740 }
741 
742 std::unique_ptr<widget> builder_listbox_base::build() const
743 {
744  auto widget = std::make_unique<listbox>(*this);
745  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.";
746  return widget;
747 }
748 
750  : builder_listbox_base(cfg, generator_base::vertical_list)
751 {
752  if(auto h = cfg.optional_child("header")) {
753  header = std::make_shared<builder_grid>(*h);
754  }
755 
756  if(auto f = cfg.optional_child("footer")) {
757  footer = std::make_shared<builder_grid>(*f);
758  }
759 }
760 
761 } // namespace implementation
762 
763 // }------------ END --------------
764 
765 } // 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 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:646
void list_item_clicked(widget &caller)
Function to call after the user clicked on a row.
Definition: listbox.cpp:294
void mark_as_unsorted()
Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted.
Definition: listbox.cpp:626
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: listbox.cpp:446
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:523
void set_row_active(const unsigned row, const bool active)
Makes a row active or inactive.
Definition: listbox.cpp:158
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:163
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:511
void order_by_column(unsigned column, widget &widget)
Definition: listbox.cpp:556
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:394
void order_by(const generator_base::order_func &func)
Definition: listbox.cpp:582
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: listbox.cpp:338
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:256
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:598
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:635
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: listbox.cpp:362
void update_visible_area_on_key_event(const KEY_SCROLL_DIRECTION direction)
Helper to update visible area after a key event.
Definition: listbox.cpp:466
void initialize_sorter(std::string_view id, generator_sort_array &&)
Implementation detail of set_single_sorter.
Definition: listbox.cpp:535
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:267
bool update_content_size()
Request to update the size of the content after changing the content.
Definition: listbox.cpp:343
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition: listbox.cpp:251
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:238
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:499
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:612
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:108
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:147
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:289
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:153
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:279
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:284
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:487
Base class for creating containers with one or two scrollbar(s).
void set_horizontal_scrollbar_item_position(const unsigned position)
Move the horizontal scrollbar to a position.
void show_content_rect(const rect &rect)
Shows a certain part of the content.
const rect & content_visible_area() const
rect content_visible_area_
Cache for the visible area for the content.
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.
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:1201
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:760
std::size_t i
Definition: function.cpp:1032
int w
static std::string _(const char *str)
Definition: gettext.hpp:97
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:214
@ NOTIFY_MODIFIED
Definition: handler.hpp:175
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:189
static std::vector< widget_data > parse_list_data(const config &data, const unsigned int req_cols)
Definition: listbox.cpp:687
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:188
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:742
builder_listbox(const config &cfg)
Definition: listbox.cpp:749
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:666
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 rect &r) const
Whether the given rectangle and this rectangle overlap.
Definition: rect.cpp:74
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