The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
listbox.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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
29namespace gui2
30{
31// ------------ WIDGET -----------{
32
33class selectable_item;
34namespace implementation
35{
36struct builder_listbox_base;
37}
38
39/** The listbox class. */
41{
43
44 friend class debug_layout_graph;
45
46public:
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
254private:
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
280public:
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 {
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
336protected:
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
351private:
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
428public:
429 /** Static type getter that does not rely on the widget being constructed. */
430 static const std::string& type();
431
432private:
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
455namespace 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{
493 : builder_listbox_base(cfg, generator_base::horizontal_list)
494 {
495 }
496};
497
499{
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:157
Abstract base class for the generator.
Definition generator.hpp:39
std::function< bool(unsigned, unsigned)> order_func
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:647
void list_item_clicked(widget &caller)
Function to call after the user clicked on a row.
Definition listbox.cpp:295
void mark_as_unsorted()
Deactivates all sorting toggle buttons at the top, making the list look like it's not sorted.
Definition listbox.cpp:627
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:447
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition listbox.hpp:186
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition listbox.cpp:524
void set_row_active(const unsigned row, const bool active)
Makes a row active or inactive.
Definition listbox.cpp:158
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:163
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition listbox.cpp:512
void order_by_column(unsigned column, widget &widget)
Definition listbox.cpp:557
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:395
void order_by(const generator_base::order_func &func)
Definition listbox.cpp:583
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition listbox.cpp:339
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:256
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:599
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:636
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition listbox.cpp:363
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:467
void initialize_sorter(std::string_view id, generator_sort_array &&)
Implementation detail of set_single_sorter.
Definition listbox.cpp:536
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:267
bool update_content_size()
Request to update the size of the content after changing the content.
Definition listbox.cpp:344
boost::dynamic_bitset get_rows_shown() const
Returns a list of visible rows.
Definition listbox.cpp:251
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:238
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition listbox.cpp:500
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:613
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition listbox.cpp:108
void clear()
Removes all the rows in the listbox, clearing it.
Definition listbox.cpp:147
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:290
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition listbox.cpp:153
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:280
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:285
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition listbox.cpp:488
Base class for creating containers with one or two scrollbar(s).
Base class for all widgets.
Definition widget.hpp:55
const config * cfg
Generic file dialog.
std::shared_ptr< builder_grid > builder_grid_ptr
std::array< generator_base::order_func, 2 > generator_sort_array
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::string_view data
Definition picture.cpp:188
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:743
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
Holds a 2D point.
Definition point.hpp:25
#define f