The Battle for Wesnoth  1.19.0+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 {
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  win->finish_build(definition);
44  return win;
45 }
46 
47 std::unique_ptr<window> build(const std::string& type)
48 {
50  auto window = build(definition);
51  window->set_id(type);
52  return window;
53 }
54 
56  : id(cfg["id"])
57  , linked_group(cfg["linked_group"])
58  , debug_border_mode(widget::debug_border::none)
59  , debug_border_color(decode_color(cfg["debug_border_color"]))
60 {
61  // TODO: move to a `decode` function?
62  switch(const int dbm = cfg["debug_border_mode"].to_int(0); dbm) {
63  case 0:
65  break;
66  case 1:
68  break;
69  case 2:
71  break;
72  default:
73  WRN_GUI_P << "Widget builder: unknown debug border mode " << dbm << ".";
74  }
75 }
76 
78 {
80  VALIDATE(children.size() == 1, "Grid cell does not have exactly 1 child.");
81 
82  if(const auto grid = cfg.optional_child("grid")) {
83  return std::make_shared<builder_grid>(grid.value());
84  }
85 
86  if(const auto instance = cfg.optional_child("instance")) {
87  return std::make_shared<implementation::builder_instance>(instance.value());
88  }
89 
90  if(const auto pane = cfg.optional_child("pane")) {
91  return std::make_shared<implementation::builder_pane>(pane.value());
92  }
93 
94  if(const auto viewport = cfg.optional_child("viewport")) {
95  return std::make_shared<implementation::builder_viewport>(viewport.value());
96  }
97 
98  for(const auto& [type, builder] : widget_builder_lookup()) {
99  if(type == "window" || type == "tooltip") {
100  continue;
101  }
102 
103  if(const auto c = cfg.optional_child(type)) {
104  return builder(c.value());
105  }
106  }
107 
108  // FAIL() doesn't return
109  //
110  // To fix this: add your new widget to source-lists/libwesnoth_widgets and rebuild.
111 
112  FAIL("Unknown widget type " + cfg.ordered_begin()->key);
113 }
114 
115 std::unique_ptr<widget> build_single_widget_instance_helper(const std::string& type, const config& cfg)
116 {
117  const auto& iter = widget_builder_lookup().find(type);
118  VALIDATE(iter != widget_builder_lookup().end(), "Invalid widget type '" + type + "'");
119 
120  widget_builder_func_t& builder = iter->second;
121  return builder(cfg)->build();
122 }
123 
124 void builder_window::read(const config& cfg)
125 {
126  VALIDATE(!id_.empty(), missing_mandatory_wml_key("window", "id"));
127  VALIDATE(!description_.empty(), missing_mandatory_wml_key("window", "description"));
128 
129  DBG_GUI_P << "Window builder: reading data for window " << id_ << ".";
130 
131  config::const_child_itors cfgs = cfg.child_range("resolution");
132  VALIDATE(!cfgs.empty(), _("No resolution defined."));
133 
134  for(const auto& i : cfgs) {
135  resolutions.emplace_back(i);
136  }
137 }
138 
140  : window_width(cfg["window_width"])
141  , window_height(cfg["window_height"])
142  , automatic_placement(cfg["automatic_placement"].to_bool(true))
143  , x(cfg["x"])
144  , y(cfg["y"])
145  , width(cfg["width"])
146  , height(cfg["height"])
147  , reevaluate_best_size(cfg["reevaluate_best_size"])
148  , functions()
149  , vertical_placement(implementation::get_v_align(cfg["vertical_placement"]))
150  , horizontal_placement(implementation::get_h_align(cfg["horizontal_placement"]))
151  , maximum_width(cfg["maximum_width"], 0u)
152  , maximum_height(cfg["maximum_height"], 0u)
153  , click_dismiss(cfg["click_dismiss"].to_bool())
154  , definition(cfg["definition"])
155  , linked_groups()
156  , tooltip(cfg.child_or_empty("tooltip"), "tooltip")
157  , helptip(cfg.child_or_empty("helptip"), "helptip")
158  , grid(nullptr)
159 {
160  if(!cfg["functions"].empty()) {
161  wfl::formula(cfg["functions"], &functions).evaluate();
162  }
163 
164  auto c = cfg.optional_child("grid");
165 
166  VALIDATE(c, _("No grid defined."));
167 
168  grid = std::make_shared<builder_grid>(*c);
169 
170  if(!automatic_placement) {
171  VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width"));
172  VALIDATE(height.has_formula() || height(), missing_mandatory_wml_key("resolution", "height"));
173  }
174 
175  DBG_GUI_P << "Window builder: parsing resolution " << window_width << ',' << window_height;
176 
177  if(definition.empty()) {
178  definition = "default";
179  }
180 
182 }
183 
185  : id(cfg["id"])
186 {
187  VALIDATE(!id.empty(), missing_mandatory_wml_key("[window][resolution][" + tagname + "]", "id"));
188 }
189 
191  : builder_widget(cfg)
192  , rows(0)
193  , cols(0)
194  , row_grow_factor()
195  , col_grow_factor()
196  , flags()
197  , border_size()
198  , widgets()
199 {
200  log_scope2(log_gui_parse, "Window builder: parsing a grid");
201 
202  for(const auto& row : cfg.child_range("row")) {
203  unsigned col = 0;
204 
205  row_grow_factor.push_back(row["grow_factor"]);
206 
207  for(const auto& c : row.child_range("column")) {
208  flags.push_back(implementation::read_flags(c));
209  border_size.push_back(c["border_size"]);
210  if(rows == 0) {
211  col_grow_factor.push_back(c["grow_factor"]);
212  }
213 
214  widgets.push_back(create_widget_builder(c));
215 
216  ++col;
217  }
218 
219  if(col == 0) {
220  const t_string msg = VGETTEXT("Grid '$grid' row $row must have at least one column.", {
221  {"grid", id}, {"row", std::to_string(rows)}
222  });
223 
224  FAIL(msg);
225  }
226 
227  ++rows;
228 
229  if(rows == 1) {
230  cols = col;
231  } else if(col != cols) {
232  const t_string msg = VGETTEXT("Grid '$grid' row $row has a differing number of columns ($found found, $expected expected)", {
233  {"grid", id}, {"row", std::to_string(rows)}, {"found", std::to_string(col)}, {"expected", std::to_string(cols)}
234  });
235 
236  FAIL(msg);
237  }
238  }
239 
240  DBG_GUI_P << "Window builder: grid has " << rows << " rows and " << cols << " columns.";
241 }
242 
243 std::unique_ptr<widget> builder_grid::build() const
244 {
245  auto result = std::make_unique<grid>();
246  build(*result);
247  return result;
248 }
249 
250 std::unique_ptr<widget> builder_grid::build(const replacements_map& replacements) const
251 {
252  auto result = std::make_unique<grid>();
253  build(*result, replacements);
254  return result;
255 }
256 
258 {
259  grid.set_id(id);
262 
263  log_scope2(log_gui_general, "Window builder: building grid");
264 
265  DBG_GUI_G << "Window builder: grid '" << id << "' has " << rows << " rows and " << cols << " columns.";
266 
267  for(unsigned x = 0; x < rows; ++x) {
269 
270  for(unsigned y = 0; y < cols; ++y) {
271  if(x == 0) {
273  }
274 
275  DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".";
276 
277  const unsigned int i = x * cols + y;
278 
279  if(replacements) {
280  auto widget = widgets[i]->build(replacements.value());
281  grid.set_child(std::move(widget), x, y, flags[i], border_size[i]);
282  } else {
283  auto widget = widgets[i]->build();
284  grid.set_child(std::move(widget), x, y, flags[i], border_size[i]);
285  }
286  }
287  }
288 }
289 
290 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const_all_children_iterator ordered_begin() const
Definition: config.cpp:867
boost::iterator_range< const_all_children_iterator > const_all_children_itors
Definition: config.hpp:804
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
child_itors child_range(config_key_type key)
Definition: config.cpp:273
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:283
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
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:42
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
A pane is a container where new members can be added and removed during run-time.
Definition: pane.hpp:41
bool has_formula() const
Determine whether the class contains a formula.
Base class for all widgets.
Definition: widget.hpp:53
void set_id(const std::string &id)
Definition: widget.cpp:98
void set_linked_group(const std::string &linked_group)
Definition: widget.cpp:346
@ outline
Single-pixel outline.
@ fill
Flood-filled rectangle.
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
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:968
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:205
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:275
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:53
std::unique_ptr< widget > build_single_widget_instance_helper(const std::string &type, const config &cfg)
Implementation detail for build_single_widget_instance.
const builder_window::window_resolution & get_window_builder(const std::string &type)
Returns an reference to the requested builder.
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::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
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.