The Battle for Wesnoth  1.17.0-dev
window_builder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
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 
20 #include "formula/string_utils.hpp"
21 #include "gettext.hpp"
22 #include "gui/core/log.hpp"
27 #include "gui/widgets/pane.hpp"
28 #include "gui/widgets/settings.hpp"
29 #include "gui/widgets/viewport.hpp"
30 #include "gui/widgets/window.hpp"
31 #include "wml_exception.hpp"
32 
33 #include <functional>
34 
35 namespace gui2
36 {
37 
38 std::unique_ptr<window> build(const builder_window::window_resolution& definition)
39 {
40  // We set the values from the definition since we can only determine the
41  // best size (if needed) after all widgets have been placed.
42  auto win = std::make_unique<window>(definition);
43  assert(win);
44 
45  for(const auto& lg : definition.linked_groups) {
46  if(win->has_linked_size_group(lg.id)) {
47  t_string msg = VGETTEXT("Linked '$id' group has multiple definitions.", {{"id", lg.id}});
48 
49  FAIL(msg);
50  }
51 
52  win->init_linked_size_group(lg.id, lg.fixed_width, lg.fixed_height);
53  }
54 
55  win->set_click_dismiss(definition.click_dismiss);
56 
57  const auto conf = win->cast_config_to<window_definition>();
58  assert(conf);
59 
60  if(conf->grid) {
61  win->init_grid(*conf->grid);
62  win->finalize(*definition.grid);
63  } else {
64  win->init_grid(*definition.grid);
65  }
66 
67  win->add_to_keyboard_chain(win.get());
68 
69  return win;
70 }
71 
72 std::unique_ptr<window> build(const std::string& type)
73 {
74  const builder_window::window_resolution& definition = get_window_builder(type);
75  auto window = build(definition);
76  window->set_id(type);
77  return window;
78 }
79 
81  : id(cfg["id"])
82  , linked_group(cfg["linked_group"])
83  , debug_border_mode(widget::debug_border::none)
84  , debug_border_color(decode_color(cfg["debug_border_color"]))
85 {
86  // TODO: move to a `decode` function?
87  switch(const int dbm = cfg["debug_border_mode"].to_int(0); dbm) {
88  case 0:
90  break;
91  case 1:
93  break;
94  case 2:
96  break;
97  default:
98  WRN_GUI_P << "Widget builder: unknown debug border mode " << dbm << ".\n";
99  }
100 }
101 
103 {
105  VALIDATE(children.size() == 1, "Grid cell does not have exactly 1 child.");
106 
107  if(const auto grid = cfg.optional_child("grid")) {
108  return std::make_shared<builder_grid>(grid.value());
109  }
110 
111  if(const auto instance = cfg.optional_child("instance")) {
112  return std::make_shared<implementation::builder_instance>(instance.value());
113  }
114 
115  if(const auto pane = cfg.optional_child("pane")) {
116  return std::make_shared<implementation::builder_pane>(pane.value());
117  }
118 
119  if(const auto viewport = cfg.optional_child("viewport")) {
120  return std::make_shared<implementation::builder_viewport>(viewport.value());
121  }
122 
123  for(const auto& [type, builder] : widget_builder_lookup()) {
124  if(type == "window" || type == "tooltip") {
125  continue;
126  }
127 
128  if(const auto c = cfg.optional_child(type)) {
129  return builder(c.value());
130  }
131  }
132 
133  // FAIL() doesn't return
134  //
135  // To fix this: add your new widget to source-lists/libwesnoth_widgets and rebuild.
136 
137  FAIL("Unknown widget type " + cfg.ordered_begin()->key);
138 }
139 
140 widget* build_single_widget_instance_helper(const std::string& type, const config& cfg)
141 {
142  const auto& iter = widget_builder_lookup().find(type);
143  VALIDATE(iter != widget_builder_lookup().end(), "Invalid widget type '" + type + "'");
144 
145  widget_builder_func_t& builder = iter->second;
146  return builder(cfg)->build();
147 }
148 
149 void builder_window::read(const config& cfg)
150 {
151  VALIDATE(!id_.empty(), missing_mandatory_wml_key("window", "id"));
152  VALIDATE(!description_.empty(), missing_mandatory_wml_key("window", "description"));
153 
154  DBG_GUI_P << "Window builder: reading data for window " << id_ << ".\n";
155 
156  config::const_child_itors cfgs = cfg.child_range("resolution");
157  VALIDATE(!cfgs.empty(), _("No resolution defined."));
158 
159  for(const auto& i : cfgs) {
160  resolutions.emplace_back(i);
161  }
162 }
163 
165  : window_width(cfg["window_width"])
166  , window_height(cfg["window_height"])
167  , automatic_placement(cfg["automatic_placement"].to_bool(true))
168  , x(cfg["x"])
169  , y(cfg["y"])
170  , width(cfg["width"])
171  , height(cfg["height"])
172  , reevaluate_best_size(cfg["reevaluate_best_size"])
173  , functions()
174  , vertical_placement(implementation::get_v_align(cfg["vertical_placement"]))
175  , horizontal_placement(implementation::get_h_align(cfg["horizontal_placement"]))
176  , maximum_width(cfg["maximum_width"], 0u)
177  , maximum_height(cfg["maximum_height"], 0u)
178  , click_dismiss(cfg["click_dismiss"].to_bool())
179  , definition(cfg["definition"])
180  , linked_groups()
181  , tooltip(cfg.child_or_empty("tooltip"), "tooltip")
182  , helptip(cfg.child_or_empty("helptip"), "helptip")
183  , grid(nullptr)
184 {
185  if(!cfg["functions"].empty()) {
186  wfl::formula(cfg["functions"], &functions).evaluate();
187  }
188 
189  const config& c = cfg.child("grid");
190 
191  VALIDATE(c, _("No grid defined."));
192 
193  grid = std::make_shared<builder_grid>(builder_grid(c));
194 
195  if(!automatic_placement) {
196  VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width"));
197  VALIDATE(height.has_formula() || height(), missing_mandatory_wml_key("resolution", "height"));
198  }
199 
200  DBG_GUI_P << "Window builder: parsing resolution " << window_width << ',' << window_height << '\n';
201 
202  if(definition.empty()) {
203  definition = "default";
204  }
205 
207 }
208 
210  : id(cfg["id"])
211 {
212  VALIDATE(!id.empty(), missing_mandatory_wml_key("[window][resolution][" + tagname + "]", "id"));
213 }
214 
216  : builder_widget(cfg)
217  , rows(0)
218  , cols(0)
219  , row_grow_factor()
220  , col_grow_factor()
221  , flags()
222  , border_size()
223  , widgets()
224 {
225  log_scope2(log_gui_parse, "Window builder: parsing a grid");
226 
227  for(const auto& row : cfg.child_range("row")) {
228  unsigned col = 0;
229 
230  row_grow_factor.push_back(row["grow_factor"]);
231 
232  for(const auto& c : row.child_range("column")) {
233  flags.push_back(implementation::read_flags(c));
234  border_size.push_back(c["border_size"]);
235  if(rows == 0) {
236  col_grow_factor.push_back(c["grow_factor"]);
237  }
238 
239  widgets.push_back(create_widget_builder(c));
240 
241  ++col;
242  }
243 
244  if(col == 0) {
245  const t_string msg = VGETTEXT("Grid '$grid' row $row must have at least one column.", {
246  {"grid", id}, {"row", std::to_string(rows)}
247  });
248 
249  FAIL(msg);
250  }
251 
252  ++rows;
253 
254  if(rows == 1) {
255  cols = col;
256  } else if(col != cols) {
257  const t_string msg = VGETTEXT("Grid '$grid' row $row has a differing number of columns ($found found, $expected expected)", {
258  {"grid", id}, {"row", std::to_string(rows)}, {"found", std::to_string(col)}, {"expected", std::to_string(cols)}
259  });
260 
261  FAIL(msg);
262  }
263  }
264 
265  DBG_GUI_P << "Window builder: grid has " << rows << " rows and " << cols << " columns.\n";
266 }
267 
269 {
270  return build(new grid());
271 }
272 
273 widget* builder_grid::build(const replacements_map& replacements) const
274 {
275  grid* result = new grid();
276  build(*result, replacements);
277  return result;
278 }
279 
281 {
282  grid->set_id(id);
284  grid->set_rows_cols(rows, cols);
285 
286  log_scope2(log_gui_general, "Window builder: building grid");
287 
288  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.\n";
289 
290  for(unsigned x = 0; x < rows; ++x) {
292 
293  for(unsigned y = 0; y < cols; ++y) {
294  if(x == 0) {
296  }
297 
298  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
299 
300  const unsigned int i = x * cols + y;
301 
302  widget* widget = widgets[i]->build();
303  grid->set_child(widget, x, y, flags[i], border_size[i]);
304  }
305  }
306 
307  return grid;
308 }
309 
310 void builder_grid::build(grid& grid, const replacements_map& replacements) const
311 {
312  grid.set_id(id);
314  grid.set_rows_cols(rows, cols);
315 
316  log_scope2(log_gui_general, "Window builder: building grid");
317 
318  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.\n";
319 
320  for(unsigned x = 0; x < rows; ++x) {
322 
323  for(unsigned y = 0; y < cols; ++y) {
324  if(x == 0) {
326  }
327 
328  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
329 
330  const unsigned int i = x * cols + y;
331  grid.set_child(widgets[i]->build(replacements), x, y, flags[i], border_size[i]);
332  }
333  }
334 }
335 
336 } // 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:66
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:402
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:978
unsigned read_flags(const config &cfg)
Returns the placement/resize flags.
Definition: helper.cpp:88
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:40
void set_row_grow_factor(const unsigned row, const unsigned factor)
Sets the grow factor for a row.
Definition: grid.hpp:87
boost::iterator_range< const_all_children_iterator > const_all_children_itors
Definition: config.hpp:719
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:344
Base class for all widgets.
Definition: widget.hpp:49
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:110
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:93
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:23
std::vector< linked_group_definition > linked_groups
Base container class.
Definition: grid.hpp:31
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:65
#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:219
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:445
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:43
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:70
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:206
Definition: pump.hpp:40
A viewport is an special widget used to view only a part of the widget it &#39;holds&#39;.
Definition: viewport.hpp:45
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
std::vector< unsigned > flags
The flags per grid cell.
std::size_t i
Definition: function.cpp:967
void read(const config &cfg)
builder_widget(const config &cfg)
#define WRN_GUI_P
Definition: log.hpp:68
#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:38
lg::log_domain log_gui_general("gui/general")
Definition: log.hpp:40
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:723
bool grid()
Definition: general.cpp:525
const_all_children_iterator ordered_begin() const
Definition: config.cpp:958
widget_builder_map & widget_builder_lookup()
Returns the list of registered widget builders.
void set_id(const std::string &id)
Definition: widget.cpp:98
wfl::function_symbol_table functions
Class to show the tips.
Definition: tooltip.cpp:57
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:102
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:61
mock_char c
unsigned get_h_align(const std::string &h_align)
Returns the horizontal alignment.
Definition: helper.cpp:53
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
#define DBG_GUI_G
Definition: log.hpp:41
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:345
color_t decode_color(const std::string &color)
Converts a color string to a color.
Definition: helper.cpp:59
builder_widget_ptr create_widget_builder(const config &cfg)
Create a widget builder.