The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
gui_definition.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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"
26#include "wml_exception.hpp"
27
28namespace gui2
29{
30using namespace std::chrono_literals;
31
33gui_theme_map_t::iterator current_gui = guis.end();
34gui_theme_map_t::iterator default_gui = guis.end();
35
37 : widget_types()
38 , window_types()
39 , id_(cfg["id"])
40 , description_(cfg["description"].t_str())
41 , settings_(cfg.mandatory_child("settings"))
42 , tips_(tip_of_the_day::load(cfg))
43{
44 VALIDATE(!id_.empty(), missing_mandatory_wml_key("gui", "id"));
45 VALIDATE(!description_.empty(), missing_mandatory_wml_key("gui", "description"));
46
47 DBG_GUI_P << "Parsing gui " << id_;
48
49 //
50 // Widget parsing
51 //
52
53 /** Parse widget definitions of each registered type. */
54 for(const auto& [type_id, widget_parser] : registered_widget_types()) {
55 auto& def_map = widget_types[type_id];
56
57 const std::string key = widget_parser.key
58 ? widget_parser.key
59 : type_id + "_definition";
60
61 bool found_default_def = false;
62
63 for(const config& definition : cfg.child_range(key)) {
64 // Run the static parser to get a definition ptr.
65 styled_widget_definition_ptr def_ptr = widget_parser.parser(definition);
66
67 const std::string& def_id = def_ptr->id;
68 auto [_, success] = def_map.emplace(def_id, std::move(def_ptr));
69
70 if(!success) {
71 ERR_GUI_P << "Skipping duplicate definition '" << def_id << "' for '" << type_id << "'";
72 continue;
73 }
74
75 if(def_id == "default") {
76 found_default_def = true;
77 }
78 }
79
80 // Only the default GUI needs to ensure each widget has a default definition.
81 // Non-default ones can just fall back to the default definition in the default GUI.
82 if(id_ == "default") {
83 VALIDATE(found_default_def, "No default definition found for widget '" + type_id + "'");
84 }
85 }
86
87 //
88 // Window parsing
89 //
90
91 /** Parse each window. */
92 for(auto& w : cfg.child_range("window")) {
93 window_types.emplace(w["id"], builder_window(w));
94 }
95
96 if(id_ == "default") {
97 // The default gui needs to define all window types since we're the
98 // fallback in case another gui doesn't define the window type.
99 for(const auto& window_type : registered_window_types()) {
100 const std::string error_msg(
101 "Window not defined in WML: '" + window_type + "'."
102 "Perhaps a mismatch between data and source versions. Try --data-dir <trunk-dir>");
103
104 VALIDATE(window_types.find(window_type) != window_types.end(), error_msg);
105 }
106 }
107}
108
110 : popup_show_delay(chrono::parse_duration(cfg["popup_show_delay"], 0ms))
111 , popup_show_time(chrono::parse_duration(cfg["popup_show_time"], 0ms))
112 , help_show_time(chrono::parse_duration(cfg["help_show_time"], 0ms))
113 , double_click_time(chrono::parse_duration(cfg["double_click_time"], 0ms))
114 , repeat_button_repeat_time(chrono::parse_duration(cfg["repeat_button_repeat_time"], 0ms))
115 , sound_button_click(cfg["sound_button_click"])
116 , sound_toggle_button_click(cfg["sound_toggle_button_click"])
117 , sound_toggle_panel_click(cfg["sound_toggle_panel_click"])
118 , sound_slider_adjust(cfg["sound_slider_adjust"])
119 , has_helptip_message(cfg["has_helptip_message"].t_str())
120{
122 missing_mandatory_wml_key("settings", "double_click_time"));
123
125 missing_mandatory_wml_key("settings", "has_helptip_message"));
126}
127
146
147namespace
148{
149template<typename TList, typename TConv>
150const typename TList::value_type& get_best_resolution(const TList& list, const TConv& get_size)
151{
152 using resolution_t = const typename TList::value_type;
153
154 resolution_t* best_resolution = nullptr;
155 int best_resolution_score = std::numeric_limits<int>::min();
156
157 const int screen_w = settings::screen_width;
158 const int screen_h = settings::screen_height;
159
160 for(const auto& res : list) {
161 point size = get_size(res);
162
163 int w = size.x ? size.x : 1;
164 int h = size.y ? size.y : 1;
165 int score = 0;
166
167 if(w <= screen_w && h <= screen_h) {
168 score = w * h;
169 } else {
170 // Negative score, only used in case none of the given resolution fits on the screen
171 // (workaround for a bug where the windows size can become < 800x600).
172 score = std::min(screen_w - w, 0) + std::min(screen_h - h, 0);
173 }
174
175 if(score >= best_resolution_score) {
176 best_resolution = &res;
177 best_resolution_score = score;
178 }
179 }
180
181 assert(best_resolution != nullptr);
182 return *best_resolution;
183}
184
185} // namespace
186
187resolution_definition_ptr get_control(const std::string& control_type, const std::string& definition)
188{
189 const auto& current_types = current_gui->second.widget_types;
190 const auto& default_types = default_gui->second.widget_types;
191
192 const auto find_definition =
193 [&](const auto& widget_types) -> utils::optional<gui_definition::widget_definition_map_t::const_iterator>
194 {
195 // Get all possible definitions for the given widget type.
196 const auto widget_definitions = widget_types.find(control_type);
197
198 // We don't have a fallback here since all types should be valid in all themes.
199 VALIDATE(widget_definitions != widget_types.end(),
200 formatter() << "Control: type '" << control_type << "' is unknown.");
201
202 const auto& options = widget_definitions->second;
203
204 // Out of all definitions for that type, find the requested one.
205 if(auto control = options.find(definition); control != options.end()) {
206 return control;
207 } else {
208 return utils::nullopt;
209 }
210 };
211
212 auto control = find_definition(current_types);
213
214 // Definition not found in the current theme, try the default theme.
215 if(!control && current_gui != default_gui) {
216 control = find_definition(default_types);
217 }
218
219 // Still no match. Try the default definition.
220 if(!control && definition != "default") {
221 LOG_GUI_G << "Control: type '" << control_type << "' definition '" << definition
222 << "' not found, falling back to 'default'.";
223 return get_control(control_type, "default");
224 }
225
226 VALIDATE(control,
227 formatter() << "Control: definition '" << definition << "' not found for styled_widget " << control_type);
228
229 // Finally, resolve the appropriate resolution
230 const auto& resolutions = (*control)->second->resolutions;
231
232 VALIDATE(!resolutions.empty(),
233 formatter() << "Control: type '" << control_type << "' definition '" << definition << "' has no resolutions.");
234
235 return get_best_resolution(resolutions, [&](const resolution_definition_ptr& ptr) {
236 return point(
237 static_cast<int>(ptr->window_width),
238 static_cast<int>(ptr->window_height)
239 );
240 });
241}
242
244{
246
247 const auto& current_windows = current_gui->second.window_types;
248 const auto& default_windows = default_gui->second.window_types;
249
250 auto iter = current_windows.find(type);
251
252 if(iter == current_windows.end()) {
253 // Current GUI is the default one and no window type was found. Throw.
254 if(current_gui == default_gui) {
256 }
257
258 // Else, try again to find the window, this time in the default GUI.
259 iter = default_windows.find(type);
260
261 if(iter == default_windows.end()) {
263 }
264 }
265
266 const auto& resolutions = iter->second.resolutions;
267
268 VALIDATE(!resolutions.empty(), formatter() << "Window '" << type << "' has no resolutions.\n");
269
270 return get_best_resolution(resolutions, [&](const builder_window::window_resolution& res) {
271 return point(
272 static_cast<int>(res.window_width),
273 static_cast<int>(res.window_height)
274 );
275 });
276}
277
278bool add_single_widget_definition(const std::string& widget_type, const std::string& definition_id, const config& cfg)
279{
280 auto& def_map = current_gui->second.widget_types[widget_type];
281 auto parser = registered_widget_types().find(widget_type);
282
283 if(parser == registered_widget_types().end()) {
284 throw std::invalid_argument("widget '" + widget_type + "' doesn't exist");
285 }
286
287 auto [_, success] = def_map.emplace(definition_id, parser->second.parser(cfg));
288 return success;
289}
290
291void remove_single_widget_definition(const std::string& widget_type, const std::string& definition_id)
292{
293 auto& definition_map = current_gui->second.widget_types[widget_type];
294
295 auto it = definition_map.find(definition_id);
296 if(it != definition_map.end()) {
297 definition_map.erase(it);
298 }
299}
300
301} // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
child_itors child_range(std::string_view key)
Definition config.cpp:268
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).
settings_helper settings_
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_
gui_definition(const config &cfg)
Private ctor.
void activate() const
Activates this GUI.
bool empty() const
Definition tstring.hpp:200
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
static std::string _(const char *str)
Definition gettext.hpp:100
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
t_string has_helptip_message
Definition settings.cpp:55
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:57
std::chrono::milliseconds double_click_time
The interval between two clicks to be detected as double click.
Definition settings.cpp:44
std::string sound_slider_adjust
Definition settings.cpp:53
std::string sound_toggle_button_click
Definition settings.cpp:51
std::chrono::milliseconds popup_show_delay
Delay before a popup shows.
Definition settings.cpp:38
unsigned screen_height
Definition settings.cpp:28
std::string sound_button_click
Definition settings.cpp:50
std::chrono::milliseconds popup_show_time
Definition settings.cpp:39
std::chrono::milliseconds help_show_time
Definition settings.cpp:40
std::string sound_toggle_panel_click
Definition settings.cpp:52
void update_screen_size_variables()
Update the size of the screen variables in settings.
Definition settings.cpp:59
std::chrono::milliseconds repeat_button_repeat_time
How much a repeating button need to be held before the action repeats.
Definition settings.cpp:48
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.
int w
Definition pathfind.cpp:188
This file contains the settings handling of the widget library.
std::chrono::milliseconds popup_show_time
std::chrono::milliseconds double_click_time
std::chrono::milliseconds popup_show_delay
std::chrono::milliseconds repeat_button_repeat_time
std::chrono::milliseconds help_show_time
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 VALIDATE(cond, message)
The macro to use for the validation of WML.
#define h