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