The Battle for Wesnoth  1.15.1+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 "utils/functional.hpp"
33 
34 namespace gui2
35 {
36 /*WIKI
37  * @page = GUIWidgetInstanceWML
38  * @order = 1
39  *
40  * {{Autogenerated}}
41  *
42  * = Widget instance =
43  *
44  * Inside a grid (which is inside all container widgets) a widget is
45  * instantiated. With this instantiation some more variables of a widget can
46  * be tuned. This page will describe what can be tuned.
47  *
48  */
50 {
51  // We set the values from the definition since we can only determine the
52  // best size (if needed) after all widgets have been placed.
53  window* win = new window(definition);
54  assert(win);
55 
56  for(const auto& lg : definition->linked_groups) {
57  if(win->has_linked_size_group(lg.id)) {
58  t_string msg = VGETTEXT("Linked '$id' group has multiple definitions.", {{"id", lg.id}});
59 
60  FAIL(msg);
61  }
62 
63  win->init_linked_size_group(lg.id, lg.fixed_width, lg.fixed_height);
64  }
65 
66  win->set_click_dismiss(definition->click_dismiss);
67 
68  const auto conf = win->cast_config_to<window_definition>();
69  assert(conf);
70 
71  if(conf->grid) {
72  win->init_grid(conf->grid);
73  win->finalize(definition->grid);
74  } else {
75  win->init_grid(definition->grid);
76  }
77 
78  win->add_to_keyboard_chain(win);
79 
80  return win;
81 }
82 
83 window* build(const std::string& type)
84 {
85  const builder_window::window_resolution& definition = get_window_builder(type);
86  window* window = build(&definition);
87  window->set_id(type);
88  return window;
89 }
90 
92  : id(cfg["id"])
93  , linked_group(cfg["linked_group"])
94  , debug_border_mode(cfg["debug_border_mode"])
95  , debug_border_color(decode_color(cfg["debug_border_color"]))
96 {
97 }
98 
100 {
102  VALIDATE(children.size() == 1, "Grid cell does not have exactly 1 child.");
103 
104  if(const config& grid = cfg.child("grid")) {
105  return std::make_shared<builder_grid>(grid);
106  }
107 
108  if(const config& instance = cfg.child("instance")) {
109  return std::make_shared<implementation::builder_instance>(instance);
110  }
111 
112  if(const config& pane = cfg.child("pane")) {
113  return std::make_shared<implementation::builder_pane>(pane);
114  }
115 
116  if(const config& viewport = cfg.child("viewport")) {
117  return std::make_shared<implementation::builder_viewport>(viewport);
118  }
119 
120  for(const auto& item : widget_builder_lookup()) {
121  if(item.first == "window" || item.first == "tooltip") {
122  continue;
123  }
124 
125  if(const config& c = cfg.child(item.first)) {
126  return item.second(c);
127  }
128  }
129 
130  // FAIL() doesn't return
131  //
132  // To fix this: add your new widget to source-lists/libwesnoth_widgets and rebuild.
133 
134  FAIL("Unknown widget type " + cfg.ordered_begin()->key);
135 }
136 
137 widget* build_single_widget_instance_helper(const std::string& type, const config& cfg)
138 {
139  const auto& iter = widget_builder_lookup().find(type);
140  VALIDATE(iter != widget_builder_lookup().end(), "Invalid widget type '" + type + "'");
141 
142  widget_builder_func_t& builder = iter->second;
143  return builder(cfg)->build();
144 }
145 
146 /*WIKI
147  * @page = GUIToolkitWML
148  * @order = 1_window
149  * @begin{parent}{name="gui/"}
150  * = Window definition =
151  * @begin{tag}{name="window"}{min="0"}{max="-1"}
152  *
153  * A window defines how a window looks in the game.
154  *
155  * @begin{table}{config}
156  * id & string & & Unique id for this window. $
157  * description & t_string & & Unique translatable name for this
158  * window. $
159  *
160  * resolution & section & & The definitions of the window in various
161  * resolutions. $
162  * @end{table}
163  * @end{tag}{name="window"}
164  * @end{parent}{name="gui/"}
165  *
166  *
167  */
168 void builder_window::read(const config& cfg)
169 {
170  VALIDATE(!id_.empty(), missing_mandatory_wml_key("window", "id"));
171  VALIDATE(!description_.empty(), missing_mandatory_wml_key("window", "description"));
172 
173  DBG_GUI_P << "Window builder: reading data for window " << id_ << ".\n";
174 
175  config::const_child_itors cfgs = cfg.child_range("resolution");
176  VALIDATE(!cfgs.empty(), _("No resolution defined."));
177 
178  for(const auto& i : cfgs) {
179  resolutions.emplace_back(i);
180  }
181 }
182 
183 /*WIKI
184  * @page = GUIToolkitWML
185  * @order = 1_window
186  * @begin{parent}{name=gui/window/}
187  * == Resolution ==
188  * @begin{tag}{name="resolution"}{min="0"}{max="-1"}
189  * @begin{table}{config}
190  * window_width & unsigned & 0 & Width of the application window. $
191  * window_height & unsigned & 0 & Height of the application window. $
192  *
193  *
194  * automatic_placement & bool & true &
195  * Automatically calculate the best size for the window and place it. If
196  * automatically placed ''vertical_placement'' and ''horizontal_placement''
197  * can be used to modify the final placement. If not automatically placed
198  * the ''width'' and ''height'' are mandatory. $
199  *
200  *
201  * x & f_unsigned & 0 & X coordinate of the window to show. $
202  * y & f_unsigned & 0 & Y coordinate of the window to show. $
203  * width & f_unsigned & 0 & Width of the window to show. $
204  * height & f_unsigned & 0 & Height of the window to show. $
205  *
206  * reevaluate_best_size & f_bool & false &
207  * The foo $
208  *
209  * functions & function & "" &
210  * The function definitions s available for the formula fields in window. $
211  *
212  * vertical_placement & v_align & "" &
213  * The vertical placement of the window. $
214  *
215  * horizontal_placement & h_align & "" &
216  * The horizontal placement of the window. $
217  *
218  *
219  * maximum_width & unsigned & 0 &
220  * The maximum width of the window (only used for automatic placement). $
221  *
222  * maximum_height & unsigned & 0 &
223  * The maximum height of the window (only used for automatic placement). $
224  *
225  *
226  * click_dismiss & bool & false &
227  * Does the window need click dismiss behavior? Click dismiss behavior
228  * means that any mouse click will close the dialog. Note certain widgets
229  * will automatically disable this behavior since they need to process the
230  * clicks as well, for example buttons do need a click and a misclick on
231  * button shouldn't close the dialog. NOTE with some widgets this behavior
232  * depends on their contents (like scrolling labels) so the behavior might
233  * get changed depending on the data in the dialog. NOTE the default
234  * behavior might be changed since it will be disabled when can't be used
235  * due to widgets which use the mouse, including buttons, so it might be
236  * wise to set the behavior explicitly when not wanted and no mouse using
237  * widgets are available. This means enter, escape or an external source
238  * needs to be used to close the dialog (which is valid). $
239  *
240  *
241  * definition & string & "default" &
242  * Definition of the window which we want to show. $
243  *
244  *
245  * linked_group & sections & [] & A group of linked widget sections. $
246  *
247  *
248  * tooltip & section & &
249  * Information regarding the tooltip for this window. $
250  *
251  * helptip & section & &
252  * Information regarding the helptip for this window. $
253  *
254  *
255  * grid & grid & & The grid with the widgets to show. $
256  * @end{table}
257  * @begin{tag}{name="linked_group"}{min=0}{max=-1}
258  * A linked_group section has the following fields:
259  * @begin{table}{config}
260  * id & string & & The unique id of the group (unique in this
261  * window). $
262  * fixed_width & bool & false & Should widget in this group have the same
263  * width. $
264  * fixed_height & bool & false & Should widget in this group have the same
265  * height. $
266  * @end{table}
267  * @end{tag}{name="linked_group"}
268  * A linked group needs to have at least one size fixed.
269  * All widgets that are defined with linked_group=foo, where foo is the id of
270  * the linked_group, will have the same width (if fixed_width) and the same
271  * height (if fixed_height).
272  * @begin{tag}{name="tooltip"}{min=0}{max=1}
273  * A tooltip and helptip section have the following field:
274  * @begin{table}{config}
275  * id & string & & The id of the tip to show.
276  * Note more fields will probably be added later on.
277  * @end{table}{config}
278  * @end{tag}{name=tooltip}
279  * @begin{tag}{name="foreground"}{min=0}{max=1}
280  * @end{tag}{name="foreground"}
281  * @begin{tag}{name="background"}{min=0}{max=1}
282  * @end{tag}{name="background"}
283  * @end{tag}{name="resolution"}
284  * @end{parent}{name=gui/window/}
285  * @begin{parent}{name=gui/window/resolution/}
286  * @begin{tag}{name="helptip"}{min=0}{max=1}{super="gui/window/resolution/tooltip"}
287  * @end{tag}{name="helptip"}
288  * @end{parent}{name=gui/window/resolution/}
289  */
291  : window_width(cfg["window_width"])
292  , window_height(cfg["window_height"])
293  , automatic_placement(cfg["automatic_placement"].to_bool(true))
294  , x(cfg["x"])
295  , y(cfg["y"])
296  , width(cfg["width"])
297  , height(cfg["height"])
298  , reevaluate_best_size(cfg["reevaluate_best_size"])
299  , functions()
300  , vertical_placement(implementation::get_v_align(cfg["vertical_placement"]))
301  , horizontal_placement(implementation::get_h_align(cfg["horizontal_placement"]))
302  , maximum_width(cfg["maximum_width"])
303  , maximum_height(cfg["maximum_height"])
304  , click_dismiss(cfg["click_dismiss"].to_bool())
305  , definition(cfg["definition"])
306  , linked_groups()
307  , tooltip(cfg.child_or_empty("tooltip"), "tooltip")
308  , helptip(cfg.child_or_empty("helptip"), "helptip")
309  , grid(nullptr)
310 {
311  if(!cfg["functions"].empty()) {
312  wfl::formula(cfg["functions"], &functions).evaluate();
313  }
314 
315  const config& c = cfg.child("grid");
316 
317  VALIDATE(c, _("No grid defined."));
318 
319  grid = std::make_shared<builder_grid>(builder_grid(c));
320 
321  if(!automatic_placement) {
322  VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width"));
323  VALIDATE(height.has_formula() || height(), missing_mandatory_wml_key("resolution", "height"));
324  }
325 
326  DBG_GUI_P << "Window builder: parsing resolution " << window_width << ',' << window_height << '\n';
327 
328  if(definition.empty()) {
329  definition = "default";
330  }
331 
333 }
334 
336  : id(cfg["id"])
337 {
338  VALIDATE(!id.empty(), missing_mandatory_wml_key("[window][resolution][" + tagname + "]", "id"));
339 }
340 
341 /*WIKI
342  * @page = GUIToolkitWML
343  * @order = 2_cell
344  * @begin{parent}{name="gui/window/resolution/"}
345  * = Cell =
346  * @begin{tag}{name="grid"}{min="1"}{max="1"}
347  * @begin{table}{config}
348  * id & string & "" & A grid is a widget and can have an id. This isn't
349  * used that often, but is allowed. $
350  * linked_group & string & 0 & $
351  * @end{table}
352  *
353  * Every grid cell has some cell configuration values and one widget in the grid
354  * cell. Here we describe the what is available more information about the usage
355  * can be found here [[GUILayout]].
356  *
357  * == Row values ==
358  * @begin{tag}{name="row"}{min="0"}{max="-1"}
359  * For every row the following variables are available:
360  *
361  * @begin{table}{config}
362  * grow_factor & unsigned & 0 & The grow factor for a row. $
363  * @end{table}
364  *
365  * == Cell values ==
366  * @begin{tag}{name="column"}{min="0"}{max="-1"}
367  * @allow{link}{name="gui/window/resolution/grid"}
368  * For every column the following variables are available:
369  * @begin{table}{config}
370  * grow_factor & unsigned & 0 & The grow factor for a column, this
371  * value is only read for the first row. $
372  *
373  * border_size & unsigned & 0 & The border size for this grid cell. $
374  * border & border & "" & Where to place the border in this grid
375  * cell. $
376  *
377  * vertical_alignment & v_align & "" &
378  * The vertical alignment of the widget in
379  * the grid cell. (This value is ignored if
380  * vertical_grow is true.) $
381  * horizontal_alignment & h_align & "" &
382  * The horizontal alignment of the widget in
383  * the grid cell.(This value is ignored if
384  * horizontal_grow is true.) $
385  *
386  * vertical_grow & bool & false & Does the widget grow in vertical
387  * direction when the grid cell grows in the
388  * vertical direction. This is used if the
389  * grid cell is wider as the best width for
390  * the widget. $
391  * horizontal_grow & bool & false & Does the widget grow in horizontal
392  * direction when the grid cell grows in the
393  * horizontal direction. This is used if the
394  * grid cell is higher as the best width for
395  * the widget. $
396  * @end{table}
397  * @end{tag}{name="column"}
398  * @end{tag}{name="row"}
399  * @end{tag}{name="grid"}
400  * @end{parent}{name="gui/window/resolution/"}
401  *
402  */
404  : builder_widget(cfg)
405  , rows(0)
406  , cols(0)
407  , row_grow_factor()
408  , col_grow_factor()
409  , flags()
410  , border_size()
411  , widgets()
412 {
413  log_scope2(log_gui_parse, "Window builder: parsing a grid");
414 
415  for(const auto& row : cfg.child_range("row")) {
416  unsigned col = 0;
417 
418  row_grow_factor.push_back(row["grow_factor"]);
419 
420  for(const auto& c : row.child_range("column")) {
421  flags.push_back(implementation::read_flags(c));
422  border_size.push_back(c["border_size"]);
423  if(rows == 0) {
424  col_grow_factor.push_back(c["grow_factor"]);
425  }
426 
427  widgets.push_back(create_widget_builder(c));
428 
429  ++col;
430  }
431 
432  ++rows;
433  if(rows == 1) {
434  cols = col;
435  } else {
436  VALIDATE(col, _("A row must have a column."));
437  VALIDATE(col == cols, _("Number of columns differ."));
438  }
439  }
440 
441  DBG_GUI_P << "Window builder: grid has " << rows << " rows and " << cols << " columns.\n";
442 }
443 
445 {
446  return build(new grid());
447 }
448 
449 widget* builder_grid::build(const replacements_map& replacements) const
450 {
451  grid* result = new grid();
452  build(*result, replacements);
453  return result;
454 }
455 
457 {
458  grid->set_id(id);
460  grid->set_rows_cols(rows, cols);
461 
462  log_scope2(log_gui_general, "Window builder: building grid");
463 
464  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.\n";
465 
466  for(unsigned x = 0; x < rows; ++x) {
468 
469  for(unsigned y = 0; y < cols; ++y) {
470  if(x == 0) {
472  }
473 
474  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
475 
476  const unsigned int i = x * cols + y;
477 
478  widget* widget = widgets[i]->build();
479  grid->set_child(widget, x, y, flags[i], border_size[i]);
480  }
481  }
482 
483  return grid;
484 }
485 
486 void builder_grid::build(grid& grid, const replacements_map& replacements) const
487 {
488  grid.set_id(id);
490  grid.set_rows_cols(rows, cols);
491 
492  log_scope2(log_gui_general, "Window builder: building grid");
493 
494  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.\n";
495 
496  for(unsigned x = 0; x < rows; ++x) {
498 
499  for(unsigned y = 0; y < cols; ++y) {
500  if(x == 0) {
502  }
503 
504  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
505 
506  const unsigned int i = x * cols + y;
507  grid.set_child(widgets[i]->build(replacements), x, y, flags[i], border_size[i]);
508  }
509  }
510 }
511 
512 } // namespace gui2
513 
514 /*WIKI
515  * @page = GUIToolkitWML
516  * @order = ZZZZZZ_footer
517  *
518  * [[Category: WML Reference]]
519  * [[Category: GUI WML Reference]]
520  */
521 
522 /*WIKI
523  * @page = GUIWidgetInstanceWML
524  * @order = ZZZZZZ_footer
525  *
526  * [[Category: WML Reference]]
527  * [[Category: GUI WML Reference]]
528  *
529  */
Define the common log macros for the gui toolkit.
Contains the info needed to instantiate a widget.
#define DBG_GUI_P
Definition: log.hpp:68
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
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:921
unsigned read_flags(const config &cfg)
Returns the placement/resize flags.
Definition: helper.cpp:87
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:668
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:362
Base class for all widgets.
Definition: widget.hpp:47
void set_click_dismiss(const bool click_dismiss)
Definition: window.hpp:381
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.
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1288
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
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
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:67
#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.
void finalize(const std::shared_ptr< builder_grid > &content_grid)
Finishes the initialization of the grid.
Definition: window.cpp:1189
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:187
void init_linked_size_group(const std::string &id, const bool fixed_width, const bool fixed_height)
Initializes a linked size group.
Definition: window.cpp:834
tooltip_info(const config &cfg, const std::string &tagname)
void init_grid(const std::shared_ptr< builder_grid > &grid_builder)
Initializes and builds the grid.
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:238
Definition: pump.hpp:39
std::vector< unsigned > flags
The flags per grid cell.
std::size_t i
Definition: function.cpp:933
void read(const config &cfg)
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...
builder_widget(const config &cfg)
window * build(const builder_window::window_resolution *definition)
Builds a window.
bool has_linked_size_group(const std::string &id)
Is the linked size group defined for this window?
Definition: window.cpp:844
#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:505
const_all_children_iterator ordered_begin() const
Definition: config.cpp:901
widget_builder_map & widget_builder_lookup()
Returns the list of registered widget builders.
void set_id(const std::string &id)
Definition: widget.cpp:95
wfl::function_symbol_table functions
Class to show the tips.
Definition: tooltip.cpp:71
grid * build() const
std::vector< builder_widget_ptr > widgets
The widgets per grid cell.
#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:92
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:63
#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:342
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:371
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.