mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-21 21:55:38 +00:00
First part of the theming system rewrite.
This commit is contained in:
parent
6f442556c0
commit
7f46e50688
|
@ -233,6 +233,7 @@ set(ES_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData_applicators.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.cpp
|
||||
|
|
|
@ -47,7 +47,14 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con
|
|||
mRootFolder->sort(FileSorts::SortTypes.at(0));
|
||||
|
||||
mTheme = std::make_shared<ThemeData>();
|
||||
mTheme->loadFile(getThemePath());
|
||||
try
|
||||
{
|
||||
mTheme->loadFile(getThemePath());
|
||||
} catch(ThemeException& e)
|
||||
{
|
||||
LOG(LogError) << e.what();
|
||||
mTheme = std::make_shared<ThemeData>(); // reset to empty
|
||||
}
|
||||
}
|
||||
|
||||
SystemData::~SystemData()
|
||||
|
|
|
@ -4,81 +4,125 @@
|
|||
#include "Sound.h"
|
||||
#include "resources/TextureResource.h"
|
||||
#include "Log.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/assign.hpp>
|
||||
#include "pugiXML/pugixml.hpp"
|
||||
#include <boost/assign.hpp>
|
||||
|
||||
// Defaults
|
||||
std::map<std::string, FontDef > ThemeData::sDefaultFonts = boost::assign::map_list_of
|
||||
("listFont", FontDef(0.045f, ""))
|
||||
("descriptionFont", FontDef(0.035f, ""))
|
||||
("fastSelectLetterFont", FontDef(0.15f, ""));
|
||||
std::map< std::string, std::map<std::string, ThemeData::ElementPropertyType> > ThemeData::sElementMap = boost::assign::map_list_of
|
||||
("image", boost::assign::map_list_of
|
||||
("pos", NORMALIZED_PAIR)
|
||||
("size", NORMALIZED_PAIR)
|
||||
("origin", NORMALIZED_PAIR)
|
||||
("path", PATH)
|
||||
("tile", BOOLEAN))
|
||||
("text", boost::assign::map_list_of
|
||||
("pos", NORMALIZED_PAIR)
|
||||
("size", NORMALIZED_PAIR)
|
||||
("text", STRING)
|
||||
("color", COLOR)
|
||||
("fontPath", PATH)
|
||||
("fontSize", FLOAT)
|
||||
("center", BOOLEAN))
|
||||
("textlist", boost::assign::map_list_of
|
||||
("pos", NORMALIZED_PAIR)
|
||||
("size", NORMALIZED_PAIR)
|
||||
("selectorColor", COLOR)
|
||||
("selectedColor", COLOR)
|
||||
("primaryColor", COLOR)
|
||||
("secondaryColor", COLOR)
|
||||
("fontPath", PATH)
|
||||
("fontSize", FLOAT))
|
||||
("sound", boost::assign::map_list_of
|
||||
("path", PATH));
|
||||
|
||||
std::map<std::string, unsigned int> ThemeData::sDefaultColors = boost::assign::map_list_of
|
||||
("listPrimaryColor", 0x0000FFFF)
|
||||
("listSecondaryColor", 0x00FF00FF)
|
||||
("listSelectorColor", 0x000000FF)
|
||||
("listSelectedColor", 0x00000000)
|
||||
("descriptionColor", 0x48474DFF)
|
||||
("fastSelectLetterColor", 0xFFFFFFFF)
|
||||
("fastSelectTextColor", 0xDDDDDDFF);
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
std::map<std::string, ImageDef> ThemeData::sDefaultImages = boost::assign::map_list_of
|
||||
("backgroundImage", ImageDef("", true))
|
||||
("headerImage", ImageDef("", false))
|
||||
("infoBackgroundImage", ImageDef("", false))
|
||||
("verticalDividerImage", ImageDef("", false))
|
||||
("fastSelectBackgroundImage", ImageDef(":/button.png", false))
|
||||
("systemImage", ImageDef("", false));
|
||||
#define MINIMUM_THEME_VERSION 3
|
||||
#define CURRENT_THEME_VERSION 3
|
||||
|
||||
std::map<std::string, SoundDef> ThemeData::sDefaultSounds = boost::assign::map_list_of
|
||||
("scrollSound", SoundDef(""))
|
||||
("gameSelectSound", SoundDef(""))
|
||||
("backSound", SoundDef(""))
|
||||
("menuOpenSound", SoundDef(""))
|
||||
("menuCloseSound", SoundDef(""));
|
||||
|
||||
|
||||
|
||||
const std::shared_ptr<ThemeData>& ThemeData::getDefault()
|
||||
{
|
||||
static const std::shared_ptr<ThemeData> def = std::shared_ptr<ThemeData>(new ThemeData());
|
||||
return def;
|
||||
}
|
||||
// still TODO:
|
||||
// * how to do <include>?
|
||||
|
||||
ThemeData::ThemeData()
|
||||
{
|
||||
setDefaults();
|
||||
|
||||
std::string defaultDir = getHomePath() + "/.emulationstation/es_theme_default.xml";
|
||||
if(boost::filesystem::exists(defaultDir))
|
||||
loadFile(defaultDir);
|
||||
mVersion = 0;
|
||||
}
|
||||
|
||||
void ThemeData::setDefaults()
|
||||
void ThemeData::loadFile(const std::string& path)
|
||||
{
|
||||
mFontMap.clear();
|
||||
mImageMap.clear();
|
||||
mColorMap.clear();
|
||||
mSoundMap.clear();
|
||||
ThemeException error;
|
||||
error.setFile(path);
|
||||
|
||||
mFontMap = sDefaultFonts;
|
||||
mImageMap = sDefaultImages;
|
||||
mColorMap = sDefaultColors;
|
||||
mSoundMap = sDefaultSounds;
|
||||
mPath = path;
|
||||
|
||||
if(!fs::exists(path))
|
||||
throw error << "Missing file!";
|
||||
|
||||
mVersion = 0;
|
||||
mViews.clear();
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
||||
if(!res)
|
||||
throw error << "XML parsing error: \n " << res.description();
|
||||
|
||||
pugi::xml_node root = doc.child("theme");
|
||||
if(!root)
|
||||
throw error << "Missing <theme> tag!";
|
||||
|
||||
// parse version
|
||||
mVersion = root.child("version").text().as_float(-404);
|
||||
if(mVersion == -404)
|
||||
throw error << "<version> tag missing!\n It's either out of date or you need to add <version>" << CURRENT_THEME_VERSION << "</version> inside your <theme> tag.";
|
||||
|
||||
|
||||
if(mVersion < MINIMUM_THEME_VERSION)
|
||||
throw error << "Theme is version " << mVersion << ". Minimum supported version is " << MINIMUM_THEME_VERSION << ".";
|
||||
|
||||
// parse views
|
||||
for(pugi::xml_node node = root.child("view"); node; node = node.next_sibling("view"))
|
||||
{
|
||||
if(!node.attribute("name"))
|
||||
throw error << "View missing \"name\" attribute!";
|
||||
|
||||
ThemeView view = parseView(node);
|
||||
|
||||
if(view.elements.size() > 0)
|
||||
mViews[node.attribute("name").as_string()] = view;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getHexColor(const char* str, unsigned int defaultColor)
|
||||
ThemeData::ThemeView ThemeData::parseView(const pugi::xml_node& root)
|
||||
{
|
||||
ThemeView view;
|
||||
ThemeException error;
|
||||
error.setFile(mPath.string());
|
||||
|
||||
for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling())
|
||||
{
|
||||
if(!node.attribute("name"))
|
||||
throw error << "Element of type \"" << node.name() << "\" missing \"name\" attribute!";
|
||||
|
||||
auto elemTypeIt = sElementMap.find(node.name());
|
||||
if(elemTypeIt == sElementMap.end())
|
||||
throw error << "Unknown element of type \"" << node.name() << "\"!";
|
||||
|
||||
ThemeElement element = parseElement(node, elemTypeIt->second);
|
||||
|
||||
view.elements[node.attribute("name").as_string()] = element;
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
unsigned int getHexColor(const char* str)
|
||||
{
|
||||
ThemeException error;
|
||||
if(!str)
|
||||
return defaultColor;
|
||||
throw error << "Empty color";
|
||||
|
||||
size_t len = strlen(str);
|
||||
if(len != 6 && len != 8)
|
||||
{
|
||||
LOG(LogError) << "Invalid theme color \"" << str << "\" (must be 6 or 8 characters)";
|
||||
return defaultColor;
|
||||
}
|
||||
throw error << "Invalid color (bad length, \"" << str << "\" - must be 6 or 8)";
|
||||
|
||||
unsigned int val;
|
||||
std::stringstream ss;
|
||||
|
@ -91,13 +135,12 @@ unsigned int getHexColor(const char* str, unsigned int defaultColor)
|
|||
return val;
|
||||
}
|
||||
|
||||
std::string resolvePath(const char* in, const std::string& relative)
|
||||
std::string resolvePath(const char* in, const fs::path& relative)
|
||||
{
|
||||
if(!in || in[0] == '\0')
|
||||
return in;
|
||||
|
||||
boost::filesystem::path relPath(relative);
|
||||
relPath = relPath.parent_path();
|
||||
fs::path relPath = relative.parent_path();
|
||||
|
||||
boost::filesystem::path path(in);
|
||||
|
||||
|
@ -114,102 +157,63 @@ std::string resolvePath(const char* in, const std::string& relative)
|
|||
return path.generic_string();
|
||||
}
|
||||
|
||||
void ThemeData::loadFile(const std::string& themePath)
|
||||
|
||||
ThemeData::ThemeElement ThemeData::parseElement(const pugi::xml_node& root, const std::map<std::string, ElementPropertyType>& typeMap)
|
||||
{
|
||||
if(themePath.empty() || !boost::filesystem::exists(themePath))
|
||||
return;
|
||||
ThemeException error;
|
||||
error.setFile(mPath.string());
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(themePath.c_str());
|
||||
if(!result)
|
||||
ThemeElement element;
|
||||
element.extra = root.attribute("extra").as_bool(false);
|
||||
|
||||
for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling())
|
||||
{
|
||||
LOG(LogWarning) << "Could not parse theme file \"" << themePath << "\":\n " << result.description();
|
||||
return;
|
||||
}
|
||||
auto typeIt = typeMap.find(node.name());
|
||||
if(typeIt == typeMap.end())
|
||||
throw error << "Unknown property type \"" << node.name() << "\" (for element of type " << root.name() << ").";
|
||||
|
||||
pugi::xml_node root = doc.child("theme");
|
||||
|
||||
// Fonts
|
||||
for(auto it = mFontMap.begin(); it != mFontMap.end(); it++)
|
||||
{
|
||||
pugi::xml_node node = root.child(it->first.c_str());
|
||||
if(node)
|
||||
switch(typeIt->second)
|
||||
{
|
||||
std::string path = resolvePath(node.child("path").text().as_string(it->second.path.c_str()), themePath);
|
||||
if(!boost::filesystem::exists(path))
|
||||
{
|
||||
LOG(LogWarning) << "Font \"" << path << "\" doesn't exist!";
|
||||
path = it->second.path;
|
||||
}
|
||||
case NORMALIZED_PAIR:
|
||||
{
|
||||
std::string str = std::string(node.text().as_string());
|
||||
|
||||
float size = node.child("size").text().as_float(it->second.size);
|
||||
mFontMap[it->first] = FontDef(size, path);
|
||||
root.remove_child(node);
|
||||
size_t divider = str.find(' ');
|
||||
if(divider == std::string::npos)
|
||||
throw error << "invalid normalized pair (\"" << str.c_str() << "\")";
|
||||
|
||||
std::string first = str.substr(0, divider);
|
||||
std::string second = str.substr(divider, std::string::npos);
|
||||
|
||||
Eigen::Vector2f val(atof(first.c_str()), atof(second.c_str()));
|
||||
|
||||
element.properties[node.name()] = val;
|
||||
break;
|
||||
}
|
||||
case STRING:
|
||||
element.properties[node.name()] = std::string(node.text().as_string());
|
||||
break;
|
||||
case PATH:
|
||||
{
|
||||
std::string path = resolvePath(node.text().as_string(), mPath.string());
|
||||
if(!fs::exists(path))
|
||||
LOG(LogWarning) << " Warning: theme \"" << mPath << "\" - could not find file \"" << node.text().get() << "\" (resolved to \"" << path << "\")";
|
||||
element.properties[node.name()] = path;
|
||||
break;
|
||||
}
|
||||
case COLOR:
|
||||
element.properties[node.name()] = getHexColor(node.text().as_string());
|
||||
break;
|
||||
case FLOAT:
|
||||
element.properties[node.name()] = node.text().as_float();
|
||||
break;
|
||||
case BOOLEAN:
|
||||
element.properties[node.name()] = node.text().as_bool();
|
||||
break;
|
||||
default:
|
||||
throw error << "Unknown ElementPropertyType for " << root.attribute("name").as_string() << " property " << node.name();
|
||||
}
|
||||
}
|
||||
|
||||
// Images
|
||||
for(auto it = mImageMap.begin(); it != mImageMap.end(); it++)
|
||||
{
|
||||
pugi::xml_node node = root.child(it->first.c_str());
|
||||
if(node)
|
||||
{
|
||||
std::string path = resolvePath(node.child("path").text().as_string(it->second.path.c_str()), themePath);
|
||||
if(!boost::filesystem::exists(path))
|
||||
{
|
||||
LOG(LogWarning) << "Image \"" << path << "\" doesn't exist!";
|
||||
path = it->second.path;
|
||||
}
|
||||
|
||||
bool tile = node.child("tile").text().as_bool(it->second.tile);
|
||||
mImageMap[it->first] = ImageDef(path, tile);
|
||||
root.remove_child(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Colors
|
||||
for(auto it = mColorMap.begin(); it != mColorMap.end(); it++)
|
||||
{
|
||||
pugi::xml_node node = root.child(it->first.c_str());
|
||||
if(node)
|
||||
{
|
||||
mColorMap[it->first] = getHexColor(node.text().as_string(NULL), it->second);
|
||||
root.remove_child(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Sounds
|
||||
for(auto it = mSoundMap.begin(); it != mSoundMap.end(); it++)
|
||||
{
|
||||
pugi::xml_node node = root.child(it->first.c_str());
|
||||
if(node)
|
||||
{
|
||||
std::string path = resolvePath(node.text().as_string(it->second.path.c_str()), themePath);
|
||||
if(!boost::filesystem::exists(path))
|
||||
{
|
||||
LOG(LogWarning) << "Sound \"" << path << "\" doesn't exist!";
|
||||
path = it->second.path;
|
||||
}
|
||||
|
||||
mSoundMap[it->first] = SoundDef(path);
|
||||
root.remove_child(node);
|
||||
}
|
||||
}
|
||||
|
||||
if(root.begin() != root.end())
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Unused theme identifiers:\n";
|
||||
for(auto it = root.children().begin(); it != root.children().end(); it++)
|
||||
{
|
||||
ss << " " << it->name() << "\n";
|
||||
}
|
||||
|
||||
LOG(LogWarning) << ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeData::playSound(const std::string& identifier) const
|
||||
{
|
||||
mSoundMap.at(identifier).get()->play();
|
||||
return element;
|
||||
}
|
||||
|
|
174
src/ThemeData.h
174
src/ThemeData.h
|
@ -1,82 +1,146 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "resources/Font.h"
|
||||
#include "resources/TextureResource.h"
|
||||
#include "Renderer.h"
|
||||
#include "AudioManager.h"
|
||||
#include "Sound.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <Eigen/Dense>
|
||||
#include "pugiXML/pugixml.hpp"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
struct FontDef
|
||||
template<typename T>
|
||||
class TextListComponent;
|
||||
|
||||
class Sound;
|
||||
class ImageComponent;
|
||||
class NinePatchComponent;
|
||||
class TextComponent;
|
||||
class Window;
|
||||
|
||||
namespace ThemeFlags
|
||||
{
|
||||
FontDef() {}
|
||||
FontDef(float sz, const std::string& p) : path(p), size(sz) {}
|
||||
enum PropertyFlags : unsigned int
|
||||
{
|
||||
PATH = 1,
|
||||
POSITION = 2,
|
||||
SIZE = 4,
|
||||
ORIGIN = 8,
|
||||
COLOR = 16,
|
||||
FONT_PATH = 32,
|
||||
FONT_SIZE = 64,
|
||||
TILING = 128,
|
||||
SOUND = 256,
|
||||
CENTER = 512,
|
||||
TEXT = 1024
|
||||
};
|
||||
}
|
||||
|
||||
std::string path;
|
||||
float size;
|
||||
class ThemeException : public std::exception
|
||||
{
|
||||
protected:
|
||||
std::string msg;
|
||||
|
||||
inline const std::shared_ptr<Font>& get() const { if(!font) font = Font::get((int)(size * Renderer::getScreenHeight()), path); return font; }
|
||||
public:
|
||||
virtual const char* what() const throw() { return msg.c_str(); }
|
||||
|
||||
private:
|
||||
mutable std::shared_ptr<Font> font;
|
||||
template<typename T>
|
||||
friend ThemeException& operator<<(ThemeException& e, T msg);
|
||||
|
||||
inline void setFile(const std::string& filename) { *this << "Error loading theme from \"" << filename << "\":\n "; }
|
||||
};
|
||||
|
||||
struct ImageDef
|
||||
template<typename T>
|
||||
ThemeException& operator<<(ThemeException& e, T appendMsg)
|
||||
{
|
||||
ImageDef() {}
|
||||
ImageDef(const std::string& p, bool t) : path(p), tile(t) {}
|
||||
|
||||
std::string path;
|
||||
bool tile;
|
||||
|
||||
inline const std::shared_ptr<TextureResource>& getTexture() const { if(!texture && !path.empty()) texture = TextureResource::get(path); return texture; }
|
||||
|
||||
private:
|
||||
mutable std::shared_ptr<TextureResource> texture;
|
||||
};
|
||||
|
||||
struct SoundDef
|
||||
{
|
||||
SoundDef() {}
|
||||
SoundDef(const std::string& p) : path(p) { sound = std::shared_ptr<Sound>(new Sound(path)); AudioManager::getInstance()->registerSound(sound); }
|
||||
|
||||
std::string path;
|
||||
|
||||
inline const std::shared_ptr<Sound>& get() const { return sound; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Sound> sound;
|
||||
};
|
||||
std::stringstream ss;
|
||||
ss << e.msg << appendMsg;
|
||||
e.msg = ss.str();
|
||||
return e;
|
||||
}
|
||||
|
||||
class ThemeData
|
||||
{
|
||||
private:
|
||||
|
||||
class ThemeElement
|
||||
{
|
||||
public:
|
||||
bool extra;
|
||||
std::string type;
|
||||
|
||||
std::map< std::string, boost::variant<Eigen::Vector2f, std::string, unsigned int, float, bool> > properties;
|
||||
|
||||
template<typename T>
|
||||
T get(const std::string& prop) { return boost::get<T>(properties.at(prop)); }
|
||||
|
||||
inline bool has(const std::string& prop) { return (properties.find(prop) != properties.end()); }
|
||||
};
|
||||
|
||||
class ThemeView
|
||||
{
|
||||
public:
|
||||
ThemeView() : mExtrasDirty(true) {}
|
||||
virtual ~ThemeView() { for(auto it = mExtras.begin(); it != mExtras.end(); it++) delete *it; }
|
||||
|
||||
std::map<std::string, ThemeElement> elements;
|
||||
|
||||
const std::vector<GuiComponent*>& getExtras(Window* window);
|
||||
|
||||
private:
|
||||
bool mExtrasDirty;
|
||||
std::vector<GuiComponent*> mExtras;
|
||||
};
|
||||
|
||||
public:
|
||||
static const std::shared_ptr<ThemeData>& getDefault();
|
||||
|
||||
ThemeData();
|
||||
|
||||
void setDefaults();
|
||||
// throws ThemeException
|
||||
void loadFile(const std::string& path);
|
||||
|
||||
inline const FontDef& getFontDef(const std::string& identifier) const { return mFontMap.at(identifier); }
|
||||
inline std::shared_ptr<Font> getFont(const std::string& identifier) const { return mFontMap.at(identifier).get(); }
|
||||
inline const ImageDef& getImage(const std::string& identifier) const { return mImageMap.at(identifier); }
|
||||
inline unsigned int getColor(const std::string& identifier) const { return mColorMap.at(identifier); }
|
||||
void playSound(const std::string& identifier) const;
|
||||
enum ElementPropertyType
|
||||
{
|
||||
NORMALIZED_PAIR,
|
||||
PATH,
|
||||
STRING,
|
||||
COLOR,
|
||||
FLOAT,
|
||||
BOOLEAN
|
||||
};
|
||||
|
||||
inline void setFont(const std::string& identifier, FontDef def) { mFontMap[identifier] = def; }
|
||||
inline void setColor(const std::string& identifier, unsigned int color) { mColorMap[identifier] = color; }
|
||||
void renderExtras(const std::string& view, Window* window, const Eigen::Affine3f& transform);
|
||||
|
||||
void applyToImage(const std::string& view, const std::string& element, ImageComponent* image, unsigned int properties);
|
||||
void applyToNinePatch(const std::string& view, const std::string& element, NinePatchComponent* patch, unsigned int properties);
|
||||
void applyToText(const std::string& view, const std::string& element, TextComponent* text, unsigned int properties);
|
||||
|
||||
template <typename T>
|
||||
void applyToTextList(const std::string& view, const std::string& element, TextListComponent<T>* list, unsigned int properties);
|
||||
|
||||
void playSound(const std::string& name);
|
||||
|
||||
private:
|
||||
static std::map<std::string, ImageDef> sDefaultImages;
|
||||
static std::map<std::string, unsigned int> sDefaultColors;
|
||||
static std::map<std::string, FontDef > sDefaultFonts;
|
||||
static std::map<std::string, SoundDef> sDefaultSounds;
|
||||
static std::map< std::string, std::map<std::string, ElementPropertyType> > sElementMap;
|
||||
|
||||
std::map<std::string, ImageDef> mImageMap;
|
||||
std::map<std::string, unsigned int> mColorMap;
|
||||
std::map<std::string, FontDef > mFontMap;
|
||||
std::map< std::string, SoundDef > mSoundMap;
|
||||
boost::filesystem::path mPath;
|
||||
float mVersion;
|
||||
|
||||
ThemeView parseView(const pugi::xml_node& view);
|
||||
ThemeElement parseElement(const pugi::xml_node& element, const std::map<std::string, ElementPropertyType>& typeMap);
|
||||
|
||||
ThemeElement* getElement(const std::string& viewName, const std::string& elementName);
|
||||
|
||||
std::map<std::string, ThemeView> mViews;
|
||||
|
||||
std::map< std::string, std::shared_ptr<Sound> > mSoundCache;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
void ThemeData::applyToTextList(const std::string& view, const std::string& element, TextListComponent<T>* list, unsigned int properties)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
135
src/ThemeData_applicators.cpp
Normal file
135
src/ThemeData_applicators.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "ThemeData.h"
|
||||
|
||||
#include "Renderer.h"
|
||||
#include "components/ImageComponent.h"
|
||||
#include "components/NinePatchComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "Sound.h"
|
||||
#include "Log.h"
|
||||
|
||||
using namespace ThemeFlags;
|
||||
|
||||
Eigen::Vector2f getScale(GuiComponent* comp)
|
||||
{
|
||||
if(comp && comp->getParent())
|
||||
return comp->getParent()->getSize();
|
||||
|
||||
return Eigen::Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
}
|
||||
|
||||
ThemeData::ThemeElement* ThemeData::getElement(const std::string& viewName, const std::string& elementName)
|
||||
{
|
||||
auto viewIt = mViews.find(viewName);
|
||||
if(viewIt == mViews.end())
|
||||
return NULL;
|
||||
|
||||
auto elemIt = viewIt->second.elements.find(elementName);
|
||||
if(elemIt == viewIt->second.elements.end())
|
||||
return NULL;
|
||||
|
||||
return &elemIt->second;
|
||||
}
|
||||
|
||||
void ThemeData::applyToImage(const std::string& viewName, const std::string& elementName, ImageComponent* image, unsigned int properties)
|
||||
{
|
||||
LOG(LogInfo) << " req image [" << viewName << "." << elementName << "] (flags: " << properties << ")";
|
||||
|
||||
ThemeElement* elem = getElement(viewName, elementName);
|
||||
if(!elem)
|
||||
{
|
||||
LOG(LogInfo) << " (missing)";
|
||||
return;
|
||||
}
|
||||
|
||||
Eigen::Vector2f scale = getScale(image);
|
||||
|
||||
if(properties & POSITION && elem->has("pos"))
|
||||
{
|
||||
Eigen::Vector2f denormalized = elem->get<Eigen::Vector2f>("pos").cwiseProduct(scale);
|
||||
image->setPosition(Eigen::Vector3f(denormalized.x(), denormalized.y(), 0));
|
||||
}
|
||||
|
||||
if(properties & ThemeFlags::SIZE && elem->has("size"))
|
||||
image->setResize(elem->get<Eigen::Vector2f>("size").cwiseProduct(scale), true);
|
||||
|
||||
if(properties & ORIGIN && elem->has("origin"))
|
||||
image->setOrigin(elem->get<Eigen::Vector2f>("origin"));
|
||||
|
||||
if(properties & PATH && elem->has("path"))
|
||||
image->setImage(elem->get<std::string>("path"));
|
||||
|
||||
if(properties & TILING && elem->has("tile"))
|
||||
image->setTiling(elem->get<bool>("tile"));
|
||||
}
|
||||
|
||||
void ThemeData::applyToNinePatch(const std::string& viewName, const std::string& elementName, NinePatchComponent* patch, unsigned int properties)
|
||||
{
|
||||
ThemeElement* elem = getElement(viewName, elementName);
|
||||
if(!elem)
|
||||
return;
|
||||
|
||||
Eigen::Vector2f scale = getScale(patch);
|
||||
|
||||
if(properties & POSITION && elem->has("pos"))
|
||||
{
|
||||
Eigen::Vector2f denormalized = elem->get<Eigen::Vector2f>("pos").cwiseProduct(scale);
|
||||
patch->setPosition(Eigen::Vector3f(denormalized.x(), denormalized.y(), 0));
|
||||
}
|
||||
|
||||
if(properties & PATH && elem->has("path"))
|
||||
patch->setImagePath(elem->get<std::string>("path"));
|
||||
|
||||
if(properties & ThemeFlags::SIZE && elem->has("size"))
|
||||
patch->setSize(elem->get<Eigen::Vector2f>("size").cwiseProduct(scale));
|
||||
}
|
||||
|
||||
void ThemeData::applyToText(const std::string& viewName, const std::string& elementName, TextComponent* text, unsigned int properties)
|
||||
{
|
||||
ThemeElement* elem = getElement(viewName, elementName);
|
||||
if(!elem)
|
||||
return;
|
||||
|
||||
Eigen::Vector2f scale = getScale(text);
|
||||
|
||||
if(properties & POSITION && elem->has("pos"))
|
||||
{
|
||||
Eigen::Vector2f denormalized = elem->get<Eigen::Vector2f>("pos").cwiseProduct(scale);
|
||||
text->setPosition(Eigen::Vector3f(denormalized.x(), denormalized.y(), 0));
|
||||
}
|
||||
|
||||
if(properties & ThemeFlags::SIZE && elem->has("size"))
|
||||
text->setSize(elem->get<Eigen::Vector2f>("size").cwiseProduct(scale));
|
||||
|
||||
if(properties & COLOR && elem->has("color"))
|
||||
text->setColor(elem->get<unsigned int>("color"));
|
||||
|
||||
if(properties & CENTER && elem->has("center"))
|
||||
text->setCentered(elem->get<bool>("center"));
|
||||
|
||||
if(properties & TEXT && elem->has("text"))
|
||||
text->setText(elem->get<std::string>("text"));
|
||||
|
||||
// TODO - fonts
|
||||
}
|
||||
|
||||
void ThemeData::playSound(const std::string& elementName)
|
||||
{
|
||||
ThemeElement* elem = getElement("common", elementName);
|
||||
if(!elem)
|
||||
return;
|
||||
|
||||
if(elem->has("path"))
|
||||
{
|
||||
const std::string path = elem->get<std::string>("path");
|
||||
auto cacheIt = mSoundCache.find(path);
|
||||
if(cacheIt != mSoundCache.end())
|
||||
{
|
||||
cacheIt->second->play();
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Sound> sound = std::shared_ptr<Sound>(new Sound(path));
|
||||
sound->play();
|
||||
mSoundCache[path] = sound;
|
||||
}
|
||||
}
|
|
@ -12,20 +12,23 @@ GuiFastSelect::GuiFastSelect(Window* window, IGameListView* gamelist) : GuiCompo
|
|||
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.6f);
|
||||
|
||||
const std::shared_ptr<ThemeData>& theme = mGameList->getTheme();
|
||||
using namespace ThemeFlags;
|
||||
|
||||
mBackground.setImagePath(theme->getImage("fastSelectBackgroundImage").path);
|
||||
theme->applyToNinePatch("fastSelect", "background", &mBackground, PATH);
|
||||
mBackground.fitTo(mSize);
|
||||
addChild(&mBackground);
|
||||
|
||||
mLetterText.setSize(mSize.x(), mSize.y() * 0.75f);
|
||||
mLetterText.setCentered(true);
|
||||
mLetterText.setFromTheme(theme, "fastSelectLetterFont", "fastSelectLetterColor");
|
||||
theme->applyToText("fastSelect", "letter", &mLetterText, FONT_PATH | COLOR);
|
||||
// TODO - set font size
|
||||
addChild(&mLetterText);
|
||||
|
||||
mSortText.setPosition(0, mSize.y() * 0.75f);
|
||||
mSortText.setSize(mSize.x(), mSize.y() * 0.25f);
|
||||
mSortText.setCentered(true);
|
||||
mSortText.setFromTheme(theme, "descriptionFont", "fastSelectTextColor");
|
||||
theme->applyToText("fastSelect", "subtext", &mSortText, FONT_PATH | COLOR);
|
||||
// TODO - set font size
|
||||
addChild(&mSortText);
|
||||
|
||||
mSortId = 0; // TODO
|
||||
|
|
|
@ -39,11 +39,12 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mBackground(window, ":/
|
|||
addChild(&mBackground);
|
||||
|
||||
mTheme = std::make_shared<ThemeData>();
|
||||
mTheme->setFont("listFont", FontDef(0.09f, mTheme->getFontDef("listFont").path));
|
||||
mTheme->setColor("listSelectorColor", 0xBBBBBBFF);
|
||||
mTheme->setColor("listPrimaryColor", 0x0000FFFF);
|
||||
mTheme->setColor("listSecondaryColor", 0xFF0000FF);
|
||||
mList.setTheme(mTheme);
|
||||
using namespace ThemeFlags;
|
||||
mTheme->applyToTextList< std::function<void()> >("common", "menu", &mList, FONT_PATH | COLOR);
|
||||
mList.setSelectorColor(0xBBBBBBFF);
|
||||
mList.setColor(0, 0x0000FFFF);
|
||||
mList.setColor(1, 0xFF0000FF);
|
||||
// TODO - set font size to 0.09f
|
||||
|
||||
addChild(&mList);
|
||||
}
|
||||
|
|
|
@ -23,8 +23,10 @@ public:
|
|||
void setImage(const char* image, size_t length); //Loads image from memory.
|
||||
void setImage(const std::shared_ptr<TextureResource>& texture); //Use an already existing texture.
|
||||
void setOrigin(float originX, float originY); //Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center)
|
||||
inline void setOrigin(Eigen::Vector2f origin) { setOrigin(origin.x(), origin.y()); }
|
||||
void setTiling(bool tile); //Enables or disables tiling. Must be called before loading an image or resizing will be weird.
|
||||
void setResize(float width, float height, bool allowUpscale);
|
||||
inline void setResize(Eigen::Vector2f size, bool allowUpscale) { setResize(size.x(), size.y(), allowUpscale); }
|
||||
void setColorShift(unsigned int color);
|
||||
|
||||
void setFlipX(bool flip);
|
||||
|
|
|
@ -138,9 +138,3 @@ std::string TextComponent::getValue() const
|
|||
{
|
||||
return mText;
|
||||
}
|
||||
|
||||
void TextComponent::setFromTheme(const std::shared_ptr<ThemeData>& theme, const std::string& fontIdentifier, const std::string& colorIdentifier)
|
||||
{
|
||||
setFont(theme->getFont(fontIdentifier));
|
||||
setColor(theme->getColor(colorIdentifier));
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ public:
|
|||
|
||||
std::shared_ptr<Font> getFont() const;
|
||||
|
||||
void setFromTheme(const std::shared_ptr<ThemeData>& theme, const std::string& fontIdentifier, const std::string& colorIdentifier);
|
||||
|
||||
private:
|
||||
void calculateExtent();
|
||||
|
||||
|
|
|
@ -13,13 +13,6 @@
|
|||
#include "../ThemeData.h"
|
||||
#include <functional>
|
||||
|
||||
#define THEME_FONT "listFont"
|
||||
#define THEME_SELECTOR_COLOR "listSelectorColor"
|
||||
#define THEME_HIGHLIGHTED_COLOR "listSelectedColor"
|
||||
#define THEME_SCROLL_SOUND "listScrollSound"
|
||||
static const int THEME_COLOR_ID_COUNT = 2;
|
||||
static const char* const THEME_ENTRY_COLOR[THEME_COLOR_ID_COUNT] = { "listPrimaryColor", "listSecondaryColor" };
|
||||
|
||||
//A graphical list. Supports multiple colors for rows and scrolling.
|
||||
template <typename T>
|
||||
class TextListComponent : public GuiComponent
|
||||
|
@ -54,16 +47,28 @@ public:
|
|||
void stopScrolling();
|
||||
inline bool isScrolling() const { return mScrollDir != 0; }
|
||||
|
||||
void setTheme(const std::shared_ptr<ThemeData>& theme);
|
||||
inline void setCentered(bool centered) { mCentered = centered; }
|
||||
|
||||
enum CursorState {
|
||||
enum CursorState
|
||||
{
|
||||
CURSOR_STOPPED,
|
||||
CURSOR_SCROLLING
|
||||
};
|
||||
|
||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
|
||||
|
||||
inline void setFont(const std::shared_ptr<Font>& font)
|
||||
{
|
||||
mFont = font;
|
||||
for(auto it = mRowVector.begin(); it != mRowVector.end(); it++)
|
||||
it->textCache.reset();
|
||||
}
|
||||
|
||||
inline void setSelectorColor(unsigned int color) { mSelectorColor = color; }
|
||||
inline void setSelectedColor(unsigned int color) { mSelectedColor = color; }
|
||||
inline void setScrollSound(const std::shared_ptr<Sound>& sound) { mScrollSound = sound; }
|
||||
inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
|
||||
|
||||
private:
|
||||
static const int MARQUEE_DELAY = 900;
|
||||
static const int MARQUEE_SPEED = 16;
|
||||
|
@ -81,13 +86,19 @@ private:
|
|||
int mMarqueeOffset;
|
||||
int mMarqueeTime;
|
||||
|
||||
std::shared_ptr<ThemeData> mTheme;
|
||||
bool mCentered;
|
||||
|
||||
std::vector<ListRow> mRowVector;
|
||||
int mCursor;
|
||||
|
||||
std::function<void(CursorState state)> mCursorChangedCallback;
|
||||
|
||||
std::shared_ptr<Font> mFont;
|
||||
unsigned int mSelectorColor;
|
||||
unsigned int mSelectedColor;
|
||||
std::shared_ptr<Sound> mScrollSound;
|
||||
static const unsigned int COLOR_ID_COUNT = 2;
|
||||
unsigned int mColors[COLOR_ID_COUNT];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -103,7 +114,11 @@ TextListComponent<T>::TextListComponent(Window* window) :
|
|||
|
||||
mCentered = true;
|
||||
|
||||
mTheme = ThemeData::getDefault();
|
||||
mFont = Font::get(FONT_SIZE_MEDIUM);
|
||||
mSelectorColor = 0x000000FF;
|
||||
mSelectedColor = 0;
|
||||
mColors[0] = 0x0000FFFF;
|
||||
mColors[1] = 0x00FF00FF;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -116,7 +131,7 @@ void TextListComponent<T>::render(const Eigen::Affine3f& parentTrans)
|
|||
{
|
||||
Eigen::Affine3f trans = parentTrans * getTransform();
|
||||
|
||||
std::shared_ptr<Font> font = mTheme->getFont(THEME_FONT);
|
||||
std::shared_ptr<Font>& font = mFont;
|
||||
|
||||
const int cutoff = 0;
|
||||
const int entrySize = font->getHeight() + 5;
|
||||
|
@ -157,7 +172,7 @@ void TextListComponent<T>::render(const Eigen::Affine3f& parentTrans)
|
|||
if(mCursor == i)
|
||||
{
|
||||
Renderer::setMatrix(trans);
|
||||
Renderer::drawRect(0, (int)y, (int)getSize().x(), font->getHeight(), mTheme->getColor(THEME_SELECTOR_COLOR));
|
||||
Renderer::drawRect(0, (int)y, (int)getSize().x(), font->getHeight(), mSelectorColor);
|
||||
}
|
||||
|
||||
ListRow& row = mRowVector.at((unsigned int)i);
|
||||
|
@ -165,10 +180,10 @@ void TextListComponent<T>::render(const Eigen::Affine3f& parentTrans)
|
|||
float x = (float)(mCursor == i ? -mMarqueeOffset : 0);
|
||||
|
||||
unsigned int color;
|
||||
if(mCursor == i && mTheme->getColor(THEME_HIGHLIGHTED_COLOR))
|
||||
color = mTheme->getColor(THEME_HIGHLIGHTED_COLOR);
|
||||
if(mCursor == i && mSelectedColor)
|
||||
color = mSelectedColor;
|
||||
else
|
||||
color = mTheme->getColor(THEME_ENTRY_COLOR[row.colorId]);
|
||||
color = mColors[row.colorId];
|
||||
|
||||
if(!row.textCache)
|
||||
row.textCache = std::unique_ptr<TextCache>(font->buildTextCache(row.name, 0, 0, 0x000000FF));
|
||||
|
@ -273,7 +288,7 @@ void TextListComponent<T>::update(int deltaTime)
|
|||
//if we're not scrolling and this object's text goes outside our size, marquee it!
|
||||
std::string text = getSelectedName();
|
||||
|
||||
Eigen::Vector2f textSize = mTheme->getFont(THEME_FONT)->sizeText(text);
|
||||
Eigen::Vector2f textSize = mFont->sizeText(text);
|
||||
|
||||
//it's long enough to marquee
|
||||
if(textSize.x() - mMarqueeOffset > getSize().x() - 12)
|
||||
|
@ -311,18 +326,16 @@ void TextListComponent<T>::scroll()
|
|||
}
|
||||
|
||||
onCursorChanged(CURSOR_SCROLLING);
|
||||
mTheme->playSound("scrollSound");
|
||||
|
||||
if(mScrollSound)
|
||||
mScrollSound->play();
|
||||
}
|
||||
|
||||
//list management stuff
|
||||
template <typename T>
|
||||
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
|
||||
{
|
||||
if(color >= THEME_COLOR_ID_COUNT)
|
||||
{
|
||||
LOG(LogError) << "Invalid row color Id (" << color << ")";
|
||||
color = 0;
|
||||
}
|
||||
assert(color < COLOR_ID_COUNT);
|
||||
|
||||
ListRow row = {name, obj, color};
|
||||
mRowVector.push_back(row);
|
||||
|
@ -392,14 +405,4 @@ void TextListComponent<T>::onCursorChanged(CursorState state)
|
|||
mCursorChangedCallback(state);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TextListComponent<T>::setTheme(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
mTheme = theme;
|
||||
|
||||
// invalidate text caches in case font changed
|
||||
for(auto it = mRowVector.begin(); it != mRowVector.end(); it++)
|
||||
it->textCache.reset();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,19 +35,23 @@ SystemView::SystemView(Window* window, SystemData* system) : GuiComponent(window
|
|||
|
||||
void SystemView::updateData()
|
||||
{
|
||||
using namespace ThemeFlags;
|
||||
|
||||
mHeaderImage.setImage("");
|
||||
mSystem->getTheme()->applyToImage("common", "header", &mHeaderImage, PATH);
|
||||
|
||||
// header
|
||||
if(mSystem->getTheme()->getImage("headerImage").path.empty())
|
||||
if(mHeaderImage.hasImage())
|
||||
{
|
||||
// use image
|
||||
mHeaderText.setText("");
|
||||
}else{
|
||||
// use text
|
||||
mHeaderImage.setImage("");
|
||||
mHeaderText.setText(mSystem->getFullName());
|
||||
}else{
|
||||
// use image
|
||||
mHeaderText.setText("");
|
||||
mHeaderImage.setImage(mSystem->getTheme()->getImage("headerImage").getTexture());
|
||||
}
|
||||
|
||||
mImage.setImage(mSystem->getTheme()->getImage("systemImage").getTexture());
|
||||
mSystem->getTheme()->applyToImage("common", "system", &mImage, PATH);
|
||||
}
|
||||
|
||||
bool SystemView::input(InputConfig* config, Input input)
|
||||
|
|
|
@ -18,7 +18,8 @@ BasicGameListView::BasicGameListView(Window* window, FileData* root)
|
|||
void BasicGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
ISimpleGameListView::onThemeChanged(theme);
|
||||
mList.setTheme(theme);
|
||||
using namespace ThemeFlags;
|
||||
theme->applyToTextList(getName(), "gamelist", &mList, POSITION | ThemeFlags::SIZE | COLOR | SOUND);
|
||||
}
|
||||
|
||||
void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
|
||||
|
|
|
@ -16,6 +16,8 @@ public:
|
|||
virtual FileData* getCursor() override;
|
||||
virtual void setCursor(FileData* file) override;
|
||||
|
||||
virtual const char* getName() const override { return "basic"; }
|
||||
|
||||
protected:
|
||||
virtual void populateList(const std::vector<FileData*>& files) override;
|
||||
virtual void launch(FileData* game) override;
|
||||
|
|
|
@ -5,19 +5,10 @@
|
|||
DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
||||
BasicGameListView(window, root),
|
||||
mDescContainer(window), mDescription(window),
|
||||
mImage(window), mInfoBackground(window), mDivider(window)
|
||||
mImage(window)
|
||||
{
|
||||
mHeaderImage.setPosition(mSize.x() * 0.25f, 0);
|
||||
|
||||
mInfoBackground.setPosition(0, mSize.y() * 0.5f);
|
||||
mInfoBackground.setOrigin(0, 0.5f);
|
||||
mInfoBackground.setResize(mSize.x() * 0.5f, mSize.y(), true);
|
||||
addChild(&mInfoBackground);
|
||||
|
||||
mDivider.setPosition(mSize.x() * 0.5f, mSize.y() * 0.5f);
|
||||
mDivider.setOrigin(0.5f, 0.5f);
|
||||
addChild(&mDivider);
|
||||
|
||||
const float padding = 0.01f;
|
||||
|
||||
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
|
||||
|
@ -49,13 +40,8 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
|
|||
if(mHeaderImage.getPosition().y() + mHeaderImage.getSize().y() > mImage.getPosition().y())
|
||||
mHeaderImage.setResize(0, mSize.y() * 0.185f, true);
|
||||
|
||||
mDescription.setFont(theme->getFont("descriptionFont"));
|
||||
mDescription.setColor(theme->getColor("descriptionColor"));
|
||||
mInfoBackground.setImage(theme->getImage("infoBackgroundImage").getTexture());
|
||||
mInfoBackground.setTiling(theme->getImage("infoBackgroundImage").tile);
|
||||
|
||||
mDivider.setImage(theme->getImage("verticalDividerImage").getTexture());
|
||||
mDivider.setResize((float)mDivider.getTextureSize().x(), mSize.y(), true);
|
||||
using namespace ThemeFlags;
|
||||
theme->applyToText("detailed", "description", &mDescription, POSITION | FONT_PATH | FONT_SIZE);
|
||||
}
|
||||
|
||||
void DetailedGameListView::updateInfoPanel()
|
||||
|
|
|
@ -10,6 +10,8 @@ public:
|
|||
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||
|
||||
virtual const char* getName() const override { return "detailed"; }
|
||||
|
||||
protected:
|
||||
virtual void launch(FileData* game) override;
|
||||
|
||||
|
@ -17,9 +19,7 @@ private:
|
|||
void updateInfoPanel();
|
||||
|
||||
ImageComponent mImage;
|
||||
ImageComponent mInfoBackground;
|
||||
ImageComponent mDivider;
|
||||
|
||||
|
||||
ScrollableContainer mDescContainer;
|
||||
TextComponent mDescription;
|
||||
};
|
||||
|
|
|
@ -17,6 +17,8 @@ public:
|
|||
|
||||
virtual bool input(InputConfig* config, Input input) override;
|
||||
|
||||
virtual const char* getName() const override { return "grid"; }
|
||||
|
||||
protected:
|
||||
virtual void populateList(const std::vector<FileData*>& files) override;
|
||||
virtual void launch(FileData* game) override;
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
|
||||
virtual bool input(InputConfig* config, Input input) override;
|
||||
|
||||
virtual const char* getName() const = 0;
|
||||
protected:
|
||||
FileData* mRoot;
|
||||
std::shared_ptr<ThemeData> mTheme;
|
||||
|
|
|
@ -23,14 +23,10 @@ ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGame
|
|||
|
||||
void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
const ImageDef& bg = theme->getImage("backgroundImage");
|
||||
mBackground.setTiling(bg.tile);
|
||||
mBackground.setImage(bg.getTexture());
|
||||
|
||||
const ImageDef& hdr = theme->getImage("headerImage");
|
||||
mHeaderImage.setTiling(hdr.tile);
|
||||
mHeaderImage.setImage(hdr.getTexture());
|
||||
|
||||
using namespace ThemeFlags;
|
||||
theme->applyToImage("common", "background", &mBackground, PATH | TILING);
|
||||
theme->applyToImage("common", "header", &mHeaderImage, PATH);
|
||||
|
||||
if(mHeaderImage.hasImage())
|
||||
{
|
||||
removeChild(&mHeaderText);
|
||||
|
|
Loading…
Reference in a new issue