The Battle for Wesnoth  1.19.0-dev
gui_definition.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 "config.hpp"
21 #include "formatter.hpp"
22 #include "gui/core/log.hpp"
24 #include "gui/widgets/settings.hpp"
25 #include "wml_exception.hpp"
26 
27 namespace gui2
28 {
32 
34  : widget_types()
35  , window_types()
36  , id_(cfg["id"])
37  , description_(cfg["description"].t_str())
38  , popup_show_delay_(0)
39  , popup_show_time_(0)
40  , help_show_time_(0)
41  , double_click_time_(0)
42  , repeat_button_repeat_time_(0)
43  , sound_button_click_()
44  , sound_toggle_button_click_()
45  , sound_toggle_panel_click_()
46  , sound_slider_adjust_()
47  , has_helptip_message_()
48  , tips_(tip_of_the_day::load(cfg))
49 {
50  VALIDATE(!id_.empty(), missing_mandatory_wml_key("gui", "id"));
51  VALIDATE(!description_.empty(), missing_mandatory_wml_key("gui", "description"));
52 
53  DBG_GUI_P << "Parsing gui " << id_;
54 
55  //
56  // Widget parsing
57  //
58 
59  /** Parse widget definitions of each registered type. */
60  for(const auto& [type_id, widget_parser] : registered_widget_types()) {
61  auto& def_map = widget_types[type_id];
62 
63  const std::string key = widget_parser.key
64  ? widget_parser.key
65  : type_id + "_definition";
66 
67  bool found_default_def = false;
68 
69  for(const config& definition : cfg.child_range(key)) {
70  // Run the static parser to get a definition ptr.
71  styled_widget_definition_ptr def_ptr = widget_parser.parser(definition);
72 
73  const std::string& def_id = def_ptr->id;
74 
75  if(def_map.find(def_id) != def_map.end()) {
76  ERR_GUI_P << "Skipping duplicate definition '" << def_id << "' for '" << type_id << "'";
77  continue;
78  }
79 
80  def_map.emplace(def_id, std::move(def_ptr));
81 
82  if(def_id == "default") {
83  found_default_def = true;
84  }
85  }
86 
87  // Only the default GUI needs to ensure each widget has a default definition.
88  // Non-default ones can just fall back to the default definition in the default GUI.
89  if(id_ == "default") {
90  VALIDATE(found_default_def, "No default definition found for widget '" + type_id + "'");
91  }
92  }
93 
94  //
95  // Window parsing
96  //
97 
98  /** Parse each window. */
99  for(auto& w : cfg.child_range("window")) {
100  window_types.emplace(w["id"], builder_window(w));
101  }
102 
103  if(id_ == "default") {
104  // The default gui needs to define all window types since we're the
105  // fallback in case another gui doesn't define the window type.
106  for(const auto& window_type : registered_window_types()) {
107  const std::string error_msg(
108  "Window not defined in WML: '" + window_type + "'."
109  "Perhaps a mismatch between data and source versions. Try --data-dir <trunk-dir>");
110 
111  VALIDATE(window_types.find(window_type) != window_types.end(), error_msg);
112  }
113  }
114 
115  /***** settings *****/
116 
117  /**
118  * @todo Regarding sounds:
119  * Need to evaluate but probably we want the widget definition be able to:
120  * - Override the default (and clear it). This will allow toggle buttons in a
121  * listbox to sound like a toggle panel.
122  * - Override the default and above per instance of the widget, some buttons
123  * can give a different sound.
124  */
125  const config& settings = cfg.mandatory_child("settings");
126 
127  popup_show_delay_ = settings["popup_show_delay"];
128  popup_show_time_ = settings["popup_show_time"];
129  help_show_time_ = settings["help_show_time"];
130  double_click_time_ = settings["double_click_time"];
131 
132  repeat_button_repeat_time_ = settings["repeat_button_repeat_time"];
133 
134  VALIDATE(double_click_time_, missing_mandatory_wml_key("settings", "double_click_time"));
135 
136  sound_button_click_ = settings["sound_button_click"].str();
137  sound_toggle_button_click_ = settings["sound_toggle_button_click"].str();
138  sound_toggle_panel_click_ = settings["sound_toggle_panel_click"].str();
139  sound_slider_adjust_ = settings["sound_slider_adjust"].str();
140 
141  has_helptip_message_ = settings["has_helptip_message"];
142 
143  VALIDATE(!has_helptip_message_.empty(), missing_mandatory_wml_key("settings", "has_helptip_message"));
144 }
145 
147 {
159 }
160 
161 namespace
162 {
163 template<typename TList, typename TConv>
164 const typename TList::value_type& get_best_resolution(const TList& list, const TConv& get_size)
165 {
166  using resolution_t = const typename TList::value_type;
167 
168  resolution_t* best_resolution = nullptr;
169  int best_resolution_score = std::numeric_limits<int>::min();
170 
171  const int screen_w = settings::screen_width;
172  const int screen_h = settings::screen_height;
173 
174  for(const auto& res : list) {
175  point size = get_size(res);
176 
177  int w = size.x ? size.x : 1;
178  int h = size.y ? size.y : 1;
179  int score = 0;
180 
181  if(w <= screen_w && h <= screen_h) {
182  score = w * h;
183  } else {
184  // Negative score, only used in case none of the given resolution fits on the screen
185  // (workaround for a bug where the windows size can become < 800x600).
186  score = std::min(screen_w - w, 0) + std::min(screen_h - h, 0);
187  }
188 
189  if(score >= best_resolution_score) {
190  best_resolution = &res;
191  best_resolution_score = score;
192  }
193  }
194 
195  assert(best_resolution != nullptr);
196  return *best_resolution;
197 }
198 
199 } // namespace
200 
201 resolution_definition_ptr get_control(const std::string& control_type, const std::string& definition)
202 {
203  const auto& current_types = current_gui->second.widget_types;
204  const auto& default_types = default_gui->second.widget_types;
205 
206  const auto widget_definitions = current_types.find(control_type);
207 
208  gui_definition::widget_definition_map_t::const_iterator control;
209 
210  if(widget_definitions == current_types.end()) {
211  goto fallback;
212  }
213 
214  control = widget_definitions->second.find(definition);
215 
216  if(control == widget_definitions->second.end()) {
217  fallback:
218  bool found_fallback = false;
219 
220  if(current_gui != default_gui) {
221  auto default_widget_definitions = default_types.find(control_type);
222 
223  VALIDATE(widget_definitions != current_types.end(),
224  formatter() << "Type '" << control_type << "' is unknown.");
225 
226  control = default_widget_definitions->second.find(definition);
227  found_fallback = control != default_widget_definitions->second.end();
228  }
229 
230  if(!found_fallback) {
231  if(definition != "default") {
232  LOG_GUI_G << "Control: type '" << control_type << "' definition '" << definition
233  << "' not found, falling back to 'default'.";
234  return get_control(control_type, "default");
235  }
236 
237  FAIL(formatter() << "default definition not found for styled_widget " << control_type);
238  }
239  }
240 
241  const auto& resolutions = (*control->second).resolutions;
242 
243  VALIDATE(!resolutions.empty(),
244  formatter() << "Control: type '" << control_type << "' definition '" << definition << "' has no resolutions.\n");
245 
246  return get_best_resolution(resolutions, [&](const resolution_definition_ptr& ptr) {
247  return point(
248  static_cast<int>(ptr->window_width),
249  static_cast<int>(ptr->window_height)
250  );
251  });
252 }
253 
255 {
257 
258  const auto& current_windows = current_gui->second.window_types;
259  const auto& default_windows = default_gui->second.window_types;
260 
261  auto iter = current_windows.find(type);
262 
263  if(iter == current_windows.end()) {
264  // Current GUI is the default one and no window type was found. Throw.
265  if(current_gui == default_gui) {
267  }
268 
269  // Else, try again to find the window, this time in the default GUI.
270  iter = default_windows.find(type);
271 
272  if(iter == default_windows.end()) {
274  }
275  }
276 
277  const auto& resolutions = iter->second.resolutions;
278 
279  VALIDATE(!resolutions.empty(), formatter() << "Window '" << type << "' has no resolutions.\n");
280 
281  return get_best_resolution(resolutions, [&](const builder_window::window_resolution& res) {
282  return point(
283  static_cast<int>(res.window_width),
284  static_cast<int>(res.window_height)
285  );
286  });
287 }
288 
289 bool add_single_widget_definition(const std::string& widget_type, const std::string& definition_id, const config& cfg)
290 {
291  auto& def_map = current_gui->second.widget_types[widget_type];
292  auto parser = registered_widget_types().find(widget_type);
293 
294  if(parser == registered_widget_types().end()) {
295  throw std::invalid_argument("widget '" + widget_type + "' doesn't exist");
296  }
297 
298  if(def_map.find(definition_id) != def_map.end()) {
299  return false;
300  }
301 
302  def_map.emplace(definition_id, parser->second.parser(cfg));
303  return true;
304 }
305 
306 void remove_single_widget_definition(const std::string& widget_type, const std::string& definition_id)
307 {
308  auto& definition_map = current_gui->second.widget_types[widget_type];
309 
310  auto it = definition_map.find(definition_id);
311  if(it != definition_map.end()) {
312  definition_map.erase(it);
313  }
314 }
315 
316 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:367
child_itors child_range(config_key_type key)
Definition: config.cpp:273
std::ostringstream wrapper.
Definition: formatter.hpp:40
std::map< std::string, builder_window > window_types
Map of all known windows (the builder class builds a window).
std::map< std::string, widget_definition_map_t > widget_types
Map of each widget type, by id, and a sub-map of each of the type's definitions, also by id.
unsigned repeat_button_repeat_time_
std::vector< game_tip > tips_
std::string sound_toggle_button_click_
std::string sound_slider_adjust_
std::string sound_toggle_panel_click_
gui_definition(const config &cfg)
Private ctor.
std::string sound_button_click_
void activate() const
Activates this GUI.
bool empty() const
Definition: tstring.hpp:186
int w
Define the common log macros for the gui toolkit.
#define LOG_GUI_G
Definition: log.hpp:42
#define ERR_GUI_P
Definition: log.hpp:69
#define DBG_GUI_P
Definition: log.hpp:66
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
t_string has_helptip_message
Definition: settings.cpp:53
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
Definition: settings.cpp:27
std::vector< game_tip > tips
Definition: settings.cpp:55
std::string sound_slider_adjust
Definition: settings.cpp:51
std::string sound_toggle_button_click
Definition: settings.cpp:49
unsigned help_show_time
Definition: settings.cpp:44
unsigned screen_height
Definition: settings.cpp:28
std::string sound_button_click
Definition: settings.cpp:48
unsigned popup_show_delay
These are copied from the active gui.
Definition: settings.cpp:42
unsigned repeat_button_repeat_time
Definition: settings.cpp:46
unsigned double_click_time
Definition: settings.cpp:45
std::string sound_toggle_panel_click
Definition: settings.cpp:50
void update_screen_size_variables()
Update the size of the screen variables in settings.
Definition: settings.cpp:57
unsigned popup_show_time
Definition: settings.cpp:43
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
Generic file dialog.
std::shared_ptr< styled_widget_definition > styled_widget_definition_ptr
bool add_single_widget_definition(const std::string &widget_type, const std::string &definition_id, const config &cfg)
Adds a widget definition to the default GUI.
void remove_single_widget_definition(const std::string &widget_type, const std::string &definition_id)
Removes a widget definition from the default GUI.
std::shared_ptr< resolution_definition > resolution_definition_ptr
const builder_window::window_resolution & get_window_builder(const std::string &type)
Returns an reference to the requested builder.
std::set< std::string > & registered_window_types()
Returns the list of registered windows.
gui_theme_map_t guis
Map of all known GUIs.
std::map< std::string, registered_widget_parser > & registered_widget_types()
Returns the list of registered widgets and their parsers.
std::map< std::string, gui_definition > gui_theme_map_t
gui_theme_map_t::iterator current_gui
Iterator pointing to the current GUI.
gui_theme_map_t::iterator default_gui
Iterator pointing to the default GUI.
resolution_definition_ptr get_control(const std::string &control_type, const std::string &definition)
Returns the appropriate config data for a widget instance fom the active GUI definition.
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition: picture.cpp:813
Contains the general settings which have a default.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
This file contains the settings handling of the widget library.
Helper struct to signal that get_window_builder failed.
Holds a 2D point.
Definition: point.hpp:25
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.
#define h