mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 07:35: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/Sound.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.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/VolumeControl.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.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));
|
mRootFolder->sort(FileSorts::SortTypes.at(0));
|
||||||
|
|
||||||
mTheme = std::make_shared<ThemeData>();
|
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()
|
SystemData::~SystemData()
|
||||||
|
|
|
@ -4,81 +4,125 @@
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
#include "resources/TextureResource.h"
|
#include "resources/TextureResource.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/assign.hpp>
|
|
||||||
#include "pugiXML/pugixml.hpp"
|
#include "pugiXML/pugixml.hpp"
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
|
||||||
// Defaults
|
std::map< std::string, std::map<std::string, ThemeData::ElementPropertyType> > ThemeData::sElementMap = boost::assign::map_list_of
|
||||||
std::map<std::string, FontDef > ThemeData::sDefaultFonts = boost::assign::map_list_of
|
("image", boost::assign::map_list_of
|
||||||
("listFont", FontDef(0.045f, ""))
|
("pos", NORMALIZED_PAIR)
|
||||||
("descriptionFont", FontDef(0.035f, ""))
|
("size", NORMALIZED_PAIR)
|
||||||
("fastSelectLetterFont", FontDef(0.15f, ""));
|
("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
|
namespace fs = boost::filesystem;
|
||||||
("listPrimaryColor", 0x0000FFFF)
|
|
||||||
("listSecondaryColor", 0x00FF00FF)
|
|
||||||
("listSelectorColor", 0x000000FF)
|
|
||||||
("listSelectedColor", 0x00000000)
|
|
||||||
("descriptionColor", 0x48474DFF)
|
|
||||||
("fastSelectLetterColor", 0xFFFFFFFF)
|
|
||||||
("fastSelectTextColor", 0xDDDDDDFF);
|
|
||||||
|
|
||||||
std::map<std::string, ImageDef> ThemeData::sDefaultImages = boost::assign::map_list_of
|
#define MINIMUM_THEME_VERSION 3
|
||||||
("backgroundImage", ImageDef("", true))
|
#define CURRENT_THEME_VERSION 3
|
||||||
("headerImage", ImageDef("", false))
|
|
||||||
("infoBackgroundImage", ImageDef("", false))
|
|
||||||
("verticalDividerImage", ImageDef("", false))
|
|
||||||
("fastSelectBackgroundImage", ImageDef(":/button.png", false))
|
|
||||||
("systemImage", ImageDef("", false));
|
|
||||||
|
|
||||||
std::map<std::string, SoundDef> ThemeData::sDefaultSounds = boost::assign::map_list_of
|
// still TODO:
|
||||||
("scrollSound", SoundDef(""))
|
// * how to do <include>?
|
||||||
("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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThemeData::ThemeData()
|
ThemeData::ThemeData()
|
||||||
{
|
{
|
||||||
setDefaults();
|
mVersion = 0;
|
||||||
|
|
||||||
std::string defaultDir = getHomePath() + "/.emulationstation/es_theme_default.xml";
|
|
||||||
if(boost::filesystem::exists(defaultDir))
|
|
||||||
loadFile(defaultDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThemeData::setDefaults()
|
void ThemeData::loadFile(const std::string& path)
|
||||||
{
|
{
|
||||||
mFontMap.clear();
|
ThemeException error;
|
||||||
mImageMap.clear();
|
error.setFile(path);
|
||||||
mColorMap.clear();
|
|
||||||
mSoundMap.clear();
|
|
||||||
|
|
||||||
mFontMap = sDefaultFonts;
|
mPath = path;
|
||||||
mImageMap = sDefaultImages;
|
|
||||||
mColorMap = sDefaultColors;
|
if(!fs::exists(path))
|
||||||
mSoundMap = sDefaultSounds;
|
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)
|
if(!str)
|
||||||
return defaultColor;
|
throw error << "Empty color";
|
||||||
|
|
||||||
size_t len = strlen(str);
|
size_t len = strlen(str);
|
||||||
if(len != 6 && len != 8)
|
if(len != 6 && len != 8)
|
||||||
{
|
throw error << "Invalid color (bad length, \"" << str << "\" - must be 6 or 8)";
|
||||||
LOG(LogError) << "Invalid theme color \"" << str << "\" (must be 6 or 8 characters)";
|
|
||||||
return defaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -91,13 +135,12 @@ unsigned int getHexColor(const char* str, unsigned int defaultColor)
|
||||||
return val;
|
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')
|
if(!in || in[0] == '\0')
|
||||||
return in;
|
return in;
|
||||||
|
|
||||||
boost::filesystem::path relPath(relative);
|
fs::path relPath = relative.parent_path();
|
||||||
relPath = relPath.parent_path();
|
|
||||||
|
|
||||||
boost::filesystem::path path(in);
|
boost::filesystem::path path(in);
|
||||||
|
|
||||||
|
@ -114,102 +157,63 @@ std::string resolvePath(const char* in, const std::string& relative)
|
||||||
return path.generic_string();
|
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))
|
ThemeException error;
|
||||||
return;
|
error.setFile(mPath.string());
|
||||||
|
|
||||||
pugi::xml_document doc;
|
ThemeElement element;
|
||||||
pugi::xml_parse_result result = doc.load_file(themePath.c_str());
|
element.extra = root.attribute("extra").as_bool(false);
|
||||||
if(!result)
|
|
||||||
|
for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling())
|
||||||
{
|
{
|
||||||
LOG(LogWarning) << "Could not parse theme file \"" << themePath << "\":\n " << result.description();
|
auto typeIt = typeMap.find(node.name());
|
||||||
return;
|
if(typeIt == typeMap.end())
|
||||||
}
|
throw error << "Unknown property type \"" << node.name() << "\" (for element of type " << root.name() << ").";
|
||||||
|
|
||||||
pugi::xml_node root = doc.child("theme");
|
switch(typeIt->second)
|
||||||
|
|
||||||
// Fonts
|
|
||||||
for(auto it = mFontMap.begin(); it != mFontMap.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);
|
case NORMALIZED_PAIR:
|
||||||
if(!boost::filesystem::exists(path))
|
{
|
||||||
{
|
std::string str = std::string(node.text().as_string());
|
||||||
LOG(LogWarning) << "Font \"" << path << "\" doesn't exist!";
|
|
||||||
path = it->second.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
float size = node.child("size").text().as_float(it->second.size);
|
size_t divider = str.find(' ');
|
||||||
mFontMap[it->first] = FontDef(size, path);
|
if(divider == std::string::npos)
|
||||||
root.remove_child(node);
|
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
|
return element;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
174
src/ThemeData.h
174
src/ThemeData.h
|
@ -1,82 +1,146 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "resources/Font.h"
|
#include <boost/filesystem.hpp>
|
||||||
#include "resources/TextureResource.h"
|
#include <boost/variant.hpp>
|
||||||
#include "Renderer.h"
|
#include <Eigen/Dense>
|
||||||
#include "AudioManager.h"
|
#include "pugiXML/pugixml.hpp"
|
||||||
#include "Sound.h"
|
#include "GuiComponent.h"
|
||||||
|
|
||||||
struct FontDef
|
template<typename T>
|
||||||
|
class TextListComponent;
|
||||||
|
|
||||||
|
class Sound;
|
||||||
|
class ImageComponent;
|
||||||
|
class NinePatchComponent;
|
||||||
|
class TextComponent;
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
namespace ThemeFlags
|
||||||
{
|
{
|
||||||
FontDef() {}
|
enum PropertyFlags : unsigned int
|
||||||
FontDef(float sz, const std::string& p) : path(p), size(sz) {}
|
{
|
||||||
|
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;
|
class ThemeException : public std::exception
|
||||||
float size;
|
{
|
||||||
|
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:
|
template<typename T>
|
||||||
mutable std::shared_ptr<Font> font;
|
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() {}
|
std::stringstream ss;
|
||||||
ImageDef(const std::string& p, bool t) : path(p), tile(t) {}
|
ss << e.msg << appendMsg;
|
||||||
|
e.msg = ss.str();
|
||||||
std::string path;
|
return e;
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ThemeData
|
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:
|
public:
|
||||||
static const std::shared_ptr<ThemeData>& getDefault();
|
|
||||||
|
|
||||||
ThemeData();
|
ThemeData();
|
||||||
|
|
||||||
void setDefaults();
|
// throws ThemeException
|
||||||
void loadFile(const std::string& path);
|
void loadFile(const std::string& path);
|
||||||
|
|
||||||
inline const FontDef& getFontDef(const std::string& identifier) const { return mFontMap.at(identifier); }
|
enum ElementPropertyType
|
||||||
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); }
|
NORMALIZED_PAIR,
|
||||||
inline unsigned int getColor(const std::string& identifier) const { return mColorMap.at(identifier); }
|
PATH,
|
||||||
void playSound(const std::string& identifier) const;
|
STRING,
|
||||||
|
COLOR,
|
||||||
|
FLOAT,
|
||||||
|
BOOLEAN
|
||||||
|
};
|
||||||
|
|
||||||
inline void setFont(const std::string& identifier, FontDef def) { mFontMap[identifier] = def; }
|
void renderExtras(const std::string& view, Window* window, const Eigen::Affine3f& transform);
|
||||||
inline void setColor(const std::string& identifier, unsigned int color) { mColorMap[identifier] = color; }
|
|
||||||
|
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:
|
private:
|
||||||
static std::map<std::string, ImageDef> sDefaultImages;
|
static std::map< std::string, std::map<std::string, ElementPropertyType> > sElementMap;
|
||||||
static std::map<std::string, unsigned int> sDefaultColors;
|
|
||||||
static std::map<std::string, FontDef > sDefaultFonts;
|
|
||||||
static std::map<std::string, SoundDef> sDefaultSounds;
|
|
||||||
|
|
||||||
std::map<std::string, ImageDef> mImageMap;
|
boost::filesystem::path mPath;
|
||||||
std::map<std::string, unsigned int> mColorMap;
|
float mVersion;
|
||||||
std::map<std::string, FontDef > mFontMap;
|
|
||||||
std::map< std::string, SoundDef > mSoundMap;
|
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);
|
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.6f);
|
||||||
|
|
||||||
const std::shared_ptr<ThemeData>& theme = mGameList->getTheme();
|
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);
|
mBackground.fitTo(mSize);
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
|
|
||||||
mLetterText.setSize(mSize.x(), mSize.y() * 0.75f);
|
mLetterText.setSize(mSize.x(), mSize.y() * 0.75f);
|
||||||
mLetterText.setCentered(true);
|
mLetterText.setCentered(true);
|
||||||
mLetterText.setFromTheme(theme, "fastSelectLetterFont", "fastSelectLetterColor");
|
theme->applyToText("fastSelect", "letter", &mLetterText, FONT_PATH | COLOR);
|
||||||
|
// TODO - set font size
|
||||||
addChild(&mLetterText);
|
addChild(&mLetterText);
|
||||||
|
|
||||||
mSortText.setPosition(0, mSize.y() * 0.75f);
|
mSortText.setPosition(0, mSize.y() * 0.75f);
|
||||||
mSortText.setSize(mSize.x(), mSize.y() * 0.25f);
|
mSortText.setSize(mSize.x(), mSize.y() * 0.25f);
|
||||||
mSortText.setCentered(true);
|
mSortText.setCentered(true);
|
||||||
mSortText.setFromTheme(theme, "descriptionFont", "fastSelectTextColor");
|
theme->applyToText("fastSelect", "subtext", &mSortText, FONT_PATH | COLOR);
|
||||||
|
// TODO - set font size
|
||||||
addChild(&mSortText);
|
addChild(&mSortText);
|
||||||
|
|
||||||
mSortId = 0; // TODO
|
mSortId = 0; // TODO
|
||||||
|
|
|
@ -39,11 +39,12 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mBackground(window, ":/
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
|
|
||||||
mTheme = std::make_shared<ThemeData>();
|
mTheme = std::make_shared<ThemeData>();
|
||||||
mTheme->setFont("listFont", FontDef(0.09f, mTheme->getFontDef("listFont").path));
|
using namespace ThemeFlags;
|
||||||
mTheme->setColor("listSelectorColor", 0xBBBBBBFF);
|
mTheme->applyToTextList< std::function<void()> >("common", "menu", &mList, FONT_PATH | COLOR);
|
||||||
mTheme->setColor("listPrimaryColor", 0x0000FFFF);
|
mList.setSelectorColor(0xBBBBBBFF);
|
||||||
mTheme->setColor("listSecondaryColor", 0xFF0000FF);
|
mList.setColor(0, 0x0000FFFF);
|
||||||
mList.setTheme(mTheme);
|
mList.setColor(1, 0xFF0000FF);
|
||||||
|
// TODO - set font size to 0.09f
|
||||||
|
|
||||||
addChild(&mList);
|
addChild(&mList);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,10 @@ public:
|
||||||
void setImage(const char* image, size_t length); //Loads image from memory.
|
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 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)
|
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 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);
|
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 setColorShift(unsigned int color);
|
||||||
|
|
||||||
void setFlipX(bool flip);
|
void setFlipX(bool flip);
|
||||||
|
|
|
@ -138,9 +138,3 @@ std::string TextComponent::getValue() const
|
||||||
{
|
{
|
||||||
return mText;
|
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;
|
std::shared_ptr<Font> getFont() const;
|
||||||
|
|
||||||
void setFromTheme(const std::shared_ptr<ThemeData>& theme, const std::string& fontIdentifier, const std::string& colorIdentifier);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void calculateExtent();
|
void calculateExtent();
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,6 @@
|
||||||
#include "../ThemeData.h"
|
#include "../ThemeData.h"
|
||||||
#include <functional>
|
#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.
|
//A graphical list. Supports multiple colors for rows and scrolling.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class TextListComponent : public GuiComponent
|
class TextListComponent : public GuiComponent
|
||||||
|
@ -54,16 +47,28 @@ public:
|
||||||
void stopScrolling();
|
void stopScrolling();
|
||||||
inline bool isScrolling() const { return mScrollDir != 0; }
|
inline bool isScrolling() const { return mScrollDir != 0; }
|
||||||
|
|
||||||
void setTheme(const std::shared_ptr<ThemeData>& theme);
|
|
||||||
inline void setCentered(bool centered) { mCentered = centered; }
|
inline void setCentered(bool centered) { mCentered = centered; }
|
||||||
|
|
||||||
enum CursorState {
|
enum CursorState
|
||||||
|
{
|
||||||
CURSOR_STOPPED,
|
CURSOR_STOPPED,
|
||||||
CURSOR_SCROLLING
|
CURSOR_SCROLLING
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
|
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:
|
private:
|
||||||
static const int MARQUEE_DELAY = 900;
|
static const int MARQUEE_DELAY = 900;
|
||||||
static const int MARQUEE_SPEED = 16;
|
static const int MARQUEE_SPEED = 16;
|
||||||
|
@ -81,13 +86,19 @@ private:
|
||||||
int mMarqueeOffset;
|
int mMarqueeOffset;
|
||||||
int mMarqueeTime;
|
int mMarqueeTime;
|
||||||
|
|
||||||
std::shared_ptr<ThemeData> mTheme;
|
|
||||||
bool mCentered;
|
bool mCentered;
|
||||||
|
|
||||||
std::vector<ListRow> mRowVector;
|
std::vector<ListRow> mRowVector;
|
||||||
int mCursor;
|
int mCursor;
|
||||||
|
|
||||||
std::function<void(CursorState state)> mCursorChangedCallback;
|
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>
|
template <typename T>
|
||||||
|
@ -103,7 +114,11 @@ TextListComponent<T>::TextListComponent(Window* window) :
|
||||||
|
|
||||||
mCentered = true;
|
mCentered = true;
|
||||||
|
|
||||||
mTheme = ThemeData::getDefault();
|
mFont = Font::get(FONT_SIZE_MEDIUM);
|
||||||
|
mSelectorColor = 0x000000FF;
|
||||||
|
mSelectedColor = 0;
|
||||||
|
mColors[0] = 0x0000FFFF;
|
||||||
|
mColors[1] = 0x00FF00FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -116,7 +131,7 @@ void TextListComponent<T>::render(const Eigen::Affine3f& parentTrans)
|
||||||
{
|
{
|
||||||
Eigen::Affine3f trans = parentTrans * getTransform();
|
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 cutoff = 0;
|
||||||
const int entrySize = font->getHeight() + 5;
|
const int entrySize = font->getHeight() + 5;
|
||||||
|
@ -157,7 +172,7 @@ void TextListComponent<T>::render(const Eigen::Affine3f& parentTrans)
|
||||||
if(mCursor == i)
|
if(mCursor == i)
|
||||||
{
|
{
|
||||||
Renderer::setMatrix(trans);
|
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);
|
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);
|
float x = (float)(mCursor == i ? -mMarqueeOffset : 0);
|
||||||
|
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
if(mCursor == i && mTheme->getColor(THEME_HIGHLIGHTED_COLOR))
|
if(mCursor == i && mSelectedColor)
|
||||||
color = mTheme->getColor(THEME_HIGHLIGHTED_COLOR);
|
color = mSelectedColor;
|
||||||
else
|
else
|
||||||
color = mTheme->getColor(THEME_ENTRY_COLOR[row.colorId]);
|
color = mColors[row.colorId];
|
||||||
|
|
||||||
if(!row.textCache)
|
if(!row.textCache)
|
||||||
row.textCache = std::unique_ptr<TextCache>(font->buildTextCache(row.name, 0, 0, 0x000000FF));
|
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!
|
//if we're not scrolling and this object's text goes outside our size, marquee it!
|
||||||
std::string text = getSelectedName();
|
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
|
//it's long enough to marquee
|
||||||
if(textSize.x() - mMarqueeOffset > getSize().x() - 12)
|
if(textSize.x() - mMarqueeOffset > getSize().x() - 12)
|
||||||
|
@ -311,18 +326,16 @@ void TextListComponent<T>::scroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
onCursorChanged(CURSOR_SCROLLING);
|
onCursorChanged(CURSOR_SCROLLING);
|
||||||
mTheme->playSound("scrollSound");
|
|
||||||
|
if(mScrollSound)
|
||||||
|
mScrollSound->play();
|
||||||
}
|
}
|
||||||
|
|
||||||
//list management stuff
|
//list management stuff
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
|
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
|
||||||
{
|
{
|
||||||
if(color >= THEME_COLOR_ID_COUNT)
|
assert(color < COLOR_ID_COUNT);
|
||||||
{
|
|
||||||
LOG(LogError) << "Invalid row color Id (" << color << ")";
|
|
||||||
color = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListRow row = {name, obj, color};
|
ListRow row = {name, obj, color};
|
||||||
mRowVector.push_back(row);
|
mRowVector.push_back(row);
|
||||||
|
@ -392,14 +405,4 @@ void TextListComponent<T>::onCursorChanged(CursorState state)
|
||||||
mCursorChangedCallback(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
|
#endif
|
||||||
|
|
|
@ -35,19 +35,23 @@ SystemView::SystemView(Window* window, SystemData* system) : GuiComponent(window
|
||||||
|
|
||||||
void SystemView::updateData()
|
void SystemView::updateData()
|
||||||
{
|
{
|
||||||
|
using namespace ThemeFlags;
|
||||||
|
|
||||||
|
mHeaderImage.setImage("");
|
||||||
|
mSystem->getTheme()->applyToImage("common", "header", &mHeaderImage, PATH);
|
||||||
|
|
||||||
// header
|
// header
|
||||||
if(mSystem->getTheme()->getImage("headerImage").path.empty())
|
if(mHeaderImage.hasImage())
|
||||||
{
|
{
|
||||||
|
// use image
|
||||||
|
mHeaderText.setText("");
|
||||||
|
}else{
|
||||||
// use text
|
// use text
|
||||||
mHeaderImage.setImage("");
|
mHeaderImage.setImage("");
|
||||||
mHeaderText.setText(mSystem->getFullName());
|
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)
|
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)
|
void BasicGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
{
|
{
|
||||||
ISimpleGameListView::onThemeChanged(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)
|
void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
|
||||||
|
|
|
@ -16,6 +16,8 @@ public:
|
||||||
virtual FileData* getCursor() override;
|
virtual FileData* getCursor() override;
|
||||||
virtual void setCursor(FileData* file) override;
|
virtual void setCursor(FileData* file) override;
|
||||||
|
|
||||||
|
virtual const char* getName() const override { return "basic"; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void populateList(const std::vector<FileData*>& files) override;
|
virtual void populateList(const std::vector<FileData*>& files) override;
|
||||||
virtual void launch(FileData* game) override;
|
virtual void launch(FileData* game) override;
|
||||||
|
|
|
@ -5,19 +5,10 @@
|
||||||
DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
||||||
BasicGameListView(window, root),
|
BasicGameListView(window, root),
|
||||||
mDescContainer(window), mDescription(window),
|
mDescContainer(window), mDescription(window),
|
||||||
mImage(window), mInfoBackground(window), mDivider(window)
|
mImage(window)
|
||||||
{
|
{
|
||||||
mHeaderImage.setPosition(mSize.x() * 0.25f, 0);
|
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;
|
const float padding = 0.01f;
|
||||||
|
|
||||||
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
|
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())
|
if(mHeaderImage.getPosition().y() + mHeaderImage.getSize().y() > mImage.getPosition().y())
|
||||||
mHeaderImage.setResize(0, mSize.y() * 0.185f, true);
|
mHeaderImage.setResize(0, mSize.y() * 0.185f, true);
|
||||||
|
|
||||||
mDescription.setFont(theme->getFont("descriptionFont"));
|
using namespace ThemeFlags;
|
||||||
mDescription.setColor(theme->getColor("descriptionColor"));
|
theme->applyToText("detailed", "description", &mDescription, POSITION | FONT_PATH | FONT_SIZE);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedGameListView::updateInfoPanel()
|
void DetailedGameListView::updateInfoPanel()
|
||||||
|
|
|
@ -10,6 +10,8 @@ public:
|
||||||
|
|
||||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||||
|
|
||||||
|
virtual const char* getName() const override { return "detailed"; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void launch(FileData* game) override;
|
virtual void launch(FileData* game) override;
|
||||||
|
|
||||||
|
@ -17,9 +19,7 @@ private:
|
||||||
void updateInfoPanel();
|
void updateInfoPanel();
|
||||||
|
|
||||||
ImageComponent mImage;
|
ImageComponent mImage;
|
||||||
ImageComponent mInfoBackground;
|
|
||||||
ImageComponent mDivider;
|
|
||||||
|
|
||||||
ScrollableContainer mDescContainer;
|
ScrollableContainer mDescContainer;
|
||||||
TextComponent mDescription;
|
TextComponent mDescription;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,8 @@ public:
|
||||||
|
|
||||||
virtual bool input(InputConfig* config, Input input) override;
|
virtual bool input(InputConfig* config, Input input) override;
|
||||||
|
|
||||||
|
virtual const char* getName() const override { return "grid"; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void populateList(const std::vector<FileData*>& files) override;
|
virtual void populateList(const std::vector<FileData*>& files) override;
|
||||||
virtual void launch(FileData* game) override;
|
virtual void launch(FileData* game) override;
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
|
|
||||||
virtual bool input(InputConfig* config, Input input) override;
|
virtual bool input(InputConfig* config, Input input) override;
|
||||||
|
|
||||||
|
virtual const char* getName() const = 0;
|
||||||
protected:
|
protected:
|
||||||
FileData* mRoot;
|
FileData* mRoot;
|
||||||
std::shared_ptr<ThemeData> mTheme;
|
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)
|
void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
{
|
{
|
||||||
const ImageDef& bg = theme->getImage("backgroundImage");
|
using namespace ThemeFlags;
|
||||||
mBackground.setTiling(bg.tile);
|
theme->applyToImage("common", "background", &mBackground, PATH | TILING);
|
||||||
mBackground.setImage(bg.getTexture());
|
theme->applyToImage("common", "header", &mHeaderImage, PATH);
|
||||||
|
|
||||||
const ImageDef& hdr = theme->getImage("headerImage");
|
|
||||||
mHeaderImage.setTiling(hdr.tile);
|
|
||||||
mHeaderImage.setImage(hdr.getTexture());
|
|
||||||
|
|
||||||
if(mHeaderImage.hasImage())
|
if(mHeaderImage.hasImage())
|
||||||
{
|
{
|
||||||
removeChild(&mHeaderText);
|
removeChild(&mHeaderText);
|
||||||
|
|
Loading…
Reference in a new issue