The Battle for Wesnoth  1.19.5+dev
stacked_widget.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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 
29 static lg::log_domain log_wml("wml");
30 #define ERR_WML LOG_STREAM(err, log_wml)
31 
32 namespace gui2
33 {
34 
35 // ------------ WIDGET -----------{
36 
37 REGISTER_WIDGET(stacked_widget)
38 
40 {
41  template<typename W>
43  const std::string& 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 }
68 
70 {
71  return true;
72 }
73 
74 unsigned stacked_widget::get_state() const
75 {
76  return 0;
77 }
78 
80 {
81  assert(generator_);
82  for(unsigned i = 0; i < generator_->get_item_count(); ++i) {
84  }
85 }
86 
87 void stacked_widget::finalize(std::unique_ptr<generator_base> generator, const std::vector<builder_grid>& widget_builders)
88 {
89  // Save our *non-owning* pointer before this gets moved into the grid.
90  generator_ = generator.get();
91  assert(generator_);
92 
93  widget_item empty_data;
94  for(const auto & builder : widget_builders) {
95  generator->create_item(-1, builder, empty_data, nullptr);
96  }
97  swap_grid(nullptr, &get_grid(), std::move(generator), "_content_grid");
98 
99  select_layer(-1);
100 }
101 
102 void stacked_widget::set_self_active(const bool /*active*/)
103 {
104  /* DO NOTHING */
105 }
106 
107 void stacked_widget::select_layer_impl(std::function<bool(unsigned int i)> display_condition)
108 {
109  const unsigned int num_layers = get_layer_count();
110 
111  // Deselect all layers except the chosen ones.
112  for(unsigned int i = 0; i < num_layers; ++i) {
113  const bool selected = display_condition(i);
114 
115  /* Selecting a previously selected item will deselect it, regardless of the what is passed to
116  * select_item. This causes issues if this function is called when all layers are visible (for
117  * example, initialization). For layers other than the chosen one, this is the desired behavior.
118  * However the chosen layer could *also* be deselected undesirably due to the conditions outlined
119  * above, and as this widget's generator does not stipulate a minimum selection, it's possible to
120  * end up with no layers visible at all.
121  *
122  * This works around that by performing no selection unless necessary to change states.
123  */
124  if(generator_->is_selected(i) != selected) {
126  }
127  }
128 
129  // If we already have our chosen layers, exit.
130  if(selected_layer_ >= 0) {
131  return;
132  }
133 
134  // Else, re-show all layers.
135  for(unsigned int i = 0; i < num_layers; ++i) {
136  /* By design, only the last selected item will receive events even if multiple items are visible
137  * and said item is not at the top of the stack. If this point is reached, all layers have already
138  * been hidden by the loop above, so the last layer selected will be the top-most one, as desired.
139  */
140  generator_->select_item(i, true);
141  }
142 }
143 
145 {
146  selected_layer_ = std::clamp<int>(i, -1, get_layer_count() - 1);
147 }
148 
149 bool stacked_widget::layer_selected(const unsigned layer)
150 {
151  assert(layer < get_layer_count());
152  return generator_->is_selected(layer);
153 }
154 
155 void stacked_widget::select_layer(const int layer)
156 {
158 
159  select_layer_impl([this](unsigned int i)
160  {
161  return i == static_cast<unsigned int>(selected_layer_);
162  });
163 }
164 
165 void stacked_widget::select_layers(const boost::dynamic_bitset<>& mask)
166 {
167  assert(mask.size() == get_layer_count());
168 
169  select_layer_impl([&](unsigned int i)
170  {
171  if(mask[i]) {
173  }
174 
175  return mask[i];
176  });
177 }
178 
180 {
181  return generator_->get_item_count();
182 }
183 
185 {
186  assert(generator_);
187  return &generator_->item(i);
188 }
189 
190 const grid* stacked_widget::get_layer_grid(unsigned int i) const
191 {
192  assert(generator_);
193  return &generator_->item(i);
194 }
195 
196 widget* stacked_widget::find(const std::string& id, const bool must_be_active)
197 {
198  return stacked_widget_implementation::find<widget>(*this, id, must_be_active);
199 }
200 
201 const widget* stacked_widget::find(const std::string& id, const bool must_be_active) const
202 {
203  return stacked_widget_implementation::find<const widget>(*this, id, must_be_active);
204 }
205 
206 // }---------- DEFINITION ---------{
207 
210 {
211  DBG_GUI_P << "Parsing stacked widget " << id;
212 
213  load_resolutions<resolution>(cfg);
214 }
215 
217  : resolution_definition(cfg), grid(nullptr)
218 {
219  // Add a dummy state since every widget needs a state.
220  static config dummy("draw");
221  state.emplace_back(dummy);
222 
223  auto child = cfg.optional_child("grid");
224  VALIDATE(child, _("No grid defined."));
225 
226  grid = std::make_shared<builder_grid>(*child);
227 }
228 
229 // }---------- BUILDER -----------{
230 
231 namespace implementation
232 {
233 
234 builder_stacked_widget::builder_stacked_widget(const config& real_cfg)
235  : builder_styled_widget(real_cfg), stack()
236 {
237  const config& cfg = real_cfg.has_child("stack") ? real_cfg.mandatory_child("stack") : real_cfg;
238  if(&cfg != &real_cfg) {
239  lg::log_to_chat() << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.\n";
240  ERR_WML << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.";
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 
249 std::unique_ptr<widget> builder_stacked_widget::build() const
250 {
251  auto widget = std::make_unique<stacked_widget>(*this);
252 
253  DBG_GUI_G << "Window builder: placed stacked widget '" << id
254  << "' with definition '" << definition << "'.";
255 
256  const auto conf = widget->cast_config_to<stacked_widget_definition>();
257  assert(conf);
258 
259  widget->init_grid(*conf->grid);
260 
261  auto generator = generator_base::build(false, false, generator_base::independent, false);
262  widget->finalize(std::move(generator), stack);
263 
264  return widget;
265 }
266 
267 } // namespace implementation
268 
269 // }------------ END --------------
270 
271 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:366
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:316
child_itors child_range(config_key_type key)
Definition: config.cpp:272
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
A generic container base class.
const grid & get_grid() const
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
virtual grid & item(const unsigned index)=0
Gets the grid of 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.
Definition: generator.cpp:1160
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 &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 select_layer_impl(std::function< bool(unsigned int i)> display_condition)
Internal implementation detail for selecting layers.
void update_selected_layer_index(const int i)
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 finalize(std::unique_ptr< generator_base > generator, const std::vector< builder_grid > &widget_builders)
Finishes the building initialization of the widget.
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
std::size_t i
Definition: function.cpp:1028
static auto & dummy
static std::string _(const char *str)
Definition: gettext.hpp:93
#define DBG_GUI_G
Definition: log.hpp:41
#define DBG_GUI_P
Definition: log.hpp:66
std::string selected
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.
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:543
typename const_clone< D, S >::reference const_clone_ref
Definition: const_clone.hpp:63
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
#define ERR_WML
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 &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.