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