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