The Battle for Wesnoth  1.19.7+dev
listbox.hpp
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 #pragma once
17 
20 
23 
25 
26 #include <boost/dynamic_bitset.hpp>
27 #include <functional>
28 
29 namespace gui2
30 {
31 // ------------ WIDGET -----------{
32 
33 class selectable_item;
34 namespace implementation
35 {
36 struct builder_listbox_base;
37 }
38 
39 /** The listbox class. */
41 {
43 
44  friend class debug_layout_graph;
45 
46 public:
47  /**
48  * Constructor.
49  *
50  * @param builder The builder for the appropriate listbox variant.
51  */
53 
54  /***** ***** ***** ***** Row handling. ***** ***** ****** *****/
55 
56  /**
57  * When an item in the list is selected by the user we need to
58  * update the state. We installed a callback handler which
59  * calls us.
60  *
61  * @param item The data send to the set_members of the
62  * widgets.
63  * @param index The item before which to add the new item,
64  * 0 == begin, -1 == end.
65  */
66  grid& add_row(const widget_item& item, const int index = -1);
67 
68  /**
69  * Adds single row to the grid.
70  *
71  * This function expect a row to have multiple widgets (either multiple
72  * columns or one column with multiple widgets).
73  *
74  *
75  * @param data The data to send to the set_members of the
76  * widgets. If the member id is not an empty
77  * string it is only send to the widget that has
78  * the wanted id (if any). If the member id is an
79  * empty string, it is send to all members.
80  * Having both empty and non-empty id's gives
81  * undefined behavior.
82  * @param index The item before which to add the new item,
83  * 0 == begin, -1 == end.
84  */
85  grid& add_row(const widget_data& data, const int index = -1);
86 
87  /**
88  * Removes a row in the listbox.
89  *
90  * @param row The row to remove, when not in
91  * range the function is ignored.
92  * @param count The number of rows to remove, 0 means all
93  * rows (starting from row).
94  */
95  void remove_row(const unsigned row, unsigned count = 1);
96 
97  /** Removes all the rows in the listbox, clearing it. */
98  void clear();
99 
100  /** Returns the number of items in the listbox. */
101  unsigned get_item_count() const;
102 
103  /**
104  * Makes a row active or inactive.
105  *
106  * NOTE this doesn't change the select status of the row.
107  *
108  * @param row The row to (de)activate.
109  * @param active true activate, false deactivate.
110  */
111  void set_row_active(const unsigned row, const bool active);
112 
113  /**
114  * Makes a row visible or invisible.
115  *
116  * @param row The row to show or hide.
117  * @param shown true visible, false invisible.
118  */
119  void set_row_shown(const unsigned row, const bool shown);
120 
121  /**
122  * Makes a row visible or invisible.
123  *
124  * Use this version if you want to show hide multiple items since it's
125  * optimized for that purpose, for one it calls the selection changed
126  * callback only once instead of several times.
127  *
128  * @param shown A vector with the show hide status for every
129  * row. The number of items in the vector must
130  * be equal to the number of items in the
131  * listbox.
132  */
133  void set_row_shown(const boost::dynamic_bitset<>& shown);
134 
135  /**
136  * Hides all rows for which the given predicate returns false.
137  *
138  * @returns The number of rows now visible.
139  */
140  std::size_t filter_rows_by(const std::function<bool(std::size_t)>& filter);
141 
142  /**
143  * Returns a list of visible rows
144  *
145  * @returns A mask indicating which rows are visible
146  */
147  boost::dynamic_bitset<> get_rows_shown() const;
148 
149  /**
150  * Returns the grid of the wanted row.
151  *
152  * There's only a const version since allowing callers to modify the grid
153  * behind our backs might give problems. We return a pointer instead of a
154  * reference since dynamic casting of pointers is easier (no try catch
155  * needed).
156  *
157  * @param row The row to get the grid from, the caller has
158  * to make sure the row is a valid row.
159  * @returns The grid of the wanted row.
160  */
161  const grid* get_row_grid(const unsigned row) const;
162 
163  /**
164  * The possibly-giving-problems nonconst version of get_row_grid
165  *
166  * @param row The row to get the grid from, the caller has
167  * to make sure the row is a valid row.
168  * @returns The grid of the wanted row.
169  */
170  grid* get_row_grid(const unsigned row);
171 
172  /**
173  * Selects a row.
174  *
175  * @param row The row to select.
176  * @param select Select or deselect the row.
177  * @returns True if the operation succeeded.
178  */
179  bool select_row(const unsigned row, const bool select = true);
180 
181  /**
182  * Does exactly as advertised: selects the list's last row.
183  *
184  * @param select Select or deselect the row.
185  */
186  bool select_last_row(const bool select = true)
187  {
188  return select_row(get_item_count() - 1, select);
189  }
190 
191  /**
192  * Selects a row at the given position, regardless of sorting order.
193  *
194  * When using @ref select_row the relevant row is located by index regardless
195  * of its actual position in the list, which could differ if the list had been
196  * sorted. In that case, `select_row(0)` would not select the list's first row
197  * as displayed.
198  *
199  * This function allows row selection based on position. `select_row_at(0)` will
200  * always select the list's first row, regardless of sorting order.
201  *
202  * @param row The row to select.
203  * @param select Select or deselect the row.
204  *
205  * @returns True if the operation succeeded.
206  */
207  bool select_row_at(const unsigned row, const bool select = true);
208 
209  /**
210  * Check if a row is selected
211  * @param row The row to test
212  * @returns True if it is selected.
213  */
214  bool row_selected(const unsigned row);
215 
216  /**
217  * Returns the first selected row
218  *
219  * @returns The first selected row, or -1 if no row is selected.
220  */
221  int get_selected_row() const;
222 
223  /** Function to call after the user clicked on a row. */
224  void list_item_clicked(widget& caller);
225 
226  /** See @ref container_base::set_self_active. */
227  virtual void set_self_active(const bool active) override;
228 
229  /**
230  * Request to update the size of the content after changing the content.
231  *
232  * When a resize is required the container first can try to handle it
233  * itself. If it can't honor the request the function will call @ref
234  * window::invalidate_layout().
235  *
236  * @note Calling this function on a widget with size == (0, 0) results
237  * false but doesn't call invalidate_layout, the engine expects to be in
238  * build up phase with the layout already invalidated.
239  *
240  * @returns True if the resizing succeeded, false
241  * otherwise.
242  */
243  bool update_content_size();
244 
245  /***** ***** ***** ***** inherited ***** ***** ****** *****/
246 
247  /** See @ref widget::place. */
248  virtual void place(const point& origin, const point& size) override;
249 
250  /***** ***** ***** setters / getters for members ***** ****** *****/
251 
252  void order_by(const generator_base::order_func& func);
253 
254 private:
255  struct sort_helper
256  {
257  template<typename T>
258  static bool less(const T& lhs, const T& rhs) { return lhs < rhs; }
259 
260  /** Performs case-insensitive comparison using the current locale. */
261  static bool less(const t_string& lhs, const t_string& rhs);
262 
263  template<typename T>
264  static bool more(const T& lhs, const T& rhs) { return lhs > rhs; }
265 
266  /** Performs case-insensitive comparison using the current locale. */
267  static bool more(const t_string& lhs, const t_string& rhs);
268  };
269 
270  /** Implementation detail of @ref set_single_sorter */
271  void initialize_sorter(std::string_view id, generator_sort_array&&);
272 
273  /** Implementation detail of @ref set_sorters */
274  template<std::size_t... Is, typename... Args>
275  void set_sorters_impl(std::index_sequence<Is...>, Args&&... fs)
276  {
277  (set_single_sorter("sort_" + std::to_string(Is), fs), ...);
278  }
279 
280 public:
281  /**
282  * Registers a single sorting control by ID.
283  *
284  * @param id The ID of the selectable_item header widget to bind to.
285  * @param f Any callable whose result is sortable.
286  */
287  template<typename Func>
288  void set_single_sorter(std::string_view id, const Func& f)
289  {
290  initialize_sorter(id, {
291  [f](int lhs, int rhs) { return sort_helper::less(f(lhs), f(rhs)); },
292  [f](int lhs, int rhs) { return sort_helper::more(f(lhs), f(rhs)); }
293  });
294  }
295 
296  /**
297  * Registers sorting controls using magic index IDs.
298  *
299  * This function accepts any callable whose result is sortable. Each callable passed
300  * will be bound to a corresponding selectable_item widget in the header, if present,
301  * whose ID is sort_N, where N is the index of the callable in the parameter pack.
302  *
303  * @param functors Zero or more callables with the signature T(std::size_t).
304  */
305  template<typename... Args>
306  void set_sorters(Args&&... functors)
307  {
308  set_sorters_impl(std::index_sequence_for<Args...>{}, std::forward<Args>(functors)...);
309  }
310 
311  /**
312  * Sorts the listbox by a pre-set sorting option. The corresponding header widget
313  * will also be toggled. The sorting option should already have been registered by
314  * @ref listbox::set_sorters().
315  *
316  * @param id The id of the sorter widget whose value to set.
317  * @param order The order to sort by (ascending, descending, or none).
318  * @param select_first If true, the first row post-sort will be selected.
319  * If false (default), the selected row will be maintained
320  * post-sort as per standard sorting functionality.
321  */
322  void set_active_sorter(std::string_view id, sort_order::type order, bool select_first = false);
323 
324  /** Returns a widget pointer to the active sorter, along with its corresponding order. */
325  std::pair<widget*, sort_order::type> get_active_sorter() const;
326 
327  /** Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted. */
328  void mark_as_unsorted();
329 
330  /** Registers a callback to be called when the active sorting option changes. */
331  void set_callback_order_change(std::function<void(unsigned, sort_order::type)> callback)
332  {
333  callback_order_change_ = callback;
334  }
335 
336 protected:
337  /***** ***** ***** ***** keyboard functions ***** ***** ***** *****/
338 
339  /** Inherited from scrollbar_container. */
340  void handle_key_up_arrow(SDL_Keymod modifier, bool& handled) override;
341 
342  /** Inherited from scrollbar_container. */
343  void handle_key_down_arrow(SDL_Keymod modifier, bool& handled) override;
344 
345  /** Inherited from scrollbar_container. */
346  void handle_key_left_arrow(SDL_Keymod modifier, bool& handled) override;
347 
348  /** Inherited from scrollbar_container. */
349  void handle_key_right_arrow(SDL_Keymod modifier, bool& handled) override;
350 
351 private:
352  /** See @ref widget::calculate_best_size. */
353  virtual point calculate_best_size() const override;
354 
356 
357  /** Helper to update visible area after a key event. */
359 
360  /**
361  * @todo A listbox must have the following config parameters in the
362  * instantiation:
363  * - fixed row height?
364  * - fixed column width?
365  * and if so the following ways to set them
366  * - fixed depending on header ids
367  * - fixed depending on footer ids
368  * - fixed depending on first row ids
369  * - fixed depending on list (the user has to enter a list of ids)
370  *
371  * For now it's always fixed width depending on the first row.
372  */
373 
374  /**
375  * Contains a pointer to the generator.
376  *
377  * The pointer is not owned by this class, it's stored in the content_grid_
378  * of the scrollbar_container super class and freed when it's grid is freed.
379  */
381 
383 
384  /** Contains the builder for the new items. */
386 
387  std::vector<std::pair<selectable_item*, generator_sort_array>> orders_;
388 
389  std::function<void(unsigned, sort_order::type)> callback_order_change_;
390 
391  /**
392  * Resizes the content.
393  *
394  * The resize either happens due to resizing the content or invalidate the
395  * layout of the window.
396  *
397  * @param width_modification The wanted modification to the width:
398  * * negative values reduce width.
399  * * zero leave width as is.
400  * * positive values increase width.
401  * @param height_modification The wanted modification to the height:
402  * * negative values reduce height.
403  * * zero leave height as is.
404  * * positive values increase height.
405  * @param width_modification_pos
406  * @param height_modification_pos
407  */
408  void resize_content(const int width_modification,
409  const int height_modification,
410  const int width_modification_pos = -1,
411  const int height_modification_pos = -1);
412 
413  /**
414  * Resizes the content.
415  *
416  * The resize happens when a new row is added to the contents.
417  *
418  * @param row The new row added to the listbox.
419  */
420  void resize_content(const widget& row);
421 
422  /** Updates internal layout. */
423  void update_layout();
424 
425  /** Inherited from scrollbar_container. */
426  virtual void set_content_size(const point& origin, const point& size) override;
427 
428 public:
429  /** Static type getter that does not rely on the widget being constructed. */
430  static const std::string& type();
431 
432 private:
433  /** Inherited from styled_widget, implemented by REGISTER_WIDGET. */
434  virtual const std::string& get_control_type() const override;
435 
436  void order_by_column(unsigned column, widget& widget);
437 };
438 
439 // }---------- DEFINITION ---------{
440 
442 {
443  explicit listbox_definition(const config& cfg);
444 
446  {
447  explicit resolution(const config& cfg);
448 
450  };
451 };
452 
453 // }---------- BUILDER -----------{
454 
455 namespace implementation
456 {
458 {
460 
462 
463  /** Inherited from builder_widget */
464  virtual std::unique_ptr<widget> build() const override;
465 
466  /** Flag for vertical, horizontal, or grid placement. */
468 
471 
473 
474  /**
475  * Listbox data.
476  *
477  * Contains a vector with the data to set in every cell, it's used to
478  * serialize the data in the config, so the config is no longer required.
479  */
480  std::vector<widget_data> list_data;
481 
483 };
484 
486 {
487  explicit builder_listbox(const config& cfg);
488 };
489 
491 {
492  explicit builder_horizontal_listbox(const config& cfg)
493  : builder_listbox_base(cfg, generator_base::horizontal_list)
494  {
495  }
496 };
497 
499 {
500  explicit builder_grid_listbox(const config& cfg)
501  : builder_listbox_base(cfg, generator_base::table)
502  {
503  }
504 };
505 
506 } // namespace implementation
507 
508 // }------------ END --------------
509 
510 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
Abstract base class for the generator.
Definition: generator.hpp:39
std::function< bool(unsigned, unsigned)> order_func
Definition: generator.hpp:248
placement
Determines how the items are placed.
Definition: generator.hpp:48
Base container class.
Definition: grid.hpp:32
The listbox class.
Definition: listbox.hpp:41
void update_layout()
Updates internal layout.
Definition: listbox.cpp:665
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:645
void set_sorters_impl(std::index_sequence< Is... >, Args &&... fs)
Implementation detail of set_sorters.
Definition: listbox.hpp:275
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: listbox.cpp:465
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition: listbox.hpp:186
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:544
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:530
void order_by_column(unsigned column, widget &widget)
Definition: listbox.cpp:574
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:600
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:617
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:654
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: listbox.cpp:381
void set_sorters(Args &&... functors)
Registers sorting controls using magic index IDs.
Definition: listbox.hpp:306
friend class debug_layout_graph
Definition: listbox.hpp:44
void update_visible_area_on_key_event(const KEY_SCROLL_DIRECTION direction)
Helper to update visible area after a key event.
Definition: listbox.cpp:481
void initialize_sorter(std::string_view id, generator_sort_array &&)
Implementation detail of set_single_sorter.
Definition: listbox.cpp:558
void set_callback_order_change(std::function< void(unsigned, sort_order::type)> callback)
Registers a callback to be called when the active sorting option changes.
Definition: listbox.hpp:331
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
virtual const std::string & get_control_type() const override
Inherited from styled_widget, implemented by REGISTER_WIDGET.
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:516
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:631
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
void set_single_sorter(std::string_view id, const Func &f)
Registers a single sorting control by ID.
Definition: listbox.hpp:288
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:502
Base class for creating containers with one or two scrollbar(s).
Base class for all widgets.
Definition: widget.hpp:55
Generic file dialog.
std::shared_ptr< builder_grid > builder_grid_ptr
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
std::shared_ptr< const builder_grid > builder_grid_const_ptr
Contains the implementation details for lexical_cast and shouldn't be used directly.
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
std::string_view data
Definition: picture.cpp:178
std::vector< widget_data > list_data
Listbox data.
Definition: listbox.hpp:480
generator_base::placement placement
Flag for vertical, horizontal, or grid placement.
Definition: listbox.hpp:467
virtual std::unique_ptr< widget > build() const override
Inherited from builder_widget.
Definition: listbox.cpp:763
builder_listbox_base(const config &cfg, const generator_base::placement placement)
Definition: listbox.cpp:738
builder_listbox(const config &cfg)
Definition: listbox.cpp:770
virtual std::unique_ptr< widget > build() const=0
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:685
Holds a 2D point.
Definition: point.hpp:25
#define f