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:
parent
2c154b580d
commit
6759f41b1e
32 changed files with 891 additions and 832 deletions
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
331
src/gui/core/gui_definition.cpp
Normal file
331
src/gui/core/gui_definition.cpp
Normal 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
|
138
src/gui/core/gui_definition.hpp
Normal file
138
src/gui/core/gui_definition.hpp
Normal 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
|
|
@ -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); }); \
|
||||
} \
|
||||
}; \
|
||||
|
|
65
src/gui/core/static_registry.cpp
Normal file
65
src/gui/core/static_registry.cpp
Normal 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
|
123
src/gui/core/static_registry.hpp
Normal file
123
src/gui/core/static_registry.hpp
Normal 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
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "gui/auxiliary/field-fwd.hpp"
|
||||
|
||||
#include "gui/core/static_registry.hpp"
|
||||
#include "utils/functional.hpp"
|
||||
|
||||
#include <string>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
98
src/gui/gui.cpp
Normal 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
27
src/gui/gui.hpp
Normal 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
|
|
@ -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};
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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]")))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "language.hpp"
|
||||
#include "units/types.hpp"
|
||||
|
||||
#include "gui/widgets/helper.hpp"
|
||||
#include "gui/gui.hpp"
|
||||
|
||||
#include <clocale>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue