The Battle for Wesnoth  1.19.5+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;
37 struct builder_horizontal_listbox;
38 struct builder_grid_listbox;
39 }
40 
41 /** The listbox class. */
43 {
47 
48  friend class debug_layout_graph;
49 
50 public:
51  /**
52  * Constructor.
53  *
54  * @param builder The builder for the appropriate listbox variant.
55  * @param placement How are the items placed.
56  * @param list_builder Grid builder for the listbox definition grid.
57  */
59  const generator_base::placement placement,
60  builder_grid_ptr list_builder);
61 
62  /***** ***** ***** ***** Row handling. ***** ***** ****** *****/
63 
64  /**
65  * When an item in the list is selected by the user we need to
66  * update the state. We installed a callback handler which
67  * calls us.
68  *
69  * @param item The data send to the set_members of the
70  * widgets.
71  * @param index The item before which to add the new item,
72  * 0 == begin, -1 == end.
73  */
74  grid& add_row(const widget_item& item, const int index = -1);
75 
76  /**
77  * Adds single row to the grid.
78  *
79  * This function expect a row to have multiple widgets (either multiple
80  * columns or one column with multiple widgets).
81  *
82  *
83  * @param data The data to send to the set_members of the
84  * widgets. If the member id is not an empty
85  * string it is only send to the widget that has
86  * the wanted id (if any). If the member id is an
87  * empty string, it is send to all members.
88  * Having both empty and non-empty id's gives
89  * undefined behavior.
90  * @param index The item before which to add the new item,
91  * 0 == begin, -1 == end.
92  */
93  grid& add_row(const widget_data& data, const int index = -1);
94 
95  /**
96  * Removes a row in the listbox.
97  *
98  * @param row The row to remove, when not in
99  * range the function is ignored.
100  * @param count The number of rows to remove, 0 means all
101  * rows (starting from row).
102  */
103  void remove_row(const unsigned row, unsigned count = 1);
104 
105  /** Removes all the rows in the listbox, clearing it. */
106  void clear();
107 
108  /** Returns the number of items in the listbox. */
109  unsigned get_item_count() const;
110 
111  /**
112  * Makes a row active or inactive.
113  *
114  * NOTE this doesn't change the select status of the row.
115  *
116  * @param row The row to (de)activate.
117  * @param active true activate, false deactivate.
118  */
119  void set_row_active(const unsigned row, const bool active);
120 
121  /**
122  * Makes a row visible or invisible.
123  *
124  * @param row The row to show or hide.
125  * @param shown true visible, false invisible.
126  */
127  void set_row_shown(const unsigned row, const bool shown);
128 
129  /**
130  * Makes a row visible or invisible.
131  *
132  * Use this version if you want to show hide multiple items since it's
133  * optimized for that purpose, for one it calls the selection changed
134  * callback only once instead of several times.
135  *
136  * @param shown A vector with the show hide status for every
137  * row. The number of items in the vector must
138  * be equal to the number of items in the
139  * listbox.
140  */
141  void set_row_shown(const boost::dynamic_bitset<>& shown);
142 
143  /**
144  * Returns a list of visible rows
145  *
146  * @returns A mask indicating which rows are visible
147  */
148  boost::dynamic_bitset<> get_rows_shown() const;
149 
150  bool any_rows_shown() const;
151 
152  /**
153  * Returns the grid of the wanted row.
154  *
155  * There's only a const version since allowing callers to modify the grid
156  * behind our backs might give problems. We return a pointer instead of a
157  * reference since dynamic casting of pointers is easier (no try catch
158  * needed).
159  *
160  * @param row The row to get the grid from, the caller has
161  * to make sure the row is a valid row.
162  * @returns The grid of the wanted row.
163  */
164  const grid* get_row_grid(const unsigned row) const;
165 
166  /**
167  * The possibly-giving-problems nonconst version of get_row_grid
168  *
169  * @param row The row to get the grid from, the caller has
170  * to make sure the row is a valid row.
171  * @returns The grid of the wanted row.
172  */
173  grid* get_row_grid(const unsigned row);
174 
175  /**
176  * Selects a row.
177  *
178  * @param row The row to select.
179  * @param select Select or deselect the row.
180  * @returns True if the operation succeeded.
181  */
182  bool select_row(const unsigned row, const bool select = true);
183 
184  /**
185  * Does exactly as advertised: selects the list's last row.
186  *
187  * @param select Select or deselect the row.
188  */
189  bool select_last_row(const bool select = true)
190  {
191  return select_row(get_item_count() - 1, select);
192  }
193 
194  /**
195  * Selects a row at the given position, regardless of sorting order.
196  *
197  * When using @ref select_row the relevant row is located by index regardless
198  * of its actual position in the list, which could differ if the list had been
199  * sorted. In that case, `select_row(0)` would not select the list's first row
200  * as displayed.
201  *
202  * This function allows row selection based on position. `select_row_at(0)` will
203  * always select the list's first row, regardless of sorting order.
204  *
205  * @param row The row to select.
206  * @param select Select or deselect the row.
207  *
208  * @returns True if the operation succeeded.
209  */
210  bool select_row_at(const unsigned row, const bool select = true);
211 
212  /**
213  * Check if a row is selected
214  * @param row The row to test
215  * @returns True if it is selected.
216  */
217  bool row_selected(const unsigned row);
218 
219  /**
220  * Returns the first selected row
221  *
222  * @returns The first selected row, or -1 if no row is selected.
223  */
224  int get_selected_row() const;
225 
226  /** Function to call after the user clicked on a row. */
227  void list_item_clicked(widget& caller);
228 
229  /** See @ref container_base::set_self_active. */
230  virtual void set_self_active(const bool active) override;
231 
232  /**
233  * Request to update the size of the content after changing the content.
234  *
235  * When a resize is required the container first can try to handle it
236  * itself. If it can't honor the request the function will call @ref
237  * window::invalidate_layout().
238  *
239  * @note Calling this function on a widget with size == (0, 0) results
240  * false but doesn't call invalidate_layout, the engine expects to be in
241  * build up phase with the layout already invalidated.
242  *
243  * @returns True if the resizing succeeded, false
244  * otherwise.
245  */
246  bool update_content_size();
247 
248  /***** ***** ***** ***** inherited ***** ***** ****** *****/
249 
250  /** See @ref widget::place. */
251  virtual void place(const point& origin, const point& size) override;
252 
253  /***** ***** ***** setters / getters for members ***** ****** *****/
254 
255  void order_by(const generator_base::order_func& func);
256 
257  void set_column_order(unsigned col, const generator_sort_array& func);
258 
259  template<typename Func>
260  void register_sorting_option(const int col, const Func& f)
261  {
262  set_column_order(col, {{
263  [f](int lhs, int rhs) { return f(lhs) < f(rhs); },
264  [f](int lhs, int rhs) { return f(lhs) > f(rhs); }
265  }});
266  }
267 
268  using translatable_sorter_func_t = std::function<std::string(const int)>;
269 
270  /** Registers a special sorting function specifically for translatable values. */
272 
273  using order_pair = std::pair<int, sort_order::type>;
274 
275  /**
276  * Sorts the listbox by a pre-set sorting option. The corresponding header widget will also be toggled.
277  * The sorting option should already have been registered by @ref listbox::register_sorting_option().
278  *
279  * @param sort_by Pair of column index and sort direction. The column (first arguemnt)
280  * argument will be sorted in the specified direction (second argument)
281  *
282  * @param select_first If true, the first row post-sort will be selected. If false (default),
283  * the selected row will be maintained post-sort as per standard sorting
284  * functionality.
285  */
286  void set_active_sorting_option(const order_pair& sort_by, const bool select_first = false);
287 
289 
290  /** Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted. */
291  void mark_as_unsorted();
292 
293  /** Registers a callback to be called when the active sorting option changes. */
294  void set_callback_order_change(std::function<void(unsigned, sort_order::type)> callback)
295  {
296  callback_order_change_ = callback;
297  }
298 
299 protected:
300  /***** ***** ***** ***** keyboard functions ***** ***** ***** *****/
301 
302  /** Inherited from scrollbar_container. */
303  void handle_key_up_arrow(SDL_Keymod modifier, bool& handled) override;
304 
305  /** Inherited from scrollbar_container. */
306  void handle_key_down_arrow(SDL_Keymod modifier, bool& handled) override;
307 
308  /** Inherited from scrollbar_container. */
309  void handle_key_left_arrow(SDL_Keymod modifier, bool& handled) override;
310 
311  /** Inherited from scrollbar_container. */
312  void handle_key_right_arrow(SDL_Keymod modifier, bool& handled) override;
313 
314 private:
315  /** See @ref widget::calculate_best_size. */
316  virtual point calculate_best_size() const override;
317 
319 
320  /** Helper to update visible area after a key event. */
322 
323  /**
324  * @todo A listbox must have the following config parameters in the
325  * instantiation:
326  * - fixed row height?
327  * - fixed column width?
328  * and if so the following ways to set them
329  * - fixed depending on header ids
330  * - fixed depending on footer ids
331  * - fixed depending on first row ids
332  * - fixed depending on list (the user has to enter a list of ids)
333  *
334  * For now it's always fixed width depending on the first row.
335  */
336 
337  /**
338  * Finishes the building initialization of the widget.
339  *
340  * @param generator Generator for the list
341  * @param header Builder for the header.
342  * @param footer Builder for the footer.
343  * @param list_data The initial data to fill the listbox with.
344  */
345  void finalize(std::unique_ptr<generator_base> generator,
346  builder_grid_const_ptr header,
347  builder_grid_const_ptr footer,
348  const std::vector<widget_data>& list_data);
349 
350  /**
351  * Contains a pointer to the generator.
352  *
353  * The pointer is not owned by this class, it's stored in the content_grid_
354  * of the scrollbar_container super class and freed when it's grid is freed.
355  */
357 
358  const bool is_horizontal_;
359 
360  /** Contains the builder for the new items. */
362 
363  typedef std::vector<std::pair<selectable_item*, generator_sort_array>> torder_list;
365 
366  std::function<void(unsigned, sort_order::type)> callback_order_change_;
367 
368  /**
369  * Resizes the content.
370  *
371  * The resize either happens due to resizing the content or invalidate the
372  * layout of the window.
373  *
374  * @param width_modification The wanted modification to the width:
375  * * negative values reduce width.
376  * * zero leave width as is.
377  * * positive values increase width.
378  * @param height_modification The wanted modification to the height:
379  * * negative values reduce height.
380  * * zero leave height as is.
381  * * positive values increase height.
382  * @param width_modification_pos
383  * @param height_modification_pos
384  */
385  void resize_content(const int width_modification,
386  const int height_modification,
387  const int width_modification_pos = -1,
388  const int height_modification_pos = -1);
389 
390  /**
391  * Resizes the content.
392  *
393  * The resize happens when a new row is added to the contents.
394  *
395  * @param row The new row added to the listbox.
396  */
397  void resize_content(const widget& row);
398 
399  /** Updates internal layout. */
400  void update_layout();
401 
402  /** Inherited from scrollbar_container. */
403  virtual void set_content_size(const point& origin, const point& size) override;
404 
405 public:
406  /** Static type getter that does not rely on the widget being constructed. */
407  static const std::string& type();
408 
409 private:
410  /** Inherited from styled_widget, implemented by REGISTER_WIDGET. */
411  virtual const std::string& get_control_type() const override;
412 
413  void order_by_column(unsigned column, widget& widget);
414 };
415 
416 // }---------- DEFINITION ---------{
417 
419 {
420  explicit listbox_definition(const config& cfg);
421 
423  {
424  explicit resolution(const config& cfg);
425 
427  };
428 };
429 
430 // }---------- BUILDER -----------{
431 
432 namespace implementation
433 {
434 
436 {
437  explicit builder_listbox(const config& cfg);
438 
440 
441  virtual std::unique_ptr<widget> build() const override;
442 
445 
448 
450 
451  /**
452  * Listbox data.
453  *
454  * Contains a vector with the data to set in every cell, it's used to
455  * serialize the data in the config, so the config is no longer required.
456  */
457  std::vector<widget_data> list_data;
458 
460 };
461 
463 {
464  explicit builder_horizontal_listbox(const config& cfg);
465 
467 
468  virtual std::unique_ptr<widget> build() const override;
469 
472 
474 
475  /**
476  * Listbox data.
477  *
478  * Contains a vector with the data to set in every cell, it's used to
479  * serialize the data in the config, so the config is no longer required.
480  */
481  std::vector<widget_data> list_data;
482 
484 };
485 
487 {
488  explicit builder_grid_listbox(const config& cfg);
489 
491 
492  virtual std::unique_ptr<widget> build() const override;
493 
496 
498 
499  /**
500  * Listbox data.
501  *
502  * Contains a vector with the data to set in every cell, it's used to
503  * serialize the data in the config, so the config is no longer required.
504  */
505  std::vector<widget_data> list_data;
506 
508 };
509 
510 } // namespace implementation
511 
512 // }------------ END --------------
513 
514 } // 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
Basic template class to generate new items.
Base container class.
Definition: grid.hpp:32
The listbox class.
Definition: listbox.hpp:43
void update_layout()
Updates internal layout.
Definition: listbox.cpp:674
const bool is_horizontal_
Definition: listbox.hpp:358
void list_item_clicked(widget &caller)
Function to call after the user clicked on a row.
Definition: listbox.cpp:273
void mark_as_unsorted()
Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted.
Definition: listbox.cpp:654
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: listbox.cpp:427
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition: listbox.hpp:189
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:506
void set_row_active(const unsigned row, const bool active)
Makes a row active or inactive.
Definition: listbox.cpp:129
torder_list orders_
Definition: listbox.hpp:364
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:135
void set_active_sorting_option(const order_pair &sort_by, const bool select_first=false)
Sorts the listbox by a pre-set sorting option.
Definition: listbox.cpp:620
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:492
void order_by_column(unsigned column, widget &widget)
Definition: listbox.cpp:566
std::pair< int, sort_order::type > order_pair
Definition: listbox.hpp:273
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:375
void order_by(const generator_base::order_func &func)
Definition: listbox.cpp:596
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: listbox.cpp:319
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:58
std::function< std::string(const int)> translatable_sorter_func_t
Definition: listbox.hpp:268
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:229
void register_translatable_sorting_option(const int col, translatable_sorter_func_t f)
Registers a special sorting function specifically for translatable values.
Definition: listbox.cpp:612
virtual void set_content_size(const point &origin, const point &size) override
Inherited from scrollbar_container.
Definition: listbox.cpp:663
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: listbox.cpp:343
friend class debug_layout_graph
Definition: listbox.hpp:48
void update_visible_area_on_key_event(const KEY_SCROLL_DIRECTION direction)
Helper to update visible area after a key event.
Definition: listbox.cpp:443
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:294
std::vector< std::pair< selectable_item *, generator_sort_array > > torder_list
Definition: listbox.hpp:363
generator_base * generator_
Contains a pointer to the generator.
Definition: listbox.hpp:356
std::function< void(unsigned, sort_order::type)> callback_order_change_
Definition: listbox.hpp:366
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:242
bool update_content_size()
Request to update the size of the content after changing the content.
Definition: listbox.cpp:324
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition: listbox.cpp:213
bool any_rows_shown() const
Definition: listbox.cpp:218
void register_sorting_option(const int col, const Func &f)
Definition: listbox.hpp:260
virtual const std::string & get_control_type() const override
Inherited from styled_widget, implemented by REGISTER_WIDGET.
void finalize(std::unique_ptr< generator_base > generator, builder_grid_const_ptr header, builder_grid_const_ptr footer, const std::vector< widget_data > &list_data)
Finishes the building initialization of the widget.
Definition: listbox.cpp:520
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:478
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:78
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:117
void set_column_order(unsigned col, const generator_sort_array &func)
Definition: listbox.cpp:603
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:267
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:123
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:255
const order_pair get_active_sorting_option()
Definition: listbox.cpp:639
builder_grid_const_ptr list_builder_
Contains the builder for the new items.
Definition: listbox.hpp:361
bool row_selected(const unsigned row)
Check if a row is selected.
Definition: listbox.cpp:261
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: listbox.cpp:464
Base class for creating containers with one or two scrollbar(s).
scrollbar_mode
The way to handle the showing or hiding of the scrollbar.
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 index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string_view data
Definition: picture.cpp:178
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:495
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:494
virtual std::unique_ptr< widget > build() const override
Definition: listbox.cpp:867
std::vector< widget_data > list_data
Listbox data.
Definition: listbox.hpp:505
virtual std::unique_ptr< widget > build() const override
Definition: listbox.cpp:824
std::vector< widget_data > list_data
Listbox data.
Definition: listbox.hpp:481
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:470
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:471
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:443
builder_listbox(const config &cfg)
Definition: listbox.cpp:747
std::vector< widget_data > list_data
Listbox data.
Definition: listbox.hpp:457
virtual std::unique_ptr< widget > build() const override
Definition: listbox.cpp:781
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:444
virtual std::unique_ptr< widget > build() const=0
listbox_definition(const config &cfg)
Definition: listbox.cpp:694
Holds a 2D point.
Definition: point.hpp:25
#define f