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 http://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 
17 #ifndef GUI2_EXPERIMENTAL_LISTBOX
18 
21 
24 
25 #include <boost/dynamic_bitset.hpp>
26 #include <functional>
27 
28 namespace gui2
29 {
30 // ------------ WIDGET -----------{
31 
32 class selectable_item;
33 namespace implementation
34 {
35 struct builder_listbox;
36 struct builder_horizontal_listbox;
37 struct builder_grid_listbox;
38 struct builder_styled_widget;
39 }
40 
41 /** The listbox class. */
43 {
47  friend class debug_layout_graph;
48 
49 public:
50  /**
51  * Constructor.
52  *
53  * @param builder The builder for the appropriate listbox variant.
54  * @param placement How are the items placed.
55  * @param list_builder Grid builder for the listbox definition grid.
56  * @param has_minimum Does the listbox need to have one item selected.
57  * @param has_maximum Can the listbox only have one item selected.
58  * @param select Select an item when selected. If false it changes
59  * the visible state instead. Default true.
60  */
62  const generator_base::placement placement,
63  builder_grid_ptr list_builder,
64  const bool has_minimum,
65  const bool has_maximum,
66  const bool select = true);
67 
68  /***** ***** ***** ***** Row handling. ***** ***** ****** *****/
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 string_map& 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 std::map<std::string /* widget id */, string_map>& 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<std::map<std::string, string_map>>& 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  /** Inherited from styled_widget, implemented by REGISTER_WIDGET. */
418  virtual const std::string& get_control_type() const override;
419 
420  void order_by_column(unsigned column, widget& widget);
421 };
422 
423 // }---------- DEFINITION ---------{
424 
426 {
427  explicit listbox_definition(const config& cfg);
428 
430  {
431  explicit resolution(const config& cfg);
432 
434  };
435 };
436 
437 // }---------- BUILDER -----------{
438 
439 namespace implementation
440 {
442 {
443  explicit builder_listbox(const config& cfg);
444 
446 
447  widget* build() const;
448 
451 
454 
456 
457  /**
458  * Listbox data.
459  *
460  * Contains a vector with the data to set in every cell, it's used to
461  * serialize the data in the config, so the config is no longer required.
462  */
463  std::vector<std::map<std::string, string_map>> list_data;
464 
465  bool has_minimum_, has_maximum_;
466 };
467 
469 {
470  explicit builder_horizontal_listbox(const config& cfg);
471 
473 
474  widget* build() const;
475 
478 
480 
481  /**
482  * Listbox data.
483  *
484  * Contains a vector with the data to set in every cell, it's used to
485  * serialize the data in the config, so the config is no longer required.
486  */
487  std::vector<std::map<std::string, string_map>> list_data;
488 
489  bool has_minimum_, has_maximum_;
490 };
491 
493 {
494  explicit builder_grid_listbox(const config& cfg);
495 
497 
498  widget* build() const;
499 
502 
504 
505  /**
506  * Listbox data.
507  *
508  * Contains a vector with the data to set in every cell, it's used to
509  * serialize the data in the config, so the config is no longer required.
510  */
511  std::vector<std::map<std::string, string_map>> list_data;
512 
513  bool has_minimum_, has_maximum_;
514 };
515 
516 } // namespace implementation
517 
518 // }------------ END --------------
519 
520 } // namespace gui2
521 
522 #endif
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:380
std::shared_ptr< const builder_grid > builder_grid_const_ptr
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:450
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:477
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: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
The listbox class.
Definition: listbox.hpp:42
Base container class.
Definition: grid.hpp:30
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:500
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:501
Abstract base class for the generator.
Definition: generator.hpp:39
std::vector< std::pair< selectable_item *, generator_sort_array > > torder_list
Definition: listbox.hpp:377
std::vector< std::map< std::string, string_map > > list_data
Listbox data.
Definition: listbox.hpp:463
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:49
std::vector< std::map< std::string, string_map > > list_data
Listbox data.
Definition: listbox.hpp:487
Base class for creating containers with one or two scrollbar(s).
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:371
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:511
#define f
void finalize()
Definition: events.cpp:412
const bool is_horizontal_
Definition: listbox.hpp:370
point resolution()
Definition: general.cpp:375
generator_base * generator_
Contains a pointer to the generator.
Definition: listbox.hpp:368
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:476
std::shared_ptr< builder_grid > builder_grid_ptr
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:449
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.