The Battle for Wesnoth  1.15.2+dev
stacked_widget.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
21 #include "gui/widgets/settings.hpp"
24 #include "gettext.hpp"
25 #include "utils/const_clone.hpp"
26 #include "utils/general.hpp"
27 
28 #include "utils/functional.hpp"
29 
30 namespace gui2
31 {
32 
33 // ------------ WIDGET -----------{
34 
35 REGISTER_WIDGET(stacked_widget)
36 
38 {
39  template<typename W>
41  const std::string& id,
42  const bool must_be_active)
43  {
44  // Use base method if find-in-all-layer isn't set.
45  if(!stack.find_in_all_layers_) {
46  return stack.container_base::find(id, must_be_active);
47  }
48 
49  for(unsigned i = 0; i < stack.get_layer_count(); ++i) {
50  if(W* res = stack.get_layer_grid(i)->find(id, must_be_active)) {
51  return res;
52  }
53  }
54 
55  return stack.container_base::find(id, must_be_active);
56  }
57 };
58 
60  : container_base(builder, type())
61  , generator_(generator_base::build(false, false, generator_base::independent, false))
62  , selected_layer_(-1)
63  , find_in_all_layers_(false)
64 {
65 }
66 
68 {
69  return true;
70 }
71 
72 unsigned stacked_widget::get_state() const
73 {
74  return 0;
75 }
76 
78 {
79  assert(generator_);
80  for(unsigned i = 0; i < generator_->get_item_count(); ++i) {
82  }
83 }
84 
85 void
86 stacked_widget::finalize(std::vector<builder_grid_const_ptr> widget_builder)
87 {
88  assert(generator_);
89  string_map empty_data;
90  for(const auto & builder : widget_builder)
91  {
92  generator_->create_item(-1, builder, empty_data, nullptr);
93  }
94  swap_grid(nullptr, &get_grid(), generator_, "_content_grid");
95 
96  select_layer(-1);
97 }
98 
99 void stacked_widget::set_self_active(const bool /*active*/)
100 {
101  /* DO NOTHING */
102 }
103 
104 void stacked_widget::select_layer_impl(std::function<bool(unsigned int i)> display_condition)
105 {
106  const unsigned int num_layers = get_layer_count();
107 
108  // Deselect all layers except the chosen ones.
109  for(unsigned int i = 0; i < num_layers; ++i) {
110  const bool selected = display_condition(i);
111 
112  /* Selecting a previously selected item will deselect it, regardless of the what is passed to
113  * select_item. This causes issues if this function is called when all layers are visible (for
114  * example, initialization). For layers other than the chosen one, this is the desired behavior.
115  * However the chosen layer could *also* be deselected undesirably due to the conditions outlined
116  * above, and as this widget's generator does not stipulate a minimum selection, it's possible to
117  * end up with no layers visible at all.
118  *
119  * This works around that by performing no selection unless necessary to change states.
120  */
121  if(generator_->is_selected(i) != selected) {
122  generator_->select_item(i, selected);
123  }
124  }
125 
126  // If we already have our chosen layers, exit.
127  if(selected_layer_ >= 0) {
128  return;
129  }
130 
131  // Else, re-show all layers.
132  for(unsigned int i = 0; i < num_layers; ++i) {
133  /* By design, only the last selected item will receive events even if multiple items are visible
134  * and said item is not at the top of the stack. If this point is reached, all layers have already
135  * been hidden by the loop above, so the last layer selected will be the top-most one, as desired.
136  */
137  generator_->select_item(i, true);
138  }
139 }
140 
142 {
143  selected_layer_ = utils::clamp<int>(i, -1, get_layer_count() - 1);
144 }
145 
146 bool stacked_widget::layer_selected(const unsigned layer)
147 {
148  assert(layer < get_layer_count());
149  return generator_->is_selected(layer);
150 }
151 
152 void stacked_widget::select_layer(const int layer)
153 {
155 
156  select_layer_impl([this](unsigned int i)
157  {
158  return i == static_cast<unsigned int>(selected_layer_);
159  });
160 }
161 
162 void stacked_widget::select_layers(const boost::dynamic_bitset<>& mask)
163 {
164  assert(mask.size() == get_layer_count());
165 
166  select_layer_impl([&](unsigned int i)
167  {
168  if(mask[i]) {
170  }
171 
172  return mask[i];
173  });
174 }
175 
177 {
178  return generator_->get_item_count();
179 }
180 
182 {
183  assert(generator_);
184  return &generator_->item(i);
185 }
186 
187 const grid* stacked_widget::get_layer_grid(unsigned int i) const
188 {
189  assert(generator_);
190  return &generator_->item(i);
191 }
192 
193 widget* stacked_widget::find(const std::string& id, const bool must_be_active)
194 {
195  return stacked_widget_implementation::find<widget>(*this, id, must_be_active);
196 }
197 
198 const widget* stacked_widget::find(const std::string& id, const bool must_be_active) const
199 {
200  return stacked_widget_implementation::find<const widget>(*this, id, must_be_active);
201 }
202 
203 // }---------- DEFINITION ---------{
204 
207 {
208  DBG_GUI_P << "Parsing stacked widget " << id << '\n';
209 
210  load_resolutions<resolution>(cfg);
211 }
212 
213 /*WIKI
214  * @page = GUIWidgetDefinitionWML
215  * @order = 1_stacked_widget
216  *
217  * == Stacked widget ==
218  *
219  * A stacked widget holds several widgets on top of each other. This can be used
220  * for various effects; add an optional overlay to an image, stack it with a
221  * spacer to force a minimum size of a widget. The latter is handy to avoid
222  * making a separate definition for a single instance with a fixed size.
223  *
224  * A stacked widget has no states.
225  * @begin{parent}{name="gui/"}
226  * @begin{tag}{name="stacked_widget_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
227  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
228  * @allow{link}{name="gui/window/resolution/grid"}
229  * @end{tag}{name="resolution"}
230  * @end{tag}{name="stacked_widget_definition"}
231  * @end{parent}{name="gui/"}
232  */
234  : resolution_definition(cfg), grid(nullptr)
235 {
236  // Add a dummy state since every widget needs a state.
237  static config dummy("draw");
238  state.emplace_back(dummy);
239 
240  const config& child = cfg.child("grid");
241  VALIDATE(child, _("No grid defined."));
242 
243  grid = std::make_shared<builder_grid>(child);
244 }
245 
246 // }---------- BUILDER -----------{
247 
248 /*WIKI
249  * @page = GUIToolkitWML
250  * @order = 2_stacked_widget
251  *
252  * == Stacked widget ==
253  *
254  * A stacked widget is a set of widget stacked on top of each other. The
255  * widgets are drawn in the layers, in the order defined in the the instance
256  * config. By default the last drawn item is also the 'active' layer for the
257  * event handling.
258  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
259  * @begin{tag}{name="stacked_widget"}{min="0"}{max="-1"}{super="generic/widget_instance"}
260  * @begin{table}{config}
261  * @end{table}
262  * @begin{tag}{name="layer"}{min=0}{max=-1}{super="gui/window/resolution/grid"}
263  * @end{tag}{name="layer"}
264  * @end{tag}{name="stacked_widget"}
265  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
266  */
267 
268 namespace implementation
269 {
270 
271 builder_stacked_widget::builder_stacked_widget(const config& real_cfg)
272  : builder_styled_widget(real_cfg), stack()
273 {
274  const config& cfg = real_cfg.has_child("stack") ? real_cfg.child("stack") : real_cfg;
275  if(&cfg != &real_cfg) {
276  lg::wml_error() << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.\n";
277  }
278  VALIDATE(cfg.has_child("layer"), _("No stack layers defined."));
279  for(const auto & layer : cfg.child_range("layer"))
280  {
281  stack.emplace_back(std::make_shared<builder_grid>(layer));
282  }
283 }
284 
286 {
287  stacked_widget* widget = new stacked_widget(*this);
288 
289  DBG_GUI_G << "Window builder: placed stacked widget '" << id
290  << "' with definition '" << definition << "'.\n";
291 
292  const auto conf = widget->cast_config_to<stacked_widget_definition>();
293  assert(conf);
294 
295  widget->init_grid(conf->grid);
296 
297  widget->finalize(stack);
298 
299  return widget;
300 }
301 
302 } // namespace implementation
303 
304 // }------------ END --------------
305 
306 } // namespace gui2
Base class of a resolution, contains the common keys for a resolution.
#define DBG_GUI_P
Definition: log.hpp:68
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:420
std::vector< state_definition > state
virtual unsigned get_item_count() const =0
Returns the number of items.
virtual grid & create_item(const int index, builder_grid_const_ptr list_builder, const string_map &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
int dummy
Definition: lstrlib.cpp:1125
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:412
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
const std::string & id() const
Definition: widget.cpp:107
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:362
Base class for all widgets.
Definition: widget.hpp:47
virtual grid & item(const unsigned index)=0
Gets the grid of an item.
void swap_grid(grid *g, grid *content_grid, widget *widget, const std::string &id)
Swaps an item in a grid for another one.
typename const_clone< D, S >::reference const_clone_ref
Definition: const_clone.hpp:62
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Generic file dialog.
Definition: field-fwd.hpp:22
Base container class.
Definition: grid.hpp:30
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
std::string definition
Parameters for the styled_widget.
bool layer_selected(const unsigned layer)
Tests if the specified layer is selected (ie, visible).
Abstract base class for the generator.
Definition: generator.hpp:39
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
std::vector< builder_grid_const_ptr > stack
The builders for all layers of the stack .
This file contains the settings handling of the widget library.
generator_base * generator_
Contains a pointer to the generator.
std::string selected
stacked_widget(const implementation::builder_stacked_widget &builder)
void init_grid(const std::shared_ptr< builder_grid > &grid_builder)
Initializes and builds the grid.
#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:933
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
std::shared_ptr< const typename T::resolution > cast_config_to() const
Casts the current resolution definition config to the respective type of a derived widget...
virtual void layout_children() override
See widget::layout_children.
window * build(const builder_window::window_resolution *definition)
Builds a window.
std::map< std::string, t_string > string_map
Definition: widget.hpp:24
virtual unsigned get_state() const override
See styled_widget::get_state.
A generic container base class.
void select_layer(const int layer)
Selects and displays a particular layer.
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:618
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
void finalize(std::vector< builder_grid_const_ptr > widget_builder)
Finishes the building initialization of the widget.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
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:40
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.