The Battle for Wesnoth  1.19.5+dev
window_builder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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/viewport.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "wml_exception.hpp"
31 
32 #include <functional>
33 
34 namespace gui2
35 {
37  : id(cfg["id"])
38  , linked_group(cfg["linked_group"])
39  , debug_border_mode(widget::debug_border::none)
40  , debug_border_color(decode_color(cfg["debug_border_color"]))
41 {
42  // TODO: move to a `decode` function?
43  switch(const int dbm = cfg["debug_border_mode"].to_int(0); dbm) {
44  case 0:
46  break;
47  case 1:
49  break;
50  case 2:
52  break;
53  default:
54  WRN_GUI_P << "Widget builder: unknown debug border mode " << dbm << ".";
55  }
56 }
57 
59 {
60  VALIDATE(cfg.all_children_count() == 1, "Grid cell does not have exactly 1 child.");
61  auto [widget_key, widget_cfg] = *cfg.ordered_begin();
62 
63  if(widget_key == "grid") {
64  return std::make_shared<builder_grid>(widget_cfg);
65  }
66 
67  if(widget_key == "instance") {
68  return std::make_shared<implementation::builder_instance>(widget_cfg);
69  }
70 
71  if(widget_key == "pane") {
72  return std::make_shared<implementation::builder_pane>(widget_cfg);
73  }
74 
75  if(widget_key == "viewport") {
76  return std::make_shared<implementation::builder_viewport>(widget_cfg);
77  }
78 
79  for(const auto& [type, builder] : widget_builder_lookup()) {
80  if(type == "window" || type == "tooltip") {
81  continue;
82  }
83 
84  if(widget_key == type) {
85  return builder(widget_cfg);
86  }
87  }
88 
89  // FAIL() doesn't return
90  //
91  // To fix this: add your new widget to source-lists/libwesnoth_widgets and rebuild.
92 
93  FAIL("Unknown widget type " + widget_key);
94 }
95 
96 std::unique_ptr<widget> build_single_widget_instance_helper(const std::string& type, const config& cfg)
97 {
98  const auto& iter = widget_builder_lookup().find(type);
99  VALIDATE(iter != widget_builder_lookup().end(), "Invalid widget type '" + type + "'");
100 
101  widget_builder_func_t& builder = iter->second;
102  return builder(cfg)->build();
103 }
104 
105 void builder_window::read(const config& cfg)
106 {
107  VALIDATE(!id_.empty(), missing_mandatory_wml_key("window", "id"));
108  VALIDATE(!description_.empty(), missing_mandatory_wml_key("window", "description"));
109 
110  DBG_GUI_P << "Window builder: reading data for window " << id_ << ".";
111 
112  config::const_child_itors cfgs = cfg.child_range("resolution");
113  VALIDATE(!cfgs.empty(), _("No resolution defined for ") + id_);
114 
115  for(const auto& i : cfgs) {
116  resolutions.emplace_back(i);
117  }
118 }
119 
121  : window_width(cfg["window_width"].to_unsigned())
122  , window_height(cfg["window_height"].to_unsigned())
123  , automatic_placement(cfg["automatic_placement"].to_bool(true))
124  , x(cfg["x"])
125  , y(cfg["y"])
126  , width(cfg["width"])
127  , height(cfg["height"])
128  , reevaluate_best_size(cfg["reevaluate_best_size"])
129  , functions()
130  , vertical_placement(implementation::get_v_align(cfg["vertical_placement"]))
131  , horizontal_placement(implementation::get_h_align(cfg["horizontal_placement"]))
132  , maximum_width(cfg["maximum_width"], 0u)
133  , maximum_height(cfg["maximum_height"], 0u)
134  , click_dismiss(cfg["click_dismiss"].to_bool())
135  , definition(cfg["definition"])
136  , linked_groups()
137  , tooltip(cfg.child_or_empty("tooltip"), "tooltip")
138  , helptip(cfg.child_or_empty("helptip"), "helptip")
139  , grid(nullptr)
140 {
141  if(!cfg["functions"].empty()) {
142  wfl::formula(cfg["functions"], &functions).evaluate();
143  }
144 
145  auto c = cfg.optional_child("grid");
146 
147  VALIDATE(c, _("No grid defined."));
148 
149  grid = std::make_shared<builder_grid>(*c);
150 
151  if(!automatic_placement) {
152  VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width"));
153  VALIDATE(height.has_formula() || height(), missing_mandatory_wml_key("resolution", "height"));
154  }
155 
156  DBG_GUI_P << "Window builder: parsing resolution " << window_width << ',' << window_height;
157 
158  if(definition.empty()) {
159  definition = "default";
160  }
161 
163 }
164 
166  : id(cfg["id"])
167 {
168  VALIDATE(!id.empty(), missing_mandatory_wml_key("[window][resolution][" + tagname + "]", "id"));
169 }
170 
172  : builder_widget(cfg)
173  , rows(0)
174  , cols(0)
175  , row_grow_factor()
176  , col_grow_factor()
177  , flags()
178  , border_size()
179  , widgets()
180 {
181  log_scope2(log_gui_parse, "Window builder: parsing a grid");
182 
183  for(const auto& row : cfg.child_range("row")) {
184  unsigned col = 0;
185 
186  row_grow_factor.push_back(row["grow_factor"].to_unsigned());
187 
188  for(const auto& c : row.child_range("column")) {
189  flags.push_back(implementation::read_flags(c));
190  border_size.push_back(c["border_size"].to_unsigned());
191  if(rows == 0) {
192  col_grow_factor.push_back(c["grow_factor"].to_unsigned());
193  }
194 
195  widgets.push_back(create_widget_builder(c));
196 
197  ++col;
198  }
199 
200  if(col == 0) {
201  const t_string msg = VGETTEXT("Grid ‘$grid’ row $row must have at least one column.", {
202  {"grid", id}, {"row", std::to_string(rows)}
203  });
204 
205  FAIL(msg);
206  }
207 
208  ++rows;
209 
210  if(rows == 1) {
211  cols = col;
212  } else if(col != cols) {
213  const t_string msg = VGETTEXT("Grid ‘$grid’ row $row has a differing number of columns ($found found, $expected expected)", {
214  {"grid", id}, {"row", std::to_string(rows)}, {"found", std::to_string(col)}, {"expected", std::to_string(cols)}
215  });
216 
217  FAIL(msg);
218  }
219  }
220 
221  DBG_GUI_P << "Window builder: grid has " << rows << " rows and " << cols << " columns.";
222 }
223 
224 std::unique_ptr<widget> builder_grid::build() const
225 {
226  auto result = std::make_unique<grid>();
227  build(*result);
228  return result;
229 }
230 
231 std::unique_ptr<widget> builder_grid::build(const replacements_map& replacements) const
232 {
233  auto result = std::make_unique<grid>();
234  build(*result, replacements);
235  return result;
236 }
237 
239 {
240  grid.set_id(id);
243 
244  log_scope2(log_gui_general, "Window builder: building grid");
245 
246  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.";
247 
248  for(unsigned x = 0; x < rows; ++x) {
250 
251  for(unsigned y = 0; y < cols; ++y) {
252  if(x == 0) {
254  }
255 
256  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".";
257 
258  const unsigned int i = x * cols + y;
259 
260  if(replacements) {
261  auto widget = widgets[i]->build(replacements.value());
262  grid.set_child(std::move(widget), x, y, flags[i], border_size[i]);
263  } else {
264  auto widget = widgets[i]->build();
265  grid.set_child(std::move(widget), x, y, flags[i], border_size[i]);
266  }
267  }
268  }
269 }
270 
271 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
const_all_children_iterator ordered_begin() const
Definition: config.cpp:864
child_itors child_range(config_key_type key)
Definition: config.cpp:272
std::size_t all_children_count() const
Definition: config.cpp:306
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:296
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
std::vector< window_resolution > resolutions
Resolution options for this window instance.
void read(const config &cfg)
At the moment two kinds of tips are known:
Definition: tooltip.cpp:41
Base container class.
Definition: grid.hpp:32
void set_row_grow_factor(const unsigned row, const unsigned factor)
Sets the grow factor for a row.
Definition: grid.hpp:87
void set_column_grow_factor(const unsigned column, const unsigned factor)
Sets the grow factor for a column.
Definition: grid.hpp:102
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:712
void set_child(std::unique_ptr< widget > widget, const unsigned row, const unsigned col, const unsigned flags, const unsigned border_size)
Sets a child in the grid.
Definition: grid.cpp:71
bool has_formula() const
Determine whether the class contains a formula.
Base class for all widgets.
Definition: widget.hpp:55
void set_id(const std::string &id)
Definition: widget.cpp:98
void set_linked_group(const std::string &linked_group)
Definition: widget.cpp:356
@ outline
Single-pixel outline.
@ fill
Flood-filled rectangle.
A simple wrapper class for optional reference types.
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
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1028
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define WRN_GUI_P
Definition: log.hpp:68
#define DBG_GUI_G
Definition: log.hpp:41
#define DBG_GUI_P
Definition: log.hpp:66
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
This file contains the window object, this object is a top level container which has the event manage...
#define log_scope2(domain, description)
Definition: log.hpp:279
unsigned get_v_align(const std::string &v_align)
Returns the vertical alignment.
Definition: helper.cpp:38
unsigned get_h_align(const std::string &h_align)
Returns the horizontal alignment.
Definition: helper.cpp:53
unsigned read_flags(const config &cfg)
Returns the placement/resize flags.
Definition: helper.cpp:88
Generic file dialog.
std::shared_ptr< builder_widget > builder_widget_ptr
color_t decode_color(const std::string &color)
Converts a color string to a color.
Definition: helper.cpp:48
std::unique_ptr< widget > build_single_widget_instance_helper(const std::string &type, const config &cfg)
Implementation detail for build_single_widget_instance.
builder_widget_ptr create_widget_builder(const config &cfg)
Create a widget builder.
std::vector< linked_group_definition > parse_linked_group_definitions(const config &cfg)
std::function< builder_widget_ptr(const config &)> widget_builder_func_t
Function type alias for register_widget_builder.
lg::log_domain log_gui_general("gui/general")
Definition: log.hpp:40
lg::log_domain log_gui_parse("gui/parse")
Definition: log.hpp:65
std::map< std::string, widget_builder_func_t > & widget_builder_lookup()
Returns the list of registered widget builders.
Contains the implementation details for lexical_cast and shouldn't be used directly.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::vector< unsigned > col_grow_factor
std::vector< unsigned > flags
The flags per grid cell.
std::vector< builder_widget_ptr > widgets
The widgets per grid cell.
builder_grid(const config &cfg)
std::vector< unsigned > row_grow_factor
The grow factor for the rows / columns.
virtual std::unique_ptr< widget > build() const override
Inherited from builder_widget.
std::vector< unsigned > border_size
The border size per grid cell.
Contains the info needed to instantiate a widget.
widget::debug_border debug_border_mode
builder_widget(const config &cfg)
std::map< std::string, std::shared_ptr< builder_widget > > replacements_map
The replacements type is used to define replacement types.
tooltip_info(const config &cfg, const std::string &tagname)
std::vector< linked_group_definition > linked_groups
wfl::function_symbol_table functions
mock_char c
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 (attribute).
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define FAIL(message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.