The Battle for Wesnoth  1.17.0-dev
window_builder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 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 
19 #include "formula/string_utils.hpp"
20 #include "gettext.hpp"
21 #include "gui/core/log.hpp"
26 #include "gui/widgets/pane.hpp"
27 #include "gui/widgets/settings.hpp"
28 #include "gui/widgets/viewport.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "wml_exception.hpp"
31 
32 #include <functional>
33 
34 namespace gui2
35 {
36 
37 std::unique_ptr<window> build(const builder_window::window_resolution& definition)
38 {
39  // We set the values from the definition since we can only determine the
40  // best size (if needed) after all widgets have been placed.
41  auto win = std::make_unique<window>(definition);
42  assert(win);
43 
44  for(const auto& lg : definition.linked_groups) {
45  if(win->has_linked_size_group(lg.id)) {
46  t_string msg = VGETTEXT("Linked '$id' group has multiple definitions.", {{"id", lg.id}});
47 
48  FAIL(msg);
49  }
50 
51  win->init_linked_size_group(lg.id, lg.fixed_width, lg.fixed_height);
52  }
53 
54  win->set_click_dismiss(definition.click_dismiss);
55 
56  const auto conf = win->cast_config_to<window_definition>();
57  assert(conf);
58 
59  if(conf->grid) {
60  win->init_grid(*conf->grid);
61  win->finalize(*definition.grid);
62  } else {
63  win->init_grid(*definition.grid);
64  }
65 
66  win->add_to_keyboard_chain(win.get());
67 
68  return win;
69 }
70 
71 std::unique_ptr<window> build(const std::string& type)
72 {
73  const builder_window::window_resolution& definition = get_window_builder(type);
74  auto window = build(definition);
75  window->set_id(type);
76  return window;
77 }
78 
80  : id(cfg["id"])
81  , linked_group(cfg["linked_group"])
82  , debug_border_mode(widget::debug_border::none)
83  , debug_border_color(decode_color(cfg["debug_border_color"]))
84 {
85  // TODO: move to a `decode` function?
86  switch(const int dbm = cfg["debug_border_mode"].to_int(0); dbm) {
87  case 0:
89  break;
90  case 1:
92  break;
93  case 2:
95  break;
96  default:
97  WRN_GUI_P << "Widget builder: unknown debug border mode " << dbm << ".\n";
98  }
99 }
100 
102 {
104  VALIDATE(children.size() == 1, "Grid cell does not have exactly 1 child.");
105 
106  if(const auto grid = cfg.optional_child("grid")) {
107  return std::make_shared<builder_grid>(grid.value());
108  }
109 
110  if(const auto instance = cfg.optional_child("instance")) {
111  return std::make_shared<implementation::builder_instance>(instance.value());
112  }
113 
114  if(const auto pane = cfg.optional_child("pane")) {
115  return std::make_shared<implementation::builder_pane>(pane.value());
116  }
117 
118  if(const auto viewport = cfg.optional_child("viewport")) {
119  return std::make_shared<implementation::builder_viewport>(viewport.value());
120  }
121 
122  for(const auto& [type, builder] : widget_builder_lookup()) {
123  if(type == "window" || type == "tooltip") {
124  continue;
125  }
126 
127  if(const auto c = cfg.optional_child(type)) {
128  return builder(c.value());
129  }
130  }
131 
132  // FAIL() doesn't return
133  //
134  // To fix this: add your new widget to source-lists/libwesnoth_widgets and rebuild.
135 
136  FAIL("Unknown widget type " + cfg.ordered_begin()->key);
137 }
138 
139 widget* build_single_widget_instance_helper(const std::string& type, const config& cfg)
140 {
141  const auto& iter = widget_builder_lookup().find(type);
142  VALIDATE(iter != widget_builder_lookup().end(), "Invalid widget type '" + type + "'");
143 
144  widget_builder_func_t& builder = iter->second;
145  return builder(cfg)->build();
146 }
147 
148 void builder_window::read(const config& cfg)
149 {
150  VALIDATE(!id_.empty(), missing_mandatory_wml_key("window", "id"));
151  VALIDATE(!description_.empty(), missing_mandatory_wml_key("window", "description"));
152 
153  DBG_GUI_P << "Window builder: reading data for window " << id_ << ".\n";
154 
155  config::const_child_itors cfgs = cfg.child_range("resolution");
156  VALIDATE(!cfgs.empty(), _("No resolution defined."));
157 
158  for(const auto& i : cfgs) {
159  resolutions.emplace_back(i);
160  }
161 }
162 
164  : window_width(cfg["window_width"])
165  , window_height(cfg["window_height"])
166  , automatic_placement(cfg["automatic_placement"].to_bool(true))
167  , x(cfg["x"])
168  , y(cfg["y"])
169  , width(cfg["width"])
170  , height(cfg["height"])
171  , reevaluate_best_size(cfg["reevaluate_best_size"])
172  , functions()
173  , vertical_placement(implementation::get_v_align(cfg["vertical_placement"]))
174  , horizontal_placement(implementation::get_h_align(cfg["horizontal_placement"]))
175  , maximum_width(cfg["maximum_width"], 0u)
176  , maximum_height(cfg["maximum_height"], 0u)
177  , click_dismiss(cfg["click_dismiss"].to_bool())
178  , definition(cfg["definition"])
179  , linked_groups()
180  , tooltip(cfg.child_or_empty("tooltip"), "tooltip")
181  , helptip(cfg.child_or_empty("helptip"), "helptip")
182  , grid(nullptr)
183 {
184  if(!cfg["functions"].empty()) {
185  wfl::formula(cfg["functions"], &functions).evaluate();
186  }
187 
188  const config& c = cfg.child("grid");
189 
190  VALIDATE(c, _("No grid defined."));
191 
192  grid = std::make_shared<builder_grid>(builder_grid(c));
193 
194  if(!automatic_placement) {
195  VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width"));
196  VALIDATE(height.has_formula() || height(), missing_mandatory_wml_key("resolution", "height"));
197  }
198 
199  DBG_GUI_P << "Window builder: parsing resolution " << window_width << ',' << window_height << '\n';
200 
201  if(definition.empty()) {
202  definition = "default";
203  }
204 
206 }
207 
209  : id(cfg["id"])
210 {
211  VALIDATE(!id.empty(), missing_mandatory_wml_key("[window][resolution][" + tagname + "]", "id"));
212 }
213 
215  : builder_widget(cfg)
216  , rows(0)
217  , cols(0)
218  , row_grow_factor()
219  , col_grow_factor()
220  , flags()
221  , border_size()
222  , widgets()
223 {
224  log_scope2(log_gui_parse, "Window builder: parsing a grid");
225 
226  for(const auto& row : cfg.child_range("row")) {
227  unsigned col = 0;
228 
229  row_grow_factor.push_back(row["grow_factor"]);
230 
231  for(const auto& c : row.child_range("column")) {
232  flags.push_back(implementation::read_flags(c));
233  border_size.push_back(c["border_size"]);
234  if(rows == 0) {
235  col_grow_factor.push_back(c["grow_factor"]);
236  }
237 
238  widgets.push_back(create_widget_builder(c));
239 
240  ++col;
241  }
242 
243  if(col == 0) {
244  const t_string msg = VGETTEXT("Grid '$grid' row $row must have at least one column.", {
245  {"grid", id}, {"row", std::to_string(rows)}
246  });
247 
248  FAIL(msg);
249  }
250 
251  ++rows;
252 
253  if(rows == 1) {
254  cols = col;
255  } else if(col != cols) {
256  const t_string msg = VGETTEXT("Grid '$grid' row $row has a differing number of columns ($found found, $expected expected)", {
257  {"grid", id}, {"row", std::to_string(rows)}, {"found", std::to_string(col)}, {"expected", std::to_string(cols)}
258  });
259 
260  FAIL(msg);
261  }
262  }
263 
264  DBG_GUI_P << "Window builder: grid has " << rows << " rows and " << cols << " columns.\n";
265 }
266 
268 {
269  return build(new grid());
270 }
271 
272 widget* builder_grid::build(const replacements_map& replacements) const
273 {
274  grid* result = new grid();
275  build(*result, replacements);
276  return result;
277 }
278 
280 {
281  grid->set_id(id);
283  grid->set_rows_cols(rows, cols);
284 
285  log_scope2(log_gui_general, "Window builder: building grid");
286 
287  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.\n";
288 
289  for(unsigned x = 0; x < rows; ++x) {
291 
292  for(unsigned y = 0; y < cols; ++y) {
293  if(x == 0) {
295  }
296 
297  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
298 
299  const unsigned int i = x * cols + y;
300 
301  widget* widget = widgets[i]->build();
302  grid->set_child(widget, x, y, flags[i], border_size[i]);
303  }
304  }
305 
306  return grid;
307 }
308 
309 void builder_grid::build(grid& grid, const replacements_map& replacements) const
310 {
311  grid.set_id(id);
313  grid.set_rows_cols(rows, cols);
314 
315  log_scope2(log_gui_general, "Window builder: building grid");
316 
317  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.\n";
318 
319  for(unsigned x = 0; x < rows; ++x) {
321 
322  for(unsigned y = 0; y < cols; ++y) {
323  if(x == 0) {
325  }
326 
327  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
328 
329  const unsigned int i = x * cols + y;
330  grid.set_child(widgets[i]->build(replacements), x, y, flags[i], border_size[i]);
331  }
332  }
333 }
334 
335 } // namespace gui2
Define the common log macros for the gui toolkit.
Contains the info needed to instantiate a widget.
#define DBG_GUI_P
Definition: log.hpp:65
widget::debug_border debug_border_mode
virtual grid * build() const override
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
const builder_window::window_resolution & get_window_builder(const std::string &type)
Returns an reference to the requested builder.
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:953
unsigned read_flags(const config &cfg)
Returns the placement/resize flags.
Definition: helper.cpp:87
Key Type Default Description window_width unsigned 0 Width of the application window.
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:39
void set_row_grow_factor(const unsigned row, const unsigned factor)
Sets the grow factor for a row.
Definition: grid.hpp:86
boost::iterator_range< const_all_children_iterator > const_all_children_itors
Definition: config.hpp:688
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
This file contains the window object, this object is a top level container which has the event manage...
child_itors child_range(config_key_type key)
Definition: config.cpp:356
Base class for all widgets.
Definition: widget.hpp:48
std::vector< linked_group_definition > parse_linked_group_definitions(const config &cfg)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::function< builder_widget_ptr(const config &)> widget_builder_func_t
Function type alias for register_widget_builder.
static std::string _(const char *str)
Definition: gettext.hpp:92
Flood-filled rectangle.
std::shared_ptr< builder_widget > builder_widget_ptr
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key.
Generic file dialog.
Definition: field-fwd.hpp:22
std::vector< linked_group_definition > linked_groups
Base container class.
Definition: grid.hpp:30
std::map< std::string, std::shared_ptr< builder_widget > > replacements_map
The replacements type is used to define replacement types.
lg::log_domain log_gui_parse("gui/parse")
Definition: log.hpp:64
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
std::vector< unsigned > row_grow_factor
The grow factor for the rows / columns.
This file contains the settings handling of the widget library.
std::vector< unsigned > col_grow_factor
widget * build_single_widget_instance_helper(const std::string &type, const config &cfg)
Helper function to implement build_single_widget_instance.
#define log_scope2(domain, description)
Definition: log.hpp:207
utils::optional_reference< config > optional_child(config_key_type key, int n=0)
Euivalent to child, but returns an empty optional if the nth child was not found. ...
Definition: config.cpp:457
tooltip_info(const config &cfg, const std::string &tagname)
A pane is a container where new members can be added and removed during run-time. ...
Definition: pane.hpp:42
builder_grid(const config &cfg)
void set_child(widget *widget, const unsigned row, const unsigned col, const unsigned flags, const unsigned border_size)
Sets a child in the grid.
Definition: grid.cpp:69
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:204
Definition: pump.hpp:39
A viewport is an special widget used to view only a part of the widget it &#39;holds&#39;.
Definition: viewport.hpp:44
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:214
std::vector< unsigned > flags
The flags per grid cell.
std::size_t i
Definition: function.cpp:940
void read(const config &cfg)
builder_widget(const config &cfg)
#define WRN_GUI_P
Definition: log.hpp:67
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
unsigned get_v_align(const std::string &v_align)
Returns the vertical alignment.
Definition: helper.cpp:37
lg::log_domain log_gui_general("gui/general")
Definition: log.hpp:39
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:722
bool grid()
Definition: general.cpp:524
const_all_children_iterator ordered_begin() const
Definition: config.cpp:933
widget_builder_map & widget_builder_lookup()
Returns the list of registered widget builders.
void set_id(const std::string &id)
Definition: widget.cpp:97
wfl::function_symbol_table functions
Class to show the tips.
Definition: tooltip.cpp:56
std::vector< builder_widget_ptr > widgets
The widgets per grid cell.
std::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
#define FAIL(message)
void set_column_grow_factor(const unsigned column, const unsigned factor)
Sets the grow factor for a column.
Definition: grid.hpp:101
bool has_formula() const
Determine whether the class contains a formula.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
mock_char c
unsigned get_h_align(const std::string &h_align)
Returns the horizontal alignment.
Definition: helper.cpp:52
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:64
#define DBG_GUI_G
Definition: log.hpp:40
std::vector< unsigned > border_size
The border size per grid cell.
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
void set_linked_group(const std::string &linked_group)
Definition: widget.cpp:344
color_t decode_color(const std::string &color)
Converts a color string to a color.
Definition: helper.cpp:58
builder_widget_ptr create_widget_builder(const config &cfg)
Create a widget builder.