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