The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
stacked_widget.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 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#define GETTEXT_DOMAIN "wesnoth-lib"
17
19
23#include "gettext.hpp"
24#include "utils/const_clone.hpp"
25#include "wml_exception.hpp"
26
27#include <functional>
28
29static lg::log_domain log_wml("wml");
30#define ERR_WML LOG_STREAM(err, log_wml)
31
32namespace gui2
33{
34
35// ------------ WIDGET -----------{
36
37REGISTER_WIDGET(stacked_widget)
38
40{
41 template<typename W>
43 const std::string_view id,
44 const bool must_be_active)
45 {
46 // Use base method if find-in-all-layer isn't set.
47 if(!stack.find_in_all_layers_) {
48 return stack.container_base::find(id, must_be_active);
49 }
50
51 for(unsigned i = 0; i < stack.get_layer_count(); ++i) {
52 if(W* res = stack.get_layer_grid(i)->find(id, must_be_active)) {
53 return res;
54 }
55 }
56
57 return stack.container_base::find(id, must_be_active);
58 }
59};
60
62 : container_base(builder, type())
63 , generator_(nullptr)
64 , selected_layer_(-1)
65 , find_in_all_layers_(false)
66{
67 const auto conf = cast_config_to<stacked_widget_definition>();
68 assert(conf);
69
70 init_grid(*conf->grid);
71
73
74 // Save our *non-owning* pointer before this gets moved into the grid.
75 generator_ = generator.get();
76 assert(generator_);
77
78 const widget_item empty_data;
79 for(const auto& layer_builder : builder.stack) {
80 generator->create_item(-1, layer_builder, empty_data, nullptr);
81 }
82
83 // TODO: can we use the replacements system here?
84 swap_grid(nullptr, &get_grid(), std::move(generator), "_content_grid");
85
86 select_layer(-1);
87}
88
90{
91 return true;
92}
93
95{
96 return 0;
97}
98
100{
101 assert(generator_);
102 for(unsigned i = 0; i < generator_->get_item_count(); ++i) {
104 }
105}
106
107void stacked_widget::set_self_active(const bool /*active*/)
108{
109 /* DO NOTHING */
110}
111
112void stacked_widget::select_layer_impl(const std::function<bool(unsigned int i)>& display_condition)
113{
114 const unsigned int num_layers = get_layer_count();
115
116 // Deselect all layers except the chosen ones.
117 for(unsigned int i = 0; i < num_layers; ++i) {
118 const bool selected = display_condition(i);
119
120 /* Selecting a previously selected item will deselect it, regardless of the what is passed to
121 * select_item. This causes issues if this function is called when all layers are visible (for
122 * example, initialization). For layers other than the chosen one, this is the desired behavior.
123 * However the chosen layer could *also* be deselected undesirably due to the conditions outlined
124 * above, and as this widget's generator does not stipulate a minimum selection, it's possible to
125 * end up with no layers visible at all.
126 *
127 * This works around that by performing no selection unless necessary to change states.
128 */
129 if(generator_->is_selected(i) != selected) {
130 generator_->select_item(i, selected);
131 }
132 }
133
134 // If we already have our chosen layers, exit.
135 if(selected_layer_ >= 0) {
136 return;
137 }
138
139 // Else, re-show all layers.
140 for(unsigned int i = 0; i < num_layers; ++i) {
141 /* By design, only the last selected item will receive events even if multiple items are visible
142 * and said item is not at the top of the stack. If this point is reached, all layers have already
143 * been hidden by the loop above, so the last layer selected will be the top-most one, as desired.
144 */
145 generator_->select_item(i, true);
146 }
147}
148
150{
151 selected_layer_ = std::clamp<int>(i, -1, get_layer_count() - 1);
152}
153
154bool stacked_widget::layer_selected(const unsigned layer)
155{
156 assert(layer < get_layer_count());
157 return generator_->is_selected(layer);
158}
159
160void stacked_widget::select_layer(const int layer)
161{
163
164 select_layer_impl([this](unsigned int i)
165 {
166 return i == static_cast<unsigned int>(selected_layer_);
167 });
168}
169
170void stacked_widget::select_layers(const boost::dynamic_bitset<>& mask)
171{
172 assert(mask.size() == get_layer_count());
173
174 select_layer_impl([&](unsigned int i)
175 {
176 if(mask[i]) {
178 }
179
180 return mask[i];
181 });
182}
183
185{
186 return generator_->get_item_count();
187}
188
190{
191 assert(generator_);
192 return &generator_->item(i);
193}
194
195const grid* stacked_widget::get_layer_grid(unsigned int i) const
196{
197 assert(generator_);
198 return &generator_->item(i);
199}
200
201widget* stacked_widget::find(const std::string_view id, const bool must_be_active)
202{
203 return stacked_widget_implementation::find<widget>(*this, id, must_be_active);
204}
205
206const widget* stacked_widget::find(const std::string_view id, const bool must_be_active) const
207{
208 return stacked_widget_implementation::find<const widget>(*this, id, must_be_active);
209}
210
211// }---------- DEFINITION ---------{
212
215{
216 DBG_GUI_P << "Parsing stacked widget " << id;
217
218 load_resolutions<resolution>(cfg);
219}
220
222 : resolution_definition(cfg), grid(nullptr)
223{
224 // Add a dummy state since every widget needs a state.
225 static config dummy("draw");
226 state.emplace_back(dummy);
227
228 auto child = cfg.optional_child("grid");
229 VALIDATE(child, _("No grid defined."));
230
231 grid = std::make_shared<builder_grid>(*child);
232}
233
234// }---------- BUILDER -----------{
235
236namespace implementation
237{
238
239builder_stacked_widget::builder_stacked_widget(const config& cfg)
240 : builder_styled_widget(cfg), stack()
241{
242 VALIDATE(cfg.has_child("layer"), _("No stack layers defined."));
243 for(const auto & layer : cfg.child_range("layer"))
244 {
245 stack.emplace_back(layer);
246 }
247}
248
249std::unique_ptr<widget> builder_stacked_widget::build() const
250{
251 auto widget = std::make_unique<stacked_widget>(*this);
252 DBG_GUI_G << "Window builder: placed stacked widget '" << id << "' with definition '" << definition << "'.";
253 return widget;
254}
255
256} // namespace implementation
257
258// }------------ END --------------
259
260} // namespace gui2
static auto & dummy
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
child_itors child_range(std::string_view key)
Definition config.cpp:268
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition config.cpp:312
A generic container base class.
const grid & get_grid() const
void init_grid(const builder_grid &grid_builder)
Initializes and builds the grid.
virtual grid & item(const unsigned index)=0
Gets the grid of an item.
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
virtual unsigned get_item_count() const =0
Returns the number of items.
static std::unique_ptr< generator_base > build(const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
Create a new generator.
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
Basic template class to generate new items.
grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback) override
Inherited from generator_base.
Base container class.
Definition grid.hpp:32
virtual void layout_children() override
See widget::layout_children.
Definition grid.cpp:623
virtual widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
generator_base * generator_
Contains a pointer to the generator.
virtual bool get_active() const override
See styled_widget::get_active.
void update_selected_layer_index(const int i)
void select_layer_impl(const std::function< bool(unsigned int i)> &display_condition)
Internal implementation detail for selecting layers.
grid * get_layer_grid(unsigned int i)
Gets the grid for a specified layer.
bool layer_selected(const unsigned layer)
Tests if the specified layer is selected (ie, visible).
int selected_layer_
The number of the current selected layer.
stacked_widget(const implementation::builder_stacked_widget &builder)
unsigned int get_layer_count() const
Gets the total number of layers.
virtual unsigned get_state() const override
See styled_widget::get_state.
void select_layers(const boost::dynamic_bitset<> &mask)
Selects and displays multiple layers based on the state of the provided dynamic_bitset.
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
virtual void layout_children() override
See widget::layout_children.
void select_layer(const int layer)
Selects and displays a particular layer.
Base class for all widgets.
Definition widget.hpp:55
const config * cfg
static std::string _(const char *str)
Definition gettext.hpp:100
#define DBG_GUI_G
Definition log.hpp:41
#define DBG_GUI_P
Definition log.hpp:66
Generic file dialog.
std::map< std::string, t_string > widget_item
Definition widget.hpp:33
void swap_grid(grid *g, grid *content_grid, std::unique_ptr< widget > widget, const std::string &id)
Swaps an item in a grid for another one.
Contains the implementation details for lexical_cast and shouldn't be used directly.
typename const_clone< D, S >::reference const_clone_ref
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
static lg::log_domain log_wml("wml")
std::vector< builder_grid > stack
The builders for all layers of the stack .
virtual std::unique_ptr< widget > build() const override
std::string definition
Parameters for the styled_widget.
std::vector< state_definition > state
stacked_widget_definition(const config &cfg)
static W * find(utils::const_clone_ref< stacked_widget, W > stack, const std::string_view id, const bool must_be_active)
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define i