The Battle for Wesnoth  1.17.0-dev
gui_definition.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
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_ << std::endl;
54 
55  //
56  // Widget parsing
57  //
58 
59  /** Parse widget definitions of each registered type. */
60  for(auto& widget_type : registered_widget_types()) {
61  const std::string& type_id = widget_type.first;
62 
64 
65  const std::string key = widget_type.second.key
66  ? widget_type.second.key
67  : type_id + "_definition";
68 
69  bool found_default_def = false;
70 
71  for(const config& definition : cfg.child_range(key)) {
72  // Run the static parser to get a definition ptr.
73  styled_widget_definition_ptr def_ptr = widget_type.second.parser(definition);
74 
75  const std::string& def_id = def_ptr->id;
76 
77  if(def_map.find(def_id) != def_map.end()) {
78  ERR_GUI_P << "Skipping duplicate definition '" << def_id << "' for '" << type_id << "'\n";
79  continue;
80  }
81 
82  def_map.emplace(def_id, std::move(def_ptr));
83 
84  if(def_id == "default") {
85  found_default_def = true;
86  }
87  }
88 
89  // Only the default GUI needs to ensure each widget has a default definition.
90  // Non-default ones can just fall back to the default definition in the default GUI.
91  if(id_ == "default") {
92  VALIDATE(found_default_def, "No default definition found for widget '" + type_id + "'");
93  }
94  }
95 
96  //
97  // Window parsing
98  //
99 
100  /** Parse each window. */
101  for(auto& w : cfg.child_range("window")) {
102  window_types.emplace(w["id"], builder_window(w));
103  }
104 
105  if(id_ == "default") {
106  // The default gui needs to define all window types since we're the
107  // fallback in case another gui doesn't define the window type.
108  for(const auto& window_type : registered_window_types()) {
109  const std::string error_msg(
110  "Window not defined in WML: '" + window_type + "'."
111  "Perhaps a mismatch between data and source versions. Try --data-dir <trunk-dir>");
112 
113  VALIDATE(window_types.find(window_type) != window_types.end(), error_msg);
114  }
115  }
116 
117  /***** settings *****/
118 
119  /**
120  * @todo Regarding sounds:
121  * Need to evaluate but probably we want the widget definition be able to:
122  * - Override the default (and clear it). This will allow toggle buttons in a
123  * listbox to sound like a toggle panel.
124  * - Override the default and above per instance of the widget, some buttons
125  * can give a different sound.
126  */
127  const config& settings = cfg.child("settings");
128 
129  popup_show_delay_ = settings["popup_show_delay"];
130  popup_show_time_ = settings["popup_show_time"];
131  help_show_time_ = settings["help_show_time"];
132  double_click_time_ = settings["double_click_time"];
133 
134  repeat_button_repeat_time_ = settings["repeat_button_repeat_time"];
135 
136  VALIDATE(double_click_time_, 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"];
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'.\n";
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
Define the common log macros for the gui toolkit.
#define DBG_GUI_P
Definition: log.hpp:66
std::map< std::string, builder_window > window_types
Map of all known windows (the builder class builds a window).
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
gui_definition(const config &cfg)
Private ctor.
const builder_window::window_resolution & get_window_builder(const std::string &type)
Returns an reference to the requested builder.
Key Type Default Description window_width unsigned 0 Width of the application window.
unsigned double_click_time
Definition: settings.cpp:40
std::vector< game_tip > tips
Definition: settings.cpp:50
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
child_itors child_range(config_key_type key)
Definition: config.cpp:344
std::string sound_button_click
Definition: settings.cpp:43
gui_theme_map_t guis
Map of all known GUIs.
#define h
Definitions for the interface to Wesnoth Markup Language (WML).
std::string sound_slider_adjust
Definition: settings.cpp:46
gui_theme_map_t::iterator default_gui
Iterator pointing 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::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.
#define ERR_GUI_P
Definition: log.hpp:69
int x
x coordinate.
Definition: point.hpp:45
std::shared_ptr< styled_widget_definition > styled_widget_definition_ptr
Generic file dialog.
Definition: field-fwd.hpp:23
unsigned repeat_button_repeat_time
Definition: settings.cpp:41
gui_theme_map_t::iterator current_gui
Iterator pointing to the current GUI.
Helper struct to signal that get_window_builder failed.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::map< std::string, gui_definition > gui_theme_map_t
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
This file contains the settings handling of the widget library.
std::shared_ptr< resolution_definition > resolution_definition_ptr
std::ostringstream wrapper.
Definition: formatter.hpp:39
t_string has_helptip_message
Definition: settings.cpp:48
std::string sound_toggle_panel_click_
std::map< std::string, styled_widget_definition_ptr > widget_definition_map_t
std::string sound_slider_adjust_
registered_widget_map & registered_widget_types()
Returns the list of registered widgets and their parsers.
#define LOG_GUI_G
Definition: log.hpp:42
Contains the general settings which have a default.
unsigned popup_show_time
Definition: settings.cpp:38
std::string sound_button_click_
Holds a 2D point.
Definition: point.hpp:24
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.
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
Definition: settings.cpp:29
int w
std::string sound_toggle_panel_click
Definition: settings.cpp:45
unsigned repeat_button_repeat_time_
std::string sound_toggle_button_click_
bool empty() const
Definition: tstring.hpp:187
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&#39;s definitions, also by id...
unsigned popup_show_delay
These are copied from the active gui.
Definition: settings.cpp:37
unsigned screen_height
Definition: settings.cpp:30
std::vector< game_tip > tips_
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.
#define FAIL(message)
std::set< std::string > & registered_window_types()
Returns the list of registered windows.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
void activate() const
Activates this GUI.
std::string sound_toggle_button_click
Definition: settings.cpp:44
int y
y coordinate.
Definition: point.hpp:48
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
void update_screen_size_variables()
Update the size of the screen variables in settings.
Definition: settings.cpp:52
unsigned help_show_time
Definition: settings.cpp:39