GUI2: cleaned up and restructured initialization process

This mostly has to do with moving various components into more logical locations instead of
all mushed together in gui/widgets/settings.*pp. To that end, the following changes have been
made:

* The gui2::init function has been moved to its own file in the gui/ toplevel.
* load_settings() has been merged into init().
* All functions and code relating to gui theme definitions have been moved to their own file.
* All code relating to widgets or window static init have been moved to their own file.
* window::update_screen_size has been moved out of the window class and into settings.cpp.
* The unimplemented free-stadnding version of load_widget_definitions has been removed.
* gui_definition::read and gui_defintion::load have been merged into the gui_definition ctor
  and greatly simplified.
* Some functions relating to builder_widgets have been renamed for clarity.
* add/remove_single_widget_defintion now access the current gui theme instead of the default.
  This looks like it was a mistake made in the original code.
* Since the static registry is now externally linked, the unit tests accessor for window types
  has been removed.
* Documentation has been updated. The wikidoc comment for gui_definition has been removed. It
  was rather out-of-date and needed to be reworked anyway.
* widget_builder_func_t's signature is now to take a config reference and not a copy, though
  REGISRER_WIDGET had already passed register_builder_widget a lambda that took a reference.
* Various other misc cleanups and improvements.
This commit is contained in:
Charles Dang 2017-12-09 11:49:06 +11:00
parent 2c154b580d
commit 6759f41b1e
32 changed files with 891 additions and 832 deletions

View file

@ -1384,6 +1384,13 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Gui\Core\Event\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Gui\Core\Event\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\gui\core\gui_definition.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\gui\core\linked_group_definition.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
@ -1419,6 +1426,13 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Gui\Core\Placer\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Gui\Core\Placer\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\gui\core\static_registry.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\gui\core\timer.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Gui\Core\</ObjectFileName>
@ -2035,6 +2049,13 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Gui\Dialogs\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Gui\Dialogs\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\gui\gui.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Gui\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Gui\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Gui\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)Gui\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Gui\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\gui\widgets\addon_list.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Gui\Widgets\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Gui\Widgets\</ObjectFileName>
@ -3692,6 +3713,7 @@
<ClInclude Include="..\..\src\gui\core\event\distributor.hpp" />
<ClInclude Include="..\..\src\gui\core\event\handler.hpp" />
<ClInclude Include="..\..\src\gui\core\event\message.hpp" />
<ClInclude Include="..\..\src\gui\core\gui_definition.hpp" />
<ClInclude Include="..\..\src\gui\core\layout_exception.hpp" />
<ClInclude Include="..\..\src\gui\core\linked_group_definition.hpp" />
<ClInclude Include="..\..\src\gui\core\log.hpp" />
@ -3701,6 +3723,7 @@
<ClInclude Include="..\..\src\gui\core\placer\horizontal_list.hpp" />
<ClInclude Include="..\..\src\gui\core\placer\vertical_list.hpp" />
<ClInclude Include="..\..\src\gui\core\register_widget.hpp" />
<ClInclude Include="..\..\src\gui\core\static_registry.hpp" />
<ClInclude Include="..\..\src\gui\core\timer.hpp" />
<ClInclude Include="..\..\src\gui\core\widget_definition.hpp" />
<ClInclude Include="..\..\src\gui\core\window_builder.hpp" />
@ -3791,6 +3814,7 @@
<ClInclude Include="..\..\src\gui\dialogs\unit_recruit.hpp" />
<ClInclude Include="..\..\src\gui\dialogs\wml_error.hpp" />
<ClInclude Include="..\..\src\gui\dialogs\wml_message.hpp" />
<ClInclude Include="..\..\src\gui\gui.hpp" />
<ClInclude Include="..\..\src\gui\widgets\addon_list.hpp" />
<ClInclude Include="..\..\src\gui\widgets\button.hpp" />
<ClInclude Include="..\..\src\gui\widgets\chatbox.hpp" />

View file

@ -1546,6 +1546,15 @@
<ClCompile Include="..\..\src\preferences\lobby.cpp">
<Filter>Preferences</Filter>
</ClCompile>
<ClCompile Include="..\..\src\gui\gui.cpp">
<Filter>Gui</Filter>
</ClCompile>
<ClCompile Include="..\..\src\gui\core\gui_definition.cpp">
<Filter>Gui\Core</Filter>
</ClCompile>
<ClCompile Include="..\..\src\gui\core\static_registry.cpp">
<Filter>Gui\Core</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\addon\client.hpp">
@ -3001,6 +3010,15 @@
<ClInclude Include="..\..\src\preferences\lobby.hpp">
<Filter>Preferences</Filter>
</ClInclude>
<ClInclude Include="..\..\src\gui\gui.hpp">
<Filter>Gui</Filter>
</ClInclude>
<ClInclude Include="..\..\src\gui\core\gui_definition.hpp">
<Filter>Gui\Core</Filter>
</ClInclude>
<ClInclude Include="..\..\src\gui\core\static_registry.hpp">
<Filter>Gui\Core</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\tests\test_sdl_utils.hpp">

View file

@ -150,11 +150,13 @@ gui/core/canvas.cpp
gui/core/event/dispatcher.cpp
gui/core/event/distributor.cpp
gui/core/event/handler.cpp
gui/core/gui_definition.cpp
gui/core/linked_group_definition.cpp
gui/core/log.cpp
gui/core/placer.cpp
gui/core/placer/horizontal_list.cpp
gui/core/placer/vertical_list.cpp
gui/core/static_registry.cpp
gui/core/timer.cpp
gui/core/widget_definition.cpp
gui/core/window_builder.cpp
@ -243,6 +245,7 @@ gui/dialogs/unit_recall.cpp
gui/dialogs/unit_recruit.cpp
gui/dialogs/wml_error.cpp
gui/dialogs/wml_message.cpp
gui/gui.cpp
gui/widgets/container_base.cpp
gui/widgets/debug.cpp
gui/widgets/generator.cpp

View file

@ -0,0 +1,331 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/core/gui_definition.hpp"
#include "config.hpp"
#include "formatter.hpp"
#include "gui/core/log.hpp"
#include "gui/core/static_registry.hpp"
#include "gui/widgets/settings.hpp"
#include "wml_exception.hpp"
namespace gui2
{
gui_theme_map_t guis;
gui_theme_map_t::iterator current_gui = guis.end();
gui_theme_map_t::iterator default_gui = guis.end();
gui_definition::gui_definition(const config& cfg)
: widget_types()
, window_types()
, id_(cfg["id"])
, description_(cfg["description"].t_str())
, popup_show_delay_(0)
, popup_show_time_(0)
, help_show_time_(0)
, double_click_time_(0)
, repeat_button_repeat_time_(0)
, sound_button_click_()
, sound_toggle_button_click_()
, sound_toggle_panel_click_()
, sound_slider_adjust_()
, has_helptip_message_()
, tips_(tip_of_the_day::load(cfg))
{
VALIDATE(!id_.empty(), missing_mandatory_wml_key("gui", "id"));
VALIDATE(!description_.empty(), missing_mandatory_wml_key("gui", "description"));
DBG_GUI_P << "Parsing gui " << id_ << std::endl;
//
// Widget parsing
//
/** Parse widget definitions of each registered type. */
for(auto& widget_type : registered_widget_types()) {
const std::string& type_id = widget_type.first;
gui_definition::widget_definition_map_t& def_map = widget_types[type_id];
const std::string key = widget_type.second.key
? widget_type.second.key
: type_id + "_definition";
bool found_default_def = false;
for(const config& definition : cfg.child_range(key)) {
// Run the static parser to get a definition ptr.
styled_widget_definition_ptr def_ptr = widget_type.second.parser(definition);
const std::string& def_id = def_ptr->id;
if(def_map.find(def_id) != def_map.end()) {
ERR_GUI_P << "Skipping duplicate definition '" << def_id << "' for '" << type_id << "'\n";
continue;
}
def_map.emplace(def_id, std::move(def_ptr));
if(def_id == "default") {
found_default_def = true;
}
}
// Only the default GUI needs to ensure each widget has a default definition.
// Non-default ones can just fall back to the default definition in the default GUI.
if(id_ == "default") {
VALIDATE(found_default_def, "No default definition found for widget '" + type_id + "'");
}
}
//
// Window parsing
//
/** Parse each window. */
for(auto& w : cfg.child_range("window")) {
window_types.emplace(w["id"], builder_window(w));
}
if(id_ == "default") {
// The default gui needs to define all window types since we're the
// fallback in case another gui doesn't define the window type.
for(const auto& window_type : registered_window_types()) {
const std::string error_msg(
"Window not defined in WML: '" + window_type + "'."
"Perhaps a mismatch between data and source versions. Try --data-dir <trunk-dir>");
VALIDATE(window_types.find(window_type) != window_types.end(), error_msg);
}
}
/***** settings *****/
/**
* @todo Regarding sounds:
* Need to evaluate but probably we want the widget definition be able to:
* - Override the default (and clear it). This will allow toggle buttons in a
* listbox to sound like a toggle panel.
* - Override the default and above per instance of the widget, some buttons
* can give a different sound.
*/
const config& settings = cfg.child("settings");
popup_show_delay_ = settings["popup_show_delay"];
popup_show_time_ = settings["popup_show_time"];
help_show_time_ = settings["help_show_time"];
double_click_time_ = settings["double_click_time"];
repeat_button_repeat_time_ = settings["repeat_button_repeat_time"];
VALIDATE(double_click_time_, missing_mandatory_wml_key("settings", "double_click_time"));
sound_button_click_ = settings["sound_button_click"].str();
sound_toggle_button_click_ = settings["sound_toggle_button_click"].str();
sound_toggle_panel_click_ = settings["sound_toggle_panel_click"].str();
sound_slider_adjust_ = settings["sound_slider_adjust"].str();
has_helptip_message_ = settings["has_helptip_message"];
VALIDATE(!has_helptip_message_.empty(), missing_mandatory_wml_key("[settings]", "has_helptip_message"));
}
void gui_definition::activate() const
{
settings::popup_show_delay = popup_show_delay_;
settings::popup_show_time = popup_show_time_;
settings::help_show_time = help_show_time_;
settings::double_click_time = double_click_time_;
settings::repeat_button_repeat_time = repeat_button_repeat_time_;
settings::sound_button_click = sound_button_click_;
settings::sound_toggle_button_click = sound_toggle_button_click_;
settings::sound_toggle_panel_click = sound_toggle_panel_click_;
settings::sound_slider_adjust = sound_slider_adjust_;
settings::has_helptip_message = has_helptip_message_;
settings::tips = tips_;
}
namespace
{
template<typename TList, typename TConv>
const typename TList::value_type& get_best_resolution(const TList& list, const TConv& get_size)
{
using resolution_t = const typename TList::value_type;
resolution_t* best_resolution = nullptr;
int best_resolution_score = std::numeric_limits<int>::min();
const int screen_w = settings::screen_width;
const int screen_h = settings::screen_height;
for(const auto& res : list) {
point size = get_size(res);
int w = size.x ? size.x : 1;
int h = size.y ? size.y : 1;
int score = 0;
if(w <= screen_w && h <= screen_h) {
score = w * h;
} else {
// Negative score, only used in case none of the given resolution fits on the screen
// (workaround for a bug where the windows size can become < 800x600).
score = std::min(screen_w - w, 0) + std::min(screen_h - h, 0);
}
if(score >= best_resolution_score) {
best_resolution = &res;
best_resolution_score = score;
}
}
assert(best_resolution != nullptr);
return *best_resolution;
}
} // namespace
resolution_definition_ptr get_control(const std::string& control_type, const std::string& definition)
{
const auto& current_types = current_gui->second.widget_types;
const auto& default_types = default_gui->second.widget_types;
#ifdef GUI2_EXPERIMENTAL_LISTBOX
const auto widget_definitions = (control_type == "list")
? current_types.find("listbox")
: current_types.find(control_type);
#else
const auto widget_definitions
= current_types.find(control_type);
#endif
gui_definition::widget_definition_map_t::const_iterator control;
if(widget_definitions == current_types.end()) {
goto fallback;
}
control = widget_definitions->second.find(definition);
if(control == widget_definitions->second.end()) {
fallback:
bool found_fallback = false;
if(current_gui != default_gui) {
#ifdef GUI2_EXPERIMENTAL_LISTBOX
auto default_widget_definitions = (control_type == "list")
? default_types.find("listbox")
: default_types.find(control_type);
#else
auto default_widget_definitions
= default_types.find(control_type);
#endif
VALIDATE(widget_definitions != current_types.end(),
formatter() << "Type '" << control_type << "' is unknown.");
control = default_widget_definitions->second.find(definition);
found_fallback = control != default_widget_definitions->second.end();
}
if(!found_fallback) {
if(definition != "default") {
LOG_GUI_G << "Control: type '" << control_type << "' definition '" << definition
<< "' not found, falling back to 'default'.\n";
return get_control(control_type, "default");
}
FAIL(formatter() << "default definition not found for styled_widget " << control_type);
}
}
const auto& resolutions = (*control->second).resolutions;
VALIDATE(!resolutions.empty(),
formatter() << "Control: type '" << control_type << "' definition '" << definition << "' has no resolutions.\n");
return get_best_resolution(resolutions, [&](const resolution_definition_ptr& ptr) {
return point(
static_cast<int>(ptr->window_width),
static_cast<int>(ptr->window_height)
);
});
}
const builder_window::window_resolution& get_window_builder(const std::string& type)
{
settings::update_screen_size_variables();
const auto& current_windows = current_gui->second.window_types;
const auto& default_windows = default_gui->second.window_types;
auto iter = current_windows.find(type);
if(iter == current_windows.end()) {
// Current GUI is the default one and no window type was found. Throw.
if(current_gui == default_gui) {
throw window_builder_invalid_id();
}
// Else, try again to find the window, this time in the default GUI.
iter = default_windows.find(type);
if(iter == default_windows.end()) {
throw window_builder_invalid_id();
}
}
const auto& resolutions = iter->second.resolutions;
VALIDATE(!resolutions.empty(), formatter() << "Window '" << type << "' has no resolutions.\n");
return get_best_resolution(resolutions, [&](const builder_window::window_resolution& res) {
return point(
static_cast<int>(res.window_width),
static_cast<int>(res.window_height)
);
});
}
bool add_single_widget_definition(const std::string& widget_type, const std::string& definition_id, const config& cfg)
{
auto& def_map = current_gui->second.widget_types[widget_type];
auto parser = registered_widget_types().find(widget_type);
if(parser == registered_widget_types().end()) {
throw std::invalid_argument("widget '" + widget_type + "' doesn't exist");
}
if(def_map.find(definition_id) != def_map.end()) {
return false;
}
def_map.emplace(definition_id, parser->second.parser(cfg));
return true;
}
void remove_single_widget_definition(const std::string& widget_type, const std::string& definition_id)
{
auto& definition_map = current_gui->second.widget_types[widget_type];
auto it = definition_map.find(definition_id);
if(it != definition_map.end()) {
definition_map.erase(it);
}
}
} // namespace gui2

View file

@ -0,0 +1,138 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include "gui/auxiliary/tips.hpp"
#include "gui/core/window_builder.hpp"
#include "gui/core/widget_definition.hpp"
#include <map>
class config;
namespace gui2
{
/**
* A GUI theme definiton.
*
* Each theme defines the appearance and layout of widgets and windows. At least one theme
* (the default) must exist for the game to run. That theme is expected to contain at least
* one default definition for each widget type and a layout for each window recorded in the
* static registry. Do note that a widget type may have any number of definitions defined
* per theme, but a window may only have one layout.
*
* Non-default themes may omit a default widget defintion or a window layout, in which case
* the game will fall back on the default definition (for widgets) or the layout (for windows)
* specified in the default theme.
*
* Each widget definition and window layout may also define different variations of itself
* to be used based on the current game resolution.
*
* As of December 2017 only the default theme is provided.
*/
class gui_definition
{
public:
/** Private ctor. Use @ref create to initialize a new definition. */
explicit gui_definition(const config& cfg);
using widget_definition_map_t = std::map<std::string, styled_widget_definition_ptr>;
/** Map of each widget type, by id, and a sub-map of each of the type's definitions, also by id. */
std::map<std::string, widget_definition_map_t> widget_types;
/** Map of all known windows (the builder class builds a window). */
std::map<std::string, builder_window> window_types;
/** Activates this GUI. */
void activate() const;
private:
std::string id_;
t_string description_;
unsigned popup_show_delay_;
unsigned popup_show_time_;
unsigned help_show_time_;
unsigned double_click_time_;
unsigned repeat_button_repeat_time_;
std::string sound_button_click_;
std::string sound_toggle_button_click_;
std::string sound_toggle_panel_click_;
std::string sound_slider_adjust_;
t_string has_helptip_message_;
std::vector<game_tip> tips_;
};
using gui_theme_map_t = std::map<std::string, gui_definition>;
/** Map of all known GUIs. */
extern gui_theme_map_t guis;
/** Iterator pointing to the current GUI. */
extern gui_theme_map_t::iterator current_gui;
/** Iterator pointing to the default GUI. */
extern gui_theme_map_t::iterator default_gui;
/**
* Returns the appropriate config data for a widget instance fom the active
* GUI definition.
*
* @param control_type The widget type.
* @param definition The definition ID.
*
* @returns A pointer to the specified definition data struct
* for the widget type, accounting for the current
* screen resolution.
*/
resolution_definition_ptr get_control(const std::string& control_type, const std::string& definition);
/** Helper struct to signal that get_window_builder failed. */
struct window_builder_invalid_id
{
};
/**
* Returns an reference to the requested builder.
*
* The builder is determined by the @p type and the current screen resolution.
*
* @pre There is a valid builder for @p type at the
* current resolution.
*
* @throws window_builder_invalid_id
* When the precondition is violated.
*
* @param type The type of builder window to get.
*
* @returns An iterator to the requested builder.
*/
const builder_window::window_resolution& get_window_builder(const std::string& type);
/** Adds a widget definiton to the default GUI. */
bool add_single_widget_definition(const std::string& widget_type,
const std::string& definition_id,
const config& cfg);
/** Removes a widget definiton from the default GUI. */
void remove_single_widget_definition(const std::string& widget_type,
const std::string& definition_id);
} // namespace gui2

View file

@ -14,13 +14,15 @@
#pragma once
#include "gui/core/static_registry.hpp"
/**
* Registers a widget.
*
* Call this function to register a widget. Use this macro in the
* implementation, inside the gui2 namespace.
*
* See @ref gui2::load_widget_definitions for more information.
* See @ref register_widget for more information.
*
* @note When the type is foo_definition, the id "foo" and no special key best
* use RESISTER_WIDGET(foo) instead.
@ -41,7 +43,7 @@
register_widget(#id, \
[](const config& cfg) { return std::make_shared<type>(cfg); }, key); \
\
register_builder_widget(#id, \
register_widget_builder(#id, \
[](const config& cfg) { return std::make_shared<implementation::builder_##id>(cfg); }); \
} \
}; \

View file

@ -0,0 +1,65 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "gui/core/static_registry.hpp"
#include "gui/core/log.hpp"
#include "utils/functional.hpp"
#include <map>
#include <set>
#include <string>
#include <tuple>
namespace gui2
{
std::set<std::string>& registered_window_types()
{
static std::set<std::string> result;
return result;
}
void register_window(const std::string& id)
{
bool added = false;
std::tie(std::ignore, added) = registered_window_types().emplace(id);
if(!added) {
WRN_GUI_P << "Window '" << id << "' already registered. Ignoring." << std::endl;
}
}
registered_widget_map& registered_widget_types()
{
static registered_widget_map result;
return result;
}
void register_widget(const std::string& type, widget_parser_t f, const char* key)
{
registered_widget_types()[type] = {f, key};
}
widget_builder_map& widget_builder_lookup()
{
static widget_builder_map result;
return result;
}
void register_widget_builder(const std::string& type, widget_builder_func_t functor)
{
widget_builder_lookup().emplace(type, functor);
}
} // namespace gui2

View file

@ -0,0 +1,123 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include "gui/core/widget_definition.hpp"
#include "gui/core/window_builder.hpp"
namespace gui2
{
/***** ***** ***** ***** Registrars ***** ***** ***** *****/
/**
* Registers a window.
*
* This function is utilized by the @ref REGISTER_WINDOW macro and notes a
* window to look for when a GUI definition is initialized.
*
* All windows need to register themselves before @ref gui2::init is called.
*
* @warning This function runs before @ref main() so needs to be careful
* regarding the static initialization problem.
*
* @note A window can't be registered twice. Any subsequently added windows
* with duplicate IDs will be ignored. Might be worth looking into adding an
* unregister function in the future if this becomes an issue.
*
* @param id The id of the window to register.
*/
void register_window(const std::string& id);
/** Function type alias for @ref register_widget. */
using widget_parser_t = std::function<styled_widget_definition_ptr(const config&)>;
/**
* Registers a widget type.
*
* This function is utilized by the @ref REGISTER_WIDGET macro and sets the
* the parser function used to process the widget type's WML when a GUI
* definition is initialized.
*
* All widgets need to register themselves before @ref gui2::init is called.
*
* @warning This function runs before @ref main() so needs to be careful
* regarding the static initialization problem.
*
* @param type The type of the widget to register.
* @param f The function to parse the definition config.
* @param key The tagname from which to read the widget's
* definition in the game config. If nullptr the
* default [<id>_definition] is used.
*/
void register_widget(const std::string& type, widget_parser_t f, const char* key = nullptr);
/** Function type alias for @ref register_widget_builder. */
using widget_builder_func_t = std::function<builder_widget_ptr(const config&)>;
/**
* Registers a widget builder.
*
* A widget builder simply creates and returns a pointer to a widget type's
* builder struct. This is part of the static registry since widget builders
* are simply used to instantiate a widget object when a dialog is being
* assembled.
*
* If the widget inherits from @ref styled_widget, any theme-dependent info
* will be fetched from the current GUI theme upon construction.
*
* @warning This function runs before @ref main() so needs to be careful
* regarding the static initialization problem.
*
* @param id The id of the widget as used in WML.
* @param functor The functor to create the widget.
*/
void register_widget_builder(const std::string& type, widget_builder_func_t functor);
/***** ***** ***** ***** Accessors ***** ***** ***** *****/
/*
* Notes on the registered widget and window lists.
*
* These lists are independent of the active GUI definition and are filled
* during static initialization via @ref register_widget and @register_window.
* Also note these cannot be free-standing static data members within this file
* since that can cause a crash.
*/
/** Returns the list of registered windows. */
std::set<std::string>& registered_window_types();
struct registered_widget_parser
{
/** The widget definiton WML parser function. */
widget_parser_t parser;
/** The tag containing the definition WML. */
const char* key;
};
using registered_widget_map = std::map<std::string, registered_widget_parser>;
/** Returns the list of registered widgets and their parsers. */
registered_widget_map& registered_widget_types();
using widget_builder_map = std::map<std::string, widget_builder_func_t>;
/** Returns the list of registered widget builders. */
widget_builder_map& widget_builder_lookup();
} // namespace gui2

View file

@ -38,7 +38,6 @@ struct state_definition
config canvas_cfg_;
};
/** Base class of a resolution, contains the common keys for a resolution. */
struct resolution_definition
{
@ -61,6 +60,7 @@ struct resolution_definition
unsigned text_extra_width;
unsigned text_extra_height;
unsigned text_font_size;
font::family_class text_font_family;
font::pango_text::FONT_STYLE text_font_style;
@ -73,16 +73,14 @@ resolution_definition_ptr;
typedef std::shared_ptr<const resolution_definition>
resolution_definition_const_ptr;
struct styled_widget_definition
{
explicit styled_widget_definition(const config& cfg);
template <class T>
template<class T>
void load_resolutions(const config& cfg)
{
for (const auto & resolution : cfg.child_range("resolution"))
{
for(const config& resolution : cfg.child_range("resolution")) {
resolutions.emplace_back(std::make_shared<T>(resolution));
}
}

View file

@ -19,6 +19,8 @@
#include "formula/string_utils.hpp"
#include "gettext.hpp"
#include "gui/core/log.hpp"
#include "gui/core/gui_definition.hpp"
#include "gui/core/static_registry.hpp"
#include "gui/core/window_builder/helper.hpp"
#include "gui/core/window_builder/instance.hpp"
#include "gui/widgets/pane.hpp"
@ -31,12 +33,6 @@
namespace gui2
{
static std::map<std::string, widget_builder_func_t>& builder_widget_lookup()
{
static std::map<std::string, widget_builder_func_t> result;
return result;
}
/*WIKI
* @page = GUIWidgetInstanceWML
* @order = 1
@ -100,12 +96,7 @@ builder_widget::builder_widget(const config& cfg)
{
}
void register_builder_widget(const std::string& id, widget_builder_func_t functor)
{
builder_widget_lookup().emplace(id, functor);
}
builder_widget_ptr create_builder_widget(const config& cfg)
builder_widget_ptr create_widget_builder(const config& cfg)
{
config::const_all_children_itors children = cfg.all_children_range();
VALIDATE(children.size() == 1, "Grid cell does not have exactly 1 child.");
@ -126,10 +117,11 @@ builder_widget_ptr create_builder_widget(const config& cfg)
return std::make_shared<implementation::builder_viewport>(viewport);
}
for(const auto& item : builder_widget_lookup()) {
for(const auto& item : widget_builder_lookup()) {
if(item.first == "window" || item.first == "tooltip") {
continue;
}
if(const config& c = cfg.child(item.first)) {
return item.second(c);
}
@ -144,8 +136,8 @@ builder_widget_ptr create_builder_widget(const config& cfg)
widget* build_single_widget_instance_helper(const std::string& type, const config& cfg)
{
const auto& iter = builder_widget_lookup().find(type);
VALIDATE(iter != builder_widget_lookup().end(), "Invalid widget type '" + type + "'");
const auto& iter = widget_builder_lookup().find(type);
VALIDATE(iter != widget_builder_lookup().end(), "Invalid widget type '" + type + "'");
widget_builder_func_t& builder = iter->second;
return builder(cfg)->build();
@ -429,7 +421,7 @@ builder_grid::builder_grid(const config& cfg)
col_grow_factor.push_back(c["grow_factor"]);
}
widgets.push_back(create_builder_widget(c));
widgets.push_back(create_widget_builder(c));
++col;
}

View file

@ -48,7 +48,7 @@ public:
* using and `[instance]' widget this decision can be postponed until
* instantiation.
*/
typedef std::map<std::string, std::shared_ptr<builder_widget> > replacements_map;
typedef std::map<std::string, std::shared_ptr<builder_widget>> replacements_map;
explicit builder_widget(const config& cfg);
@ -71,20 +71,6 @@ public:
typedef std::shared_ptr<builder_widget> builder_widget_ptr;
typedef std::shared_ptr<const builder_widget> builder_widget_const_ptr;
using widget_builder_func_t = std::function<builder_widget_ptr(config)>;
/**
* Registers a widget to be build.
*
* @warning This function runs before @ref main() so needs to be careful
* regarding the static initialization problem.
*
* @param id The id of the widget as used in WML.
* @param functor The functor to create the widget.
*/
void
register_builder_widget(const std::string& id, widget_builder_func_t functor);
/**
* Create a widget builder.
*
@ -95,7 +81,7 @@ register_builder_widget(const std::string& id, widget_builder_func_t functor);
*
* @returns The builder for the widget instance.
*/
builder_widget_ptr create_builder_widget(const config& cfg);
builder_widget_ptr create_widget_builder(const config& cfg);
/**
* Helper function to implement @ref build_single_widget_instance. This keeps the main
@ -146,7 +132,6 @@ public:
grid* build() const;
widget* build(const replacements_map& replacements) const;
grid* build(grid* grid) const;
void build(grid& grid, const replacements_map& replacements) const;
};

View file

@ -15,7 +15,7 @@
#pragma once
#include "gui/auxiliary/field-fwd.hpp"
#include "gui/core/static_registry.hpp"
#include "utils/functional.hpp"
#include <string>

View file

@ -256,7 +256,7 @@ void title_screen::pre_show(window& win)
//
multi_page& tip_pages = find_widget<multi_page>(&win, "tips", false);
std::vector<game_tip> tips(settings::get_tips());
std::vector<game_tip> tips = tip_of_the_day::shuffle(settings::tips);
if(tips.empty()) {
WRN_CF << "There are no tips of day available." << std::endl;
}

View file

@ -16,8 +16,9 @@
#include "gui/dialogs/tooltip.hpp"
#include "gui/dialogs/modal_dialog.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/core/gui_definition.hpp"
#include "gui/dialogs/modal_dialog.hpp"
#include "gui/dialogs/modeless_dialog.hpp"
#include "gui/widgets/settings.hpp"
#include "gui/widgets/window.hpp"

98
src/gui/gui.cpp Normal file
View file

@ -0,0 +1,98 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/gui.hpp"
#include "config_cache.hpp"
#include "filesystem.hpp"
#include "gettext.hpp"
#include "gui/core/log.hpp"
#include "gui/core/gui_definition.hpp"
#include "gui/widgets/settings.hpp"
#include "preferences/general.hpp"
#include "serialization/parser.hpp"
#include "serialization/preprocessor.hpp"
#include "serialization/schema_validator.hpp"
#include "wml_exception.hpp"
namespace gui2
{
static bool initialized = false;
void init()
{
if(initialized) {
return;
}
LOG_GUI_G << "Initializing UI subststem." << std::endl;
// Save current screen size.
settings::update_screen_size_variables();
//
// Read and validate the WML files.
//
config cfg;
try {
schema_validation::schema_validator validator(filesystem::get_wml_location("gui/schema.cfg"));
preproc_map preproc(game_config::config_cache::instance().get_preproc_map());
filesystem::scoped_istream stream = preprocess_file(filesystem::get_wml_location("gui/_main.cfg"), &preproc);
read(cfg, *stream, &validator);
} catch(config::error& e) {
ERR_GUI_P << e.what() << '\n';
ERR_GUI_P << "Setting: could not read file 'data/gui/_main.cfg'." << std::endl;
} catch(const abstract_validator::error& e) {
ERR_GUI_P << "Setting: could not read file 'data/gui/schema.cfg'." << std::endl;
ERR_GUI_P << e.message;
}
//
// Parse GUI definitions.
//
const std::string& current_theme = preferences::gui_theme();
for(const config& g : cfg.child_range("gui")) {
const std::string id = g["id"];
auto iter = guis.emplace(id, gui_definition(g)).first;
if(id == "default") {
default_gui = iter;
}
if(!current_theme.empty() && id == current_theme) {
current_gui = iter;
}
}
VALIDATE(default_gui != guis.end(), _("No default gui defined."));
if(current_theme.empty()) {
current_gui = default_gui;
} else if(current_gui == guis.end()) {
ERR_GUI_P << "Missing [gui] definition for '" << current_theme << "'\n";
current_gui = default_gui;
}
current_gui->second.activate();
initialized = true;
}
} // namespace gui2

27
src/gui/gui.hpp Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
namespace gui2
{
/**
* Initializes the GUI subsystems.
*
* @note This function must be called before other parts of the UI engine
* are used.
*/
void init();
} // namespace gui2

View file

@ -29,20 +29,6 @@
namespace gui2
{
static bool initialized_ = false;
bool init()
{
if(initialized_) {
return true;
}
load_settings();
initialized_ = true;
return initialized_;
}
SDL_Rect create_rect(const point& origin, const point& size)
{
return {origin.x, origin.y, size.x, size.y};

View file

@ -33,15 +33,6 @@ class map_formula_callable;
namespace gui2
{
/**
* Initializes the gui subsystems.
*
* This function needs to be called before other parts of the gui engine are
* used.
*/
bool init();
/**
* Creates a rectangle.
*

View file

@ -275,7 +275,7 @@ builder_matrix::builder_matrix(const config& cfg)
, builder_bottom(nullptr)
, builder_left(nullptr)
, builder_right(nullptr)
, builder_main(create_builder_widget(cfg.child("main", "[matrix]")))
, builder_main(create_widget_builder(cfg.child("main", "[matrix]")))
{
if(const config& top = cfg.child("top")) {
builder_top = std::make_shared<builder_grid>(top);

View file

@ -12,23 +12,9 @@
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/widgets/settings.hpp"
#include "config_cache.hpp"
#include "filesystem.hpp"
#include "formatter.hpp"
#include "formula/string_utils.hpp"
#include "gettext.hpp"
#include "gui/auxiliary/tips.hpp"
#include "gui/core/log.hpp"
#include "gui/widgets/window.hpp"
#include "preferences/general.hpp"
#include "serialization/parser.hpp"
#include "serialization/preprocessor.hpp"
#include "serialization/schema_validator.hpp"
#include "wml_exception.hpp"
#include "display.hpp"
namespace gui2
{
@ -57,614 +43,29 @@ std::string sound_slider_adjust = "";
t_string has_helptip_message;
static std::vector<game_tip> tips;
std::vector<game_tip> tips;
std::vector<game_tip> get_tips()
void update_screen_size_variables()
{
return tip_of_the_day::shuffle(tips);
const SDL_Rect rect = CVideo::get_singleton().screen_area();
screen_width = rect.w;
screen_height = rect.h;
gamemap_width = screen_width;
gamemap_height = screen_height;
if(display* display = display::get_singleton()) {
const SDL_Rect rect_gm = display->map_outside_area();
if(rect_gm.w && rect_gm.h) {
gamemap_width = rect_gm.w;
gamemap_height = rect_gm.h;
gamemap_x_offset = rect_gm.x;
}
}
}
} // namespace settings
/**
* Notes on the registered widget and window lists.
*
* These lists are GUI-independent. They represent the widgets and windows
* registered from the C++ interface with @ref register_widget or @register_window.
*
* Also note these cannot be free-standing static data members within this file since
* that causes a crash for some reason.
*/
/** Returns the list of registered windows. */
static std::set<std::string>& registered_window_types()
{
static std::set<std::string> result;
return result;
}
struct registered_widget_parser
{
widget_parser_t parser;
const char* key;
};
using registered_widget_map = std::map<std::string, registered_widget_parser>;
/** Returns the list of registered widgets. */
static registered_widget_map& registered_widget_types()
{
static registered_widget_map result;
return result;
}
/**
* A GUI definiton.
*
* Each GUI contains several widgets, their definitons, and windows and controls the appearance and
* layout of each.
* Multiple GUI definitions may exist, though only a single default one is provided right now.
*/
class gui_definition
{
public:
explicit gui_definition(const config& cfg)
: id(cfg["id"])
, description(cfg["description"].t_str())
, widget_types()
, window_types()
, popup_show_delay_(0)
, popup_show_time_(0)
, help_show_time_(0)
, double_click_time_(0)
, repeat_button_repeat_time_(0)
, sound_button_click_()
, sound_toggle_button_click_()
, sound_toggle_panel_click_()
, sound_slider_adjust_()
, has_helptip_message_()
, tips_()
{
read(cfg);
}
std::string id;
t_string description;
/** Activates this gui. */
void activate() const;
using styled_widget_definition_map =
std::map<std::string, std::map<std::string, styled_widget_definition_ptr>>;
/** Map of each widget type, by id, and a sub-map of each of the type's definitions, also by id. */
styled_widget_definition_map widget_types;
/** Map of all known windows (the builder class builds a window). */
std::map<std::string, builder_window> window_types;
void load_widget_definitions(
const std::string& widget_type, const std::vector<styled_widget_definition_ptr>& definitions);
private:
void read(const config& cfg);
unsigned popup_show_delay_;
unsigned popup_show_time_;
unsigned help_show_time_;
unsigned double_click_time_;
unsigned repeat_button_repeat_time_;
std::string sound_button_click_;
std::string sound_toggle_button_click_;
std::string sound_toggle_panel_click_;
std::string sound_slider_adjust_;
t_string has_helptip_message_;
std::vector<game_tip> tips_;
};
/*WIKI
* @page = GUIToolkitWML
* @order = 1
*
* {{Autogenerated}}
*
* = GUI =
*
* The gui class contains the definitions of all widgets and windows used in
* the game. This can be seen as a skin and it allows the user to define the
* visual aspect of the various items. The visual aspect can be determined
* depending on the size of the game window.
*
* Widgets have a definition and an instance, the definition contains the
* general info/looks of a widget and the instance the actual looks. Eg the
* where the button text is placed is the same for every button, but the
* text of every button might differ.
*
* The default gui has the id ''default'' and must exist, in the default gui
* there must a definition of every widget with the id ''default'' and every
* window needs to be defined. If the definition of a widget with a certain
* id doesn't exist it will fall back to default in the current gui, if it's
* not defined there either it will fall back to the default widget in the
* default theme. That way it's possible to slowly create your own gui and
* test it.
*
* @begin{parent}{name="/"}
* @begin{tag}{name="gui"}{min="0"}{max="1"}
* The gui has the following data:
* @begin{table}{config}
* id & string & & Unique id for this gui (theme). $
* description & t_string & & Unique translatable name for this gui. $
*
* widget_types & section & & The definitions of all
* [[#widget_list|widgets]]. $
* window & section & & The definitions of all
* [[#window_list|windows]]. $
* settings & section & & The settings for the gui. $
* @end{table}
*
* <span id="widget_list"></span>List of available widgets:
* @begin{table}{widget_overview}
* Button & @macro = button_description $
* Image & @macro = image_description $
* Horizontal_listbox & @macro = horizontal_listbox_description $
* Horizontal_scrollbar & @macro = horizontal_scrollbar_description $
* Label & @macro = label_description $
* Listbox & @macro = listbox_description $
* Minimap & @macro = minimap_description $
* Multi_page & @macro = multi_page_description $
* Panel & @macro = panel_description $
* Password_box & A text box masking it's content by asterisks.
* $
* Repeating_button & @macro = repeating_button_description $
* Scroll_label & @macro = scroll_label_description $
* Slider & @macro = slider_description $
* Spacer & @macro = spacer_description $
* Stacked_widget &
* A stacked widget is a styled_widget where several widgets can be stacked on top of
* each other in the same space. This is mainly intended for over- and
* underlays. (The widget is still experimental.) $
*
* Text_box & A single line text box. $
* Tree_view & @macro = tree_view_description $
* Toggle_button &
* A kind of button with two 'states' normal and selected. This is a more
* generic widget which is used for eg checkboxes and radioboxes. $
*
* Toggle_panel &
* Like a toggle button but then as panel so can hold multiple items in a
* grid. $
*
* Tooltip & A small tooltip with help. $
* Tree_view & A tree view widget. $
* Vertical_scrollbar & A vertical scrollbar. $
* Window & A window. $
* @end{table}
*
* @end{tag}{name=gui}
* @end{parent}{name="/"}
*/
/*WIKI
* @page = GUIToolkitWML
* @order = 1
*
* @begin{parent}{name="gui/"}
* @begin{tag}{name="settings"}{min="0"}{max="1"}
* A setting section has the following variables:
* @begin{table}{config}
* popup_show_delay & unsigned & 0 & The time it take before the popup shows
* if the mouse moves over the widget. 0
* means show directly. $
* popup_show_time & unsigned & 0 &
* The time a shown popup remains visible.
* 0 means until the mouse leaves the
* widget. $
* help_show_time & unsigned & 0 & The time a shown help remains visible.
* 0 means until the mouse leaves the
* widget. $
* double_click_time & unsigned & &
* The time between two clicks to still be
* a double click. $
* repeat_button_repeat_time & unsigned & 0 &
* The time a repeating button waits before
* the next event is issued if the button
* is still pressed down. $
*
* sound_button_click & string & "" &
* The sound played if a button is
* clicked. $
* sound_toggle_button_click & string & "" &
* The sound played if a toggle button is
* clicked. $
* sound_toggle_panel_click & string & "" &
* The sound played if a toggle panel is
* clicked. Normally the toggle panels
* are the items in a listbox. If a
* toggle button is in the listbox it's
* sound is played. $
* sound_slider_adjust & string & "" &
* The sound played if a slider is
* adjusted. $
*
* has_helptip_message & t_string & &
* The string used to append the tooltip
* if there is also a helptip. The WML
* variable @$hotkey can be used to get show
* the name of the hotkey for the help. $
* @end{table}
* @end{tag}{name="settings"}
*/
/*WIKI
* @begin{tag}{name="tip"}{min="0"}{max="-1"}
* @begin{table}{config}
* source & t_string & & Author
* text & t_string & & Text of the tip.
* @end{table}
* @end{tag}{name="tip"}
* @end{parent}{name="gui/"}
*/
void gui_definition::read(const config& cfg)
{
VALIDATE(!id.empty(), missing_mandatory_wml_key("gui", "id"));
VALIDATE(!description.empty(), missing_mandatory_wml_key("gui", "description"));
DBG_GUI_P << "Parsing gui " << id << std::endl;
/** Parse widget definitions of each registered type. */
for(auto& widget_type : registered_widget_types()) {
std::vector<styled_widget_definition_ptr> definitions;
for(const auto& definition : cfg.child_range(
widget_type.second.key
? widget_type.second.key
: widget_type.first + "_definition")
) {
definitions.push_back(widget_type.second.parser(definition));
}
load_widget_definitions(widget_type.first, definitions);
}
/** Parse each window. */
for(auto& w : cfg.child_range("window")) {
window_types.emplace(w["id"], builder_window(w));
}
if(id == "default") {
// The default gui needs to define all window types since we're the
// fallback in case another gui doesn't define the window type.
for(const auto& window_type : registered_window_types()) {
const std::string error_msg(
"Window not defined in WML: '" + window_type + "'."
"Perhaps a mismatch between data and source versions. Try --data-dir <trunk-dir>");
VALIDATE(window_types.find(window_type) != window_types.end(), error_msg);
}
}
/***** settings *****/
/**
* @todo Regarding sounds:
* Need to evaluate but probably we want the widget definition be able to:
* - Override the default (and clear it). This will allow toggle buttons in a
* listbox to sound like a toggle panel.
* - Override the default and above per instance of the widget, some buttons
* can give a different sound.
*/
const config& settings = cfg.child("settings");
popup_show_delay_ = settings["popup_show_delay"];
popup_show_time_ = settings["popup_show_time"];
help_show_time_ = settings["help_show_time"];
double_click_time_ = settings["double_click_time"];
repeat_button_repeat_time_ = settings["repeat_button_repeat_time"];
VALIDATE(double_click_time_, missing_mandatory_wml_key("settings", "double_click_time"));
sound_button_click_ = settings["sound_button_click"].str();
sound_toggle_button_click_ = settings["sound_toggle_button_click"].str();
sound_toggle_panel_click_ = settings["sound_toggle_panel_click"].str();
sound_slider_adjust_ = settings["sound_slider_adjust"].str();
has_helptip_message_ = settings["has_helptip_message"];
VALIDATE(!has_helptip_message_.empty(), missing_mandatory_wml_key("[settings]", "has_helptip_message"));
tips_ = tip_of_the_day::load(cfg);
}
void gui_definition::activate() const
{
settings::popup_show_delay = popup_show_delay_;
settings::popup_show_time = popup_show_time_;
settings::help_show_time = help_show_time_;
settings::double_click_time = double_click_time_;
settings::repeat_button_repeat_time = repeat_button_repeat_time_;
settings::sound_button_click = sound_button_click_;
settings::sound_toggle_button_click = sound_toggle_button_click_;
settings::sound_toggle_panel_click = sound_toggle_panel_click_;
settings::sound_slider_adjust = sound_slider_adjust_;
settings::has_helptip_message = has_helptip_message_;
settings::tips = tips_;
}
void gui_definition::load_widget_definitions(
const std::string& widget_type, const std::vector<styled_widget_definition_ptr>& definitions)
{
for(const auto& def : definitions) {
if(widget_types[widget_type].find(def->id) != widget_types[widget_type].end()) {
ERR_GUI_P << "Skipping duplicate styled_widget definition '" << def->id << "' for '" << widget_type
<< "'\n";
continue;
}
widget_types[widget_type].emplace(def->id, def);
}
// The default GUI needs to ensure each widget has a default definition, but
// non-default GUIs can just fall back to the default definition in the default GUI.
if(this->id != "default") {
return;
}
const t_string msg(vgettext("Widget definition '$definition' doesn't contain the definition for '$id'.", {
{"definition", widget_type},
{"id", "default"}
}));
VALIDATE(widget_types[widget_type].find("default") != widget_types[widget_type].end(), msg);
}
/** Map with all known GUIs. */
static std::map<std::string, gui_definition> guis;
/** Points to the current GUI. */
static auto current_gui = guis.end();
/** Points to the default GUI. */
static auto default_gui = guis.end();
void register_window(const std::string& id)
{
// The second value of emplace is the 'was successfully added' flag.
if(!registered_window_types().emplace(id).second) {
WRN_GUI_P << "Window '" << id << "' already registered. Ignoring." << std::endl;
}
}
std::set<std::string> unit_test_access_only::get_registered_window_list()
{
return gui2::registered_window_types();
}
void load_settings()
{
LOG_GUI_G << "Setting: init gui." << std::endl;
// Init.
window::update_screen_size();
// Read and validate the WML files.
config cfg;
try {
schema_validation::schema_validator validator(filesystem::get_wml_location("gui/schema.cfg"));
preproc_map preproc(game_config::config_cache::instance().get_preproc_map());
filesystem::scoped_istream stream = preprocess_file(filesystem::get_wml_location("gui/_main.cfg"), &preproc);
read(cfg, *stream, &validator);
} catch(config::error& e) {
ERR_GUI_P << e.what() << '\n';
ERR_GUI_P << "Setting: could not read file 'data/gui/_main.cfg'." << std::endl;
} catch(const abstract_validator::error& e) {
ERR_GUI_P << "Setting: could not read file 'data/gui/schema.cfg'." << std::endl;
ERR_GUI_P << e.message;
}
// Parse GUI definitions.
for(const auto& g : cfg.child_range("gui")) {
guis.emplace(g["id"], gui_definition(g));
}
default_gui = guis.find("default");
VALIDATE(default_gui != guis.end(), _("No default gui defined."));
std::string current_theme = preferences::gui_theme();
current_gui = current_theme.empty() ? default_gui : guis.find(current_theme);
if(current_gui == guis.end()) {
ERR_GUI_P << "Missing [gui] definition for '" << current_theme << "'\n";
current_gui = default_gui;
}
current_gui->second.activate();
}
void register_widget(const std::string& id, widget_parser_t f, const char* key)
{
registered_widget_types()[id] = {f, key};
}
namespace
{
template<typename TList, typename TConv>
const typename TList::value_type& get_best_resolution(const TList& list, const TConv& get_size)
{
using resolution_t = const typename TList::value_type;
resolution_t* best_resolution = nullptr;
int best_resolution_score = std::numeric_limits<int>::min();
const int screen_w = settings::screen_width;
const int screen_h = settings::screen_height;
for(const auto& res : list) {
point size = get_size(res);
int w = size.x ? size.x : 1;
int h = size.y ? size.y : 1;
int score = 0;
if(w <= screen_w && h <= screen_h) {
score = w * h;
} else {
// Negative score, only used in case none of the given resolution fits on the screen (workaround of a bug
// where the windows size can become < 800x600).
score = std::min(screen_w - w, 0) + std::min(screen_h - h, 0);
}
if(score >= best_resolution_score) {
best_resolution = &res;
best_resolution_score = score;
}
}
assert(best_resolution != nullptr);
return *best_resolution;
}
} // namespace
resolution_definition_ptr get_control(const std::string& control_type, const std::string& definition)
{
const auto& current_types = current_gui->second.widget_types;
const auto& default_types = default_gui->second.widget_types;
#ifdef GUI2_EXPERIMENTAL_LISTBOX
const auto widget_definitions = (control_type == "list")
? current_types.find("listbox")
: current_types.find(control_type);
#else
const auto widget_definitions
= current_types.find(control_type);
#endif
std::map<std::string, styled_widget_definition_ptr>::const_iterator control;
if(widget_definitions == current_types.end()) {
goto fallback;
}
control = widget_definitions->second.find(definition);
if(control == widget_definitions->second.end()) {
fallback:
bool found_fallback = false;
if(current_gui != default_gui) {
#ifdef GUI2_EXPERIMENTAL_LISTBOX
auto default_widget_definitions = (control_type == "list")
? default_types.find("listbox")
: default_types.find(control_type);
#else
auto default_widget_definitions
= default_types.find(control_type);
#endif
VALIDATE(widget_definitions != current_types.end(),
formatter() << "Type '" << control_type << "' is unknown.");
control = default_widget_definitions->second.find(definition);
found_fallback = control != default_widget_definitions->second.end();
}
if(!found_fallback) {
if(definition != "default") {
LOG_GUI_G << "Control: type '" << control_type << "' definition '" << definition
<< "' not found, falling back to 'default'.\n";
return get_control(control_type, "default");
}
FAIL(formatter() << "default definition not found for styled_widget " << control_type);
}
}
const auto& resolutions = (*control->second).resolutions;
VALIDATE(!resolutions.empty(),
formatter() << "Control: type '" << control_type << "' definition '" << definition << "' has no resolutions.\n");
return get_best_resolution(resolutions, [&](const resolution_definition_ptr& ptr) {
return point {
static_cast<int>(ptr->window_width),
static_cast<int>(ptr->window_height)
};
});
}
const builder_window::window_resolution& get_window_builder(const std::string& type)
{
window::update_screen_size();
auto window = current_gui->second.window_types.find(type);
if(window == current_gui->second.window_types.end()) {
// Current GUI is the default one and no window type was found. Throw.
if(current_gui == default_gui) {
throw window_builder_invalid_id();
}
// Else, try again to find the window, this time in the default GUI.
window = default_gui->second.window_types.find(type);
if(window == default_gui->second.window_types.end()) {
throw window_builder_invalid_id();
}
}
const auto& resolutions = window->second.resolutions;
VALIDATE(!resolutions.empty(), formatter() << "Window '" << type << "' has no resolutions.\n");
return get_best_resolution(resolutions, [&](const builder_window::window_resolution& res) {
return point {
static_cast<int>(res.window_width),
static_cast<int>(res.window_height)
};
});
}
/*WIKI
* @page = GUIWidgetDefinitionWML
* @order = ZZZZZZ_footer
*
* [[Category: WML Reference]]
* [[Category: GUI WML Reference]]
*
*/
bool add_single_widget_definition(const std::string& widget_type, const std::string& definition_id, const config& cfg)
{
auto& gui = default_gui->second;
auto parser = registered_widget_types().find(widget_type);
if(parser == registered_widget_types().end()) {
throw std::invalid_argument("widget '" + widget_type + "' doesn't exist");
}
if(gui.widget_types[widget_type].find(definition_id) != gui.widget_types[widget_type].end()) {
return false;
}
gui.widget_types[widget_type].emplace(definition_id, parser->second.parser(cfg));
return true;
}
void remove_single_widget_definition(const std::string& widget_type, const std::string& definition_id)
{
auto& definition_map = default_gui->second.widget_types[widget_type];
auto it = definition_map.find(definition_id);
if(it != definition_map.end()) {
definition_map.erase(it);
}
}
} // namespace gui2

View file

@ -19,10 +19,7 @@
#pragma once
#include "utils/functional.hpp"
#include "config.hpp"
#include "gui/core/widget_definition.hpp"
#include "gui/core/window_builder.hpp"
#include "gui/auxiliary/tips.hpp"
#include "tstring.hpp"
#include <string>
@ -37,106 +34,9 @@ class game_tip;
/** Do we wish to use the new library or not. */
extern bool new_widgets;
/**
* Registers a window.
*
* This function registers the available windows defined in WML. All windows
* need to register themselves before @ref gui2::init) is called.
*
* @warning This function runs before @ref main() so needs to be careful
* regarding the static initialization problem.
*
* @note Double registering a window can't hurt, but no way to probe for it,
* this can be added if needed. The same for an unregister function.
*
* @param id The id of the window to register.
*/
void register_window(const std::string& id);
/**
* Special helper class to get the list of registered windows.
*
* This is used in the unit tests, but these implementation details shouldn't
* be used in the normal code.
*/
class unit_test_access_only
{
friend std::set<std::string>& unit_test_registered_window_list();
/** Returns a copy of the list of registered windows. */
static std::set<std::string> get_registered_window_list();
};
/** Function type alias for @ref register_widget. */
using widget_parser_t = std::function<styled_widget_definition_ptr(const config&)>;
/**
* Registers a widget.
*
* This function registers the available widgets defined in WML. All widgets
* need to register themselves before @ref gui2::init) is called.
*
* @warning This function runs before @ref main() so needs to be careful
* regarding the static initialization problem.
*
* @param id The id of the widget to register.
* @param f The function to parse the definition config.
* @param key The tagname from which to read the widget's definition in the game config.
* If nullptr the default [<id>_definition] is used.
*/
void register_widget(const std::string& id, widget_parser_t f, const char* key = nullptr);
/**
* Loads the definitions of a widget.
*
* @param gui The gui definition the widget definition
* belongs to.
* @param definition_type The type of the widget whose definitions are
* to be loaded.
* @param definitions The definitions serialized from a config
* object.
*/
void load_widget_definitions(
gui_definition& gui,
const std::string& definition_type,
const std::vector<styled_widget_definition_ptr>& definitions);
resolution_definition_ptr get_control(const std::string& control_type,
const std::string& definition);
bool add_single_widget_definition(const std::string& widget_type, const std::string& definition_id, const config& cfg);
void remove_single_widget_definition(const std::string& widget_type, const std::string& definition_id);
/** Helper struct to signal that get_window_builder failed. */
struct window_builder_invalid_id
{
};
/**
* Returns an reference to the requested builder.
*
* The builder is determined by the @p type and the current screen resolution.
*
* @pre There is a valid builder for @p type at the
* current resolution.
*
* @throw window_builder_invalid_id
* When the precondition is violated.
*
* @param type The type of builder window to get.
*
* @returns An iterator to the requested builder.
*/
const builder_window::window_resolution& get_window_builder(const std::string& type);
/** Loads the setting for the theme. */
void load_settings();
/** This namespace contains the 'global' settings. */
namespace settings
{
/**
* The screen resolution should be available for all widgets since
* their drawing method will depend on it.
@ -170,7 +70,17 @@ extern std::string sound_slider_adjust;
extern t_string has_helptip_message;
std::vector<game_tip> get_tips();
extern std::vector<game_tip> tips;
/**
* Update the size of the screen variables in settings.
*
* Before a window gets build the screen sizes need to be updated. This
* function does that. It's only done when no other window is active, if
* another window is active it already updates the sizes with it's resize
* event.
*/
void update_screen_size_variables();
}
} // namespace gui2

View file

@ -152,7 +152,7 @@ builder_size_lock::builder_size_lock(const config& cfg) :
builder_styled_widget(cfg), content_(nullptr), width_(cfg["width"]), height_(cfg["height"])
{
VALIDATE(cfg.has_child("widget"), _("No widget defined."));
content_ = create_builder_widget(cfg.child("widget"));
content_ = create_widget_builder(cfg.child("widget"));
}
widget* builder_size_lock::build() const

View file

@ -21,6 +21,7 @@
#include "gettext.hpp"
#include "gui/auxiliary/iterator/walker_widget.hpp"
#include "gui/core/event/message.hpp"
#include "gui/core/gui_definition.hpp"
#include "gui/core/log.hpp"
#include "gui/dialogs/tooltip.hpp"
#include "gui/widgets/settings.hpp"

View file

@ -220,7 +220,7 @@ namespace implementation
builder_viewport::builder_viewport(const config& cfg)
: builder_widget(cfg)
, widget_(create_builder_widget(cfg.child("widget", "[viewport]")))
, widget_(create_widget_builder(cfg.child("widget", "[viewport]")))
{
}

View file

@ -23,7 +23,6 @@
#include "config.hpp"
#include "cursor.hpp"
#include "display.hpp"
#include "events.hpp"
#include "floating_label.hpp"
#include "formula/callable.hpp"
@ -123,7 +122,7 @@ const unsigned LAYOUT = 0;
/**
* Pushes a single draw event to the queue. To be used before calling
* events::pump when drawing windows.
*
*
* @todo: in the future we should simply call draw functions directly
* from events::pump and do away with the custom drawing events, but
* that's a 1.15 target. For now, this will have to do.
@ -427,28 +426,6 @@ window* window::window_instance(const unsigned handle)
return manager::instance().get_window(handle);
}
void window::update_screen_size()
{
const SDL_Rect rect = CVideo::get_singleton().screen_area();
settings::screen_width = rect.w;
settings::screen_height = rect.h;
settings::gamemap_width = settings::screen_width;
settings::gamemap_height = settings::screen_height;
display* display = display::get_singleton();
if(display) {
const SDL_Rect rect_gm = display->map_outside_area();
if(rect_gm.w && rect_gm.h) {
settings::gamemap_width = rect_gm.w;
settings::gamemap_height = rect_gm.h;
settings::gamemap_x_offset = rect_gm.x;
}
}
}
/*WIKI
* @page = GUIToolkitWML
* @order = 3_widget_window_2

View file

@ -72,16 +72,6 @@ public:
~window();
/**
* Update the size of the screen variables in settings.
*
* Before a window gets build the screen sizes need to be updated. This
* function does that. It's only done when no other window is active, if
* another window is active it already updates the sizes with it's resize
* event.
*/
static void update_screen_size();
/**
* Returns the instance of a window.
*

View file

@ -16,6 +16,7 @@
#include "gui/auxiliary/old_markup.hpp"
#include "gui/core/canvas.hpp"
#include "gui/core/gui_definition.hpp"
#include "gui/core/window_builder.hpp"
#include "gui/dialogs/drop_down_menu.hpp"
#include "gui/dialogs/gamestate_inspector.hpp"
@ -30,7 +31,6 @@
#include "gui/widgets/multimenu_button.hpp"
#include "gui/widgets/progress_bar.hpp"
#include "gui/widgets/selectable_item.hpp"
#include "gui/widgets/settings.hpp"
#include "gui/widgets/slider.hpp"
#include "gui/widgets/stacked_widget.hpp"
#include "gui/widgets/text_box.hpp"

View file

@ -17,7 +17,7 @@
#include "exceptions.hpp"
#include "game_config.hpp"
#include "game_errors.hpp"
#include "gui/widgets/settings.hpp"
#include "gui/core/gui_definition.hpp"
#include "log.hpp"
#include "lua_jailbreak_exception.hpp" // for lua_jailbreak_exception
#include "random.hpp"

View file

@ -129,9 +129,7 @@ namespace gui2 {
std::set<std::string>& unit_test_registered_window_list()
{
static std::set<std::string> result =
unit_test_access_only::get_registered_window_list();
static std::set<std::string> result = registered_window_types();
return result;
}

View file

@ -41,7 +41,7 @@
#include "game_config.hpp"
#include "game_errors.hpp"
#include "gui/core/event/handler.hpp"
#include "gui/widgets/helper.hpp"
#include "gui/gui.hpp"
#include "config.hpp"
#include "log.hpp"

View file

@ -28,7 +28,7 @@
#include "language.hpp"
#include "units/types.hpp"
#include "gui/widgets/helper.hpp"
#include "gui/gui.hpp"
#include <clocale>

View file

@ -33,7 +33,7 @@
#include "gui/dialogs/loading_screen.hpp"
#include "gui/dialogs/message.hpp" // for show_error_message
#include "gui/dialogs/title_screen.hpp" // for title_screen, etc
#include "gui/widgets/helper.hpp" // for init
#include "gui/gui.hpp" // for init
#include "image.hpp" // for flush_cache, etc
#include "log.hpp" // for LOG_STREAM, general, logger, etc
#include "preferences/general.hpp" // for core_id, etc