The Battle for Wesnoth  1.15.12+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 
27 #include <functional>
28 
29 namespace gui2
30 {
31 
32 // ------------ WIDGET -----------{
33 
34 REGISTER_WIDGET(stacked_widget)
35 
37 {
38  template<typename W>
40  const std::string& id,
41  const bool must_be_active)
42  {
43  // Use base method if find-in-all-layer isn't set.
44  if(!stack.find_in_all_layers_) {
45  return stack.container_base::find(id, must_be_active);
46  }
47 
48  for(unsigned i = 0; i < stack.get_layer_count(); ++i) {
49  if(W* res = stack.get_layer_grid(i)->find(id, must_be_active)) {
50  return res;
51  }
52  }
53 
54  return stack.container_base::find(id, must_be_active);
55  }
56 };
57 
59  : container_base(builder, type())
60  , generator_(generator_base::build(false, false, generator_base::independent, false))
61  , selected_layer_(-1)
62  , find_in_all_layers_(false)
63 {
64 }
65 
67 {
68  return true;
69 }
70 
71 unsigned stacked_widget::get_state() const
72 {
73  return 0;
74 }
75 
77 {
78  assert(generator_);
79  for(unsigned i = 0; i < generator_->get_item_count(); ++i) {
81  }
82 }
83 
84 void stacked_widget::finalize(const std::vector<builder_grid>& widget_builders)
85 {
86  assert(generator_);
87  string_map empty_data;
88  for(const auto & builder : widget_builders)
89  {
90  generator_->create_item(-1, builder, empty_data, nullptr);
91  }
92  swap_grid(nullptr, &get_grid(), generator_, "_content_grid");
93 
94  select_layer(-1);
95 }
96 
97 void stacked_widget::set_self_active(const bool /*active*/)
98 {
99  /* DO NOTHING */
100 }
101 
102 void stacked_widget::select_layer_impl(std::function<bool(unsigned int i)> display_condition)
103 {
104  const unsigned int num_layers = get_layer_count();
105 
106  // Deselect all layers except the chosen ones.
107  for(unsigned int i = 0; i < num_layers; ++i) {
108  const bool selected = display_condition(i);
109 
110  /* Selecting a previously selected item will deselect it, regardless of the what is passed to
111  * select_item. This causes issues if this function is called when all layers are visible (for
112  * example, initialization). For layers other than the chosen one, this is the desired behavior.
113  * However the chosen layer could *also* be deselected undesirably due to the conditions outlined
114  * above, and as this widget's generator does not stipulate a minimum selection, it's possible to
115  * end up with no layers visible at all.
116  *
117  * This works around that by performing no selection unless necessary to change states.
118  */
119  if(generator_->is_selected(i) != selected) {
120  generator_->select_item(i, selected);
121  }
122  }
123 
124  // If we already have our chosen layers, exit.
125  if(selected_layer_ >= 0) {
126  return;
127  }
128 
129  // Else, re-show all layers.
130  for(unsigned int i = 0; i < num_layers; ++i) {
131  /* By design, only the last selected item will receive events even if multiple items are visible
132  * and said item is not at the top of the stack. If this point is reached, all layers have already
133  * been hidden by the loop above, so the last layer selected will be the top-most one, as desired.
134  */
135  generator_->select_item(i, true);
136  }
137 }
138 
140 {
141  selected_layer_ = std::clamp<int>(i, -1, get_layer_count() - 1);
142 }
143 
144 bool stacked_widget::layer_selected(const unsigned layer)
145 {
146  assert(layer < get_layer_count());
147  return generator_->is_selected(layer);
148 }
149 
150 void stacked_widget::select_layer(const int layer)
151 {
153 
154  select_layer_impl([this](unsigned int i)
155  {
156  return i == static_cast<unsigned int>(selected_layer_);
157  });
158 }
159 
160 void stacked_widget::select_layers(const boost::dynamic_bitset<>& mask)
161 {
162  assert(mask.size() == get_layer_count());
163 
164  select_layer_impl([&](unsigned int i)
165  {
166  if(mask[i]) {
168  }
169 
170  return mask[i];
171  });
172 }
173 
175 {
176  return generator_->get_item_count();
177 }
178 
180 {
181  assert(generator_);
182  return &generator_->item(i);
183 }
184 
185 const grid* stacked_widget::get_layer_grid(unsigned int i) const
186 {
187  assert(generator_);
188  return &generator_->item(i);
189 }
190 
191 widget* stacked_widget::find(const std::string& id, const bool must_be_active)
192 {
193  return stacked_widget_implementation::find<widget>(*this, id, must_be_active);
194 }
195 
196 const widget* stacked_widget::find(const std::string& id, const bool must_be_active) const
197 {
198  return stacked_widget_implementation::find<const widget>(*this, id, must_be_active);
199 }
200 
201 // }---------- DEFINITION ---------{
202 
205 {
206  DBG_GUI_P << "Parsing stacked widget " << id << '\n';
207 
208  load_resolutions<resolution>(cfg);
209 }
210 
212  : resolution_definition(cfg), grid(nullptr)
213 {
214  // Add a dummy state since every widget needs a state.
215  static config dummy("draw");
216  state.emplace_back(dummy);
217 
218  const config& child = cfg.child("grid");
219  VALIDATE(child, _("No grid defined."));
220 
221  grid = std::make_shared<builder_grid>(child);
222 }
223 
224 // }---------- BUILDER -----------{
225 
226 namespace implementation
227 {
228 
229 builder_stacked_widget::builder_stacked_widget(const config& real_cfg)
230  : builder_styled_widget(real_cfg), stack()
231 {
232  const config& cfg = real_cfg.has_child("stack") ? real_cfg.child("stack") : real_cfg;
233  if(&cfg != &real_cfg) {
234  lg::wml_error() << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.\n";
235  }
236  VALIDATE(cfg.has_child("layer"), _("No stack layers defined."));
237  for(const auto & layer : cfg.child_range("layer"))
238  {
239  stack.emplace_back(layer);
240  }
241 }
242 
244 {
245  stacked_widget* widget = new stacked_widget(*this);
246 
247  DBG_GUI_G << "Window builder: placed stacked widget '" << id
248  << "' with definition '" << definition << "'.\n";
249 
250  const auto conf = widget->cast_config_to<stacked_widget_definition>();
251  assert(conf);
252 
253  widget->init_grid(*conf->grid);
254 
255  widget->finalize(stack);
256 
257  return widget;
258 }
259 
260 } // namespace implementation
261 
262 // }------------ END --------------
263 
264 } // namespace gui2
Base class of a resolution, contains the common keys for a resolution.
#define DBG_GUI_P
Definition: log.hpp:65
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:414
std::vector< state_definition > state
virtual unsigned get_item_count() const =0
Returns the number of items.
int dummy
Definition: lstrlib.cpp:1347
void finalize(const std::vector< builder_grid > &widget_builders)
Finishes the building initialization of the widget.
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:406
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
const std::string & id() const
Definition: widget.cpp:109
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:356
Base class for all widgets.
Definition: widget.hpp:49
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.
static std::string _(const char *str)
Definition: gettext.hpp:92
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
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:37
#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
void init_grid(const builder_grid &grid_builder)
Initializes and builds the grid.
std::vector< builder_grid > stack
The builders for all layers of the stack .
stacked_widget(const implementation::builder_stacked_widget &builder)
#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:940
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:288
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.
virtual grid & create_item(const int index, const builder_grid &list_builder, const string_map &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
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.
std::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
A stacked widget holds several widgets on top of each other.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
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.
virtual widget * build() const override
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.