The Battle for Wesnoth  1.19.18+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("accessing invalid index " + std::to_string(row)
271  + " >= max " + std::to_string(get_item_count()) + " for listbox ‘" + id() + "’");
272  }
273 
274  unsigned int before = generator_->get_selected_item_count();
275  generator_->select_item(row, select);
276 
277  return before != generator_->get_selected_item_count();
278 }
279 
280 bool listbox::select_row_at(const unsigned row, const bool select)
281 {
282  return select_row(generator_->get_item_at_ordered(row), select);
283 }
284 
285 bool listbox::row_selected(const unsigned row)
286 {
287  return generator_->is_selected(row);
288 }
289 
291 {
292  return generator_->get_selected_item();
293 }
294 
296 {
297  /** @todo Hack to capture the keyboard focus. */
298  get_window()->keyboard_capture(this);
299 
300  for(std::size_t i = 0; i < generator_->get_item_count(); ++i) {
301  if(generator_->item(i).has_widget(caller)) {
302  toggle_button* checkbox = dynamic_cast<toggle_button*>(&caller);
303 
304  if(checkbox != nullptr) {
305  generator_->select_item(i, checkbox->get_value_bool());
306  } else {
308  }
309 
310  // TODO: enable this code once toggle_panel::set_value dispatches
311  // NOTIFY_MODIFED events. See comment in said function for more details.
312 #if 0
313  selectable_item& selectable = dynamic_cast<selectable_item&>(caller);
314 
315  generator_->select_item(i, selectable.get_value_bool());
316 #endif
317 
318  fire(event::NOTIFY_MODIFIED, *this, nullptr);
319  break;
320  }
321  }
322 
323  const int selected_item = generator_->get_selected_item();
324  if(selected_item == -1) {
325  return;
326  }
327 
328  const rect& visible = content_visible_area();
329  rect r = generator_->item(selected_item).get_rectangle();
330 
331  if(visible.overlaps(r)) {
332  r.x = visible.x;
333  r.w = visible.w;
334 
336  }
337 }
338 
339 void listbox::set_self_active(const bool /*active*/)
340 {
341  /* DO NOTHING */
342 }
343 
345 {
347  return true;
348  }
349 
350  if(get_size() == point()) {
351  return false;
352  }
353 
354  if(content_resize_request(true)) {
355  content_grid_->set_visible_rectangle(content_visible_area());
356  queue_redraw(); // TODO: draw_manager - does this get the right area?
357  return true;
358  }
359 
360  return false;
361 }
362 
363 void listbox::place(const point& origin, const point& size)
364 {
365  utils::optional<unsigned> vertical_scrollbar_position, horizontal_scrollbar_position;
366 
367  // Check if this is the first time placing the list box
368  if(get_origin() != point {-1, -1}) {
369  vertical_scrollbar_position = get_vertical_scrollbar_item_position();
370  horizontal_scrollbar_position = get_horizontal_scrollbar_item_position();
371  }
372 
373  // Inherited.
375 
376  const int selected_item = generator_->get_selected_item();
377  if(vertical_scrollbar_position && horizontal_scrollbar_position) {
378  LOG_GUI_L << LOG_HEADER << " restoring scroll position";
379 
380  set_vertical_scrollbar_item_position(*vertical_scrollbar_position);
381  set_horizontal_scrollbar_item_position(*horizontal_scrollbar_position);
382  } else if(selected_item != -1) {
383  LOG_GUI_L << LOG_HEADER << " making the initially selected item visible";
384 
385  const rect& visible = content_visible_area();
386  rect rect = generator_->item(selected_item).get_rectangle();
387 
388  rect.x = visible.x;
389  rect.w = visible.w;
390 
392  }
393 }
394 
395 void listbox::resize_content(const int width_modification,
396  const int height_modification,
397  const int width_modification_pos,
398  const int height_modification_pos)
399 {
400  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
401  << width_modification << " height_modification " << height_modification << ".";
402 
404  width_modification, height_modification, width_modification_pos, height_modification_pos))
405  {
406  // Calculate new size.
408  size.x += width_modification;
409  size.y += height_modification;
410 
411  // Set new size.
413  update_layout();
414 
415  // If the content grows assume it "overwrites" the old content.
416  if(width_modification < 0 || height_modification < 0) {
417  queue_redraw();
418  }
419 
420  DBG_GUI_L << LOG_HEADER << " succeeded.";
421  } else {
422  DBG_GUI_L << LOG_HEADER << " failed.";
423  }
424 }
425 
427 {
428  if(row.get_visible() == visibility::invisible) {
429  return;
430  }
431 
432  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " row size " << row.get_best_size()
433  << ".";
434 
435  const point content = content_grid()->get_size();
436  point size = row.get_best_size();
437 
438  if(size.x < content.x) {
439  size.x = 0;
440  } else {
441  size.x -= content.x;
442  }
443 
444  resize_content(size.x, size.y);
445 }
446 
448 {
449  // Get the size from the base class, then add any extra space for the header and footer.
451 
452  if(const grid* header = get_grid().find_widget<const grid>("_header_grid", false, false)) {
453  if(header->get_visible() != widget::visibility::invisible) {
454  result.y += header->get_best_size().y;
455  }
456  }
457 
458  if(const grid* footer = get_grid().find_widget<const grid>("_footer_grid", false, false)) {
459  if(footer->get_visible() != widget::visibility::invisible) {
460  result.y += footer->get_best_size().y;
461  }
462  }
463 
464  return result;
465 }
466 
468 {
469  const rect& visible = content_visible_area();
471 
472  // When scrolling make sure the new items are visible...
473  if(direction == KEY_VERTICAL) {
474  // ...but leave the horizontal scrollbar position.
475  rect.x = visible.x;
476  rect.w = visible.w;
477  } else {
478  // ...but leave the vertical scrollbar position.
479  rect.y = visible.y;
480  rect.h = visible.h;
481  }
482 
484 
485  fire(event::NOTIFY_MODIFIED, *this, nullptr);
486 }
487 
488 void listbox::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
489 {
490  generator_->handle_key_up_arrow(modifier, handled);
491 
492  if(handled) {
494  } else {
495  // Inherited.
496  scrollbar_container::handle_key_up_arrow(modifier, handled);
497  }
498 }
499 
500 void listbox::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
501 {
502  generator_->handle_key_down_arrow(modifier, handled);
503 
504  if(handled) {
506  } else {
507  // Inherited.
508  scrollbar_container::handle_key_up_arrow(modifier, handled);
509  }
510 }
511 
512 void listbox::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
513 {
514  generator_->handle_key_left_arrow(modifier, handled);
515 
516  // Inherited.
517  if(handled) {
519  } else {
521  }
522 }
523 
524 void listbox::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
525 {
526  generator_->handle_key_right_arrow(modifier, handled);
527 
528  // Inherited.
529  if(handled) {
531  } else {
533  }
534 }
535 
536 void listbox::initialize_sorter(std::string_view id, generator_sort_array&& array)
537 {
538  auto header = find_widget<grid>("_header_grid", false, false);
539  if(!header) return;
540 
541  auto toggle = header->find_widget<selectable_item>(id, false, false);
542  if(!toggle) return;
543 
544  const std::size_t i = orders_.size();
545  orders_.emplace_back(toggle, std::move(array));
546 
547  widget& w = dynamic_cast<widget&>(*toggle);
548 
549  // If the toggle was hidden previously, show it
550  w.set_visible(widget::visibility::visible);
551 
552  // TODO: we can bind the pair directly if we remove the on-order callback
554  std::bind(&listbox::order_by_column, this, i, std::placeholders::_1));
555 }
556 
557 void listbox::order_by_column(unsigned column, widget& widget)
558 {
559  selectable_item& selectable = dynamic_cast<selectable_item&>(widget);
560 
561  for(auto& [w, _] : orders_) {
562  if(w && w != &selectable) {
563  w->set_value(utils::to_underlying(sort_order::type::none));
564  }
565  }
566 
567  auto order = sort_order::get_enum(selectable.get_value()).value_or(sort_order::type::none);
568  if(static_cast<unsigned>(order) > orders_[column].second.size()) {
569  return;
570  }
571 
572  if(order == sort_order::type::none) {
573  order_by(std::less<unsigned>());
574  } else {
575  order_by(orders_[column].second[utils::to_underlying(order) - 1]);
576  }
577 
578  if(callback_order_change_ != nullptr) {
579  callback_order_change_(column, order);
580  }
581 }
582 
584 {
585  generator_->set_order(func);
586  update_layout();
587 }
588 
589 bool listbox::sort_helper::less(const t_string& lhs, const t_string& rhs)
590 {
591  return translation::icompare(lhs, rhs) < 0;
592 }
593 
594 bool listbox::sort_helper::more(const t_string& lhs, const t_string& rhs)
595 {
596  return translation::icompare(lhs, rhs) > 0;
597 }
598 
599 void listbox::set_active_sorter(std::string_view id, sort_order::type order, bool select_first)
600 {
601  for(auto& [w, _] : orders_) {
602  if(!w || dynamic_cast<widget*>(w)->id() != id) continue;
603 
604  // Set the state and fire a modified event to handle updating the list
605  w->set_value(utils::to_underlying(order), true);
606 
607  if(select_first && generator_->get_item_count() > 0) {
608  select_row_at(0);
609  }
610  }
611 }
612 
613 std::pair<widget*, sort_order::type> listbox::get_active_sorter() const
614 {
615  for(const auto& [w, _] : orders_) {
616  if(!w) continue;
617 
618  auto sort = sort_order::get_enum(w->get_value()).value_or(sort_order::type::none);
619  if(sort != sort_order::type::none) {
620  return { dynamic_cast<widget*>(w), sort };
621  }
622  }
623 
624  return { nullptr, sort_order::type::none };
625 }
626 
628 {
629  for(auto& [w, _] : orders_) {
630  if(w) {
631  w->set_value(utils::to_underlying(sort_order::type::none));
632  }
633  }
634 }
635 
636 void listbox::set_content_size(const point& origin, const point& size)
637 {
638  /** @todo This function needs more testing. */
639  assert(content_grid());
640 
641  const int best_height = content_grid()->get_best_size().y;
642  const point s(size.x, size.y < best_height ? size.y : best_height);
643 
644  content_grid()->place(origin, s);
645 }
646 
648 {
649  assert(content_grid());
650 
651  // If we haven't initialized, or have no content, just return.
653  if(size.x <= 0 || size.y <= 0) {
654  return;
655  }
656 
658 
659  const rect& visible = content_visible_area_;
661 
662  queue_redraw();
663 }
664 
665 // }---------- DEFINITION ---------{
666 
669 {
670  DBG_GUI_P << "Parsing listbox " << id;
671  load_resolutions<resolution>(cfg);
672 }
673 
676  , grid(nullptr)
677 {
678  // Note the order should be the same as the enum state_t in listbox.hpp.
679  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_enabled")));
680  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("listbox_definition][resolution", "state_disabled")));
681 
682  auto child = VALIDATE_WML_CHILD(cfg, "grid", missing_mandatory_wml_tag("listbox_definition][resolution", "grid"));
683  grid = std::make_shared<builder_grid>(child);
684 }
685 
686 namespace implementation
687 {
688 static std::vector<widget_data> parse_list_data(const config& data, const unsigned int req_cols)
689 {
690  std::vector<widget_data> list_data;
691 
692  for(const auto& row : data.child_range("row")) {
693  auto cols = row.child_range("column");
694 
695  VALIDATE(static_cast<unsigned>(cols.size()) == req_cols,
696  _("‘list_data’ must have the same number of columns as the ‘list_definition’.")
697  );
698 
699  for(const auto& c : cols) {
700  list_data.emplace_back();
701 
702  for(const auto& [key, value] : c.attribute_range()) {
703  list_data.back()[""][key] = value.t_str();
704  }
705 
706  for(const auto& w : c.child_range("widget")) {
707  VALIDATE(w.has_attribute("id"), missing_mandatory_wml_key("[list_data][row][column][widget]", "id"));
708 
709  for(const auto& [key, value] : w.attribute_range()) {
710  list_data.back()[w["id"]][key] = value.t_str();
711  }
712  }
713  }
714  }
715 
716  return list_data;
717 }
718 
719 builder_listbox_base::builder_listbox_base(const config& cfg, const generator_base::placement placement)
721  , placement(placement)
722  , header(nullptr)
723  , footer(nullptr)
724  , list_builder(nullptr)
725  , list_data()
726  , has_minimum(cfg["has_minimum"].to_bool(true))
727  , has_maximum(cfg["has_maximum"].to_bool(true))
728  , allow_selection(cfg["allow_selection"].to_bool(true))
729 {
730  auto l = cfg.optional_child("list_definition");
731  VALIDATE(l, _("No list defined."));
732 
733  list_builder = std::make_shared<builder_grid>(*l);
734  assert(list_builder);
735 
736  VALIDATE(list_builder->rows == 1, _("A ‘list_definition’ should contain one row."));
737 
738  if(cfg.has_child("list_data")) {
740  }
741 }
742 
743 std::unique_ptr<widget> builder_listbox_base::build() const
744 {
745  auto widget = std::make_unique<listbox>(*this);
746  DBG_GUI_G << "Window builder: placed listbox '" << id << "' with definition '" << definition << "'.";
747  return widget;
748 }
749 
751  : builder_listbox_base(cfg, generator_base::vertical_list)
752 {
753  if(auto h = cfg.optional_child("header")) {
754  header = std::make_shared<builder_grid>(*h);
755  }
756 
757  if(auto f = cfg.optional_child("footer")) {
758  footer = std::make_shared<builder_grid>(*f);
759  }
760 }
761 
762 } // namespace implementation
763 
764 // }------------ END --------------
765 
766 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:380
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
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:647
void list_item_clicked(widget &caller)
Function to call after the user clicked on a row.
Definition: listbox.cpp:295
void mark_as_unsorted()
Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted.
Definition: listbox.cpp:627
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: listbox.cpp:447
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:524
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:512
void order_by_column(unsigned column, widget &widget)
Definition: listbox.cpp:557
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:395
void order_by(const generator_base::order_func &func)
Definition: listbox.cpp:583
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: listbox.cpp:339
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:599
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:636
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: listbox.cpp:363
void update_visible_area_on_key_event(const KEY_SCROLL_DIRECTION direction)
Helper to update visible area after a key event.
Definition: listbox.cpp:467
void initialize_sorter(std::string_view id, generator_sort_array &&)
Implementation detail of set_single_sorter.
Definition: listbox.cpp:536
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:344
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:500
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:613
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:290
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:280
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:285
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:488
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:232
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:59
void keyboard_capture(widget *widget)
Definition: window.cpp:1197
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:759
const config * cfg
std::size_t i
Definition: function.cpp:1032
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:688
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:81
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:41
int w
Definition: pathfind.cpp:188
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:743
builder_listbox(const config &cfg)
Definition: listbox.cpp:750
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:667
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