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