The Battle for Wesnoth  1.19.13+dev
gui.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 
18 #include "gui/gui.hpp"
19 
20 #include "config_cache.hpp"
21 #include "filesystem.hpp"
22 #include "gettext.hpp"
23 #include "gui/core/log.hpp"
25 #include "gui/widgets/settings.hpp"
26 #include "serialization/parser.hpp"
29 #include "wml_exception.hpp"
30 
31 namespace gui2
32 {
33 namespace
34 {
35 config read_and_validate(const std::string& path)
36 try {
37  preproc_map defines;
38 #ifdef __ANDROID__
39  defines["ANDROID"] = preproc_define();
40 #endif
42  return io::read(*preprocess_file(path, &defines), &validator);
43 
44 } catch(const utils::bad_optional_access&) {
45  FAIL("GUI2: schema/gui.cfg not found.");
46 
47 } catch(const abstract_validator::error& e) {
48  FAIL("GUI2: could not read schema file: " + e.message);
49 
50 } catch(const config::error& e) {
51  ERR_GUI_P << "Could not read gui file: " << path;
52  ERR_GUI_P << e.what();
53  return {};
54 }
55 
56 /**
57  * Adds a theme definition object to the global registry.
58  *
59  * @param def A valid gui_definition config.
60  *
61  * @returns An optional iterator to the newly-constructed definition.
62  * If gui_definition throws wml_exception or a theme with the
63  * given ID already exists, returns nullopt.
64  */
65 auto register_theme(const config& def) -> utils::optional<gui_theme_map_t::iterator>
66 try {
67  auto [iter, is_unique] = guis.try_emplace(def["id"], def);
68  if(is_unique) return iter;
69 
70  ERR_GUI_P << "UI Theme '" << def["id"] << "' already exists.";
71  return utils::nullopt;
72 
73 } catch(const wml_exception& e) {
74  ERR_GUI_P << "Invalid UI theme: " << def["id"];
75  ERR_GUI_P << e.user_message;
76  return utils::nullopt;
77 }
78 
79 /**
80  * Parses any GUI2 theme definitions at the file path specified.
81  *
82  * @param full_path Path to file containing one or more [gui] tags.
83  * @param is_core If true, look for the default theme here.
84  */
85 void parse(const std::string& full_path, bool is_core)
86 {
87 #if __cpp_range_based_for >= 202211L // lifetime extension of temporaries
88  for(const config& def : read_and_validate(full_path).child_range("gui")) {
89 #else
90  config cfg = read_and_validate(full_path);
91  for(const config& def : cfg.child_range("gui")) {
92 #endif
93  const bool is_default = def["id"] == "default";
94 
95  if(is_default && !is_core) {
96  ERR_GUI_P << "UI theme id 'default' is reserved for core themes.";
97  continue;
98  }
99 
100  const auto iter = register_theme(def);
101  if(!iter) continue;
102 
103  if(is_default && is_core) {
104  default_gui = *iter;
106  }
107  }
108 }
109 
110 } // namespace
111 
112 void init()
113 {
114  LOG_GUI_G << "Initializing UI subststem.";
115 
116  // Reset the registry in case we're re-initializing
117  guis.clear();
118 
119  // Save current screen size.
121 
122  //
123  // Parse GUI definitions from mainline
124  //
125 
126  try {
127  parse(filesystem::get_wml_location("gui/_main.cfg").value(), true);
128  } catch(const utils::bad_optional_access&) {
129  FAIL("GUI2: gui/_main.cfg not found.");
130  }
131 
132  // The default GUI must be in mainline
133  VALIDATE(default_gui != guis.end(), _("No default gui defined."));
134 
135  //
136  // Parse GUI definitions from addons
137  //
138 
139  std::vector<std::string> addon_dirs;
140  const std::string umc_dir = filesystem::get_addons_dir();
142 
143  // Search for all $user_campaign_dir/*/gui-theme.cfg files
144  for(const std::string& umc : addon_dirs) {
145  const std::string gui_file = umc + "/gui-theme.cfg";
146 
147  if(filesystem::file_exists(gui_file)) {
148  parse(gui_file, false);
149  }
150  }
151 }
152 
153 void switch_theme(const std::string& theme_id)
154 {
155  if(theme_id.empty() || theme_id == "default") {
157  } else {
158  current_gui = std::find_if(guis.begin(), guis.end(),
159  [&](const auto& theme) { return theme.first == theme_id; });
160 
161  if(current_gui == guis.end()) {
162  ERR_GUI_P << "Missing [gui] definition for '" << theme_id << "'";
164  }
165  }
166 
167  current_gui->second.activate();
168 }
169 
170 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
child_itors child_range(config_key_type key)
Definition: config.cpp:268
Realization of serialization/validator.hpp abstract validator.
Definition: theme.hpp:44
Declarations for File-IO.
static std::string _(const char *str)
Definition: gettext.hpp:97
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
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:463
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:341
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir)
Returns a translated path to the actual file or directory, if it exists.
std::string get_addons_dir()
std::string path
Definition: filesystem.cpp:106
void update_screen_size_variables()
Update the size of the screen variables in settings.
Definition: settings.cpp:49
Generic file dialog.
void init()
Initializes the GUI subsystems.
Definition: gui.cpp:112
gui_theme_map_t guis
Map of all known GUIs.
void switch_theme(const std::string &theme_id)
Set and activate the given gui2 theme.
Definition: gui.cpp:153
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.
config read(std::istream &in, abstract_validator *validator)
Definition: parser.cpp:600
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
std::map< std::string, struct preproc_define > preproc_map
One of the realizations of serialization/validator.hpp abstract validator.
This file contains the settings handling of the widget library.
Used to manage with not initialized validators Supposed to be thrown from the constructor.
Definition: validator.hpp:97
Helper class, don't construct this directly.
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 e