Commit a7359a2d authored by Aloshi's avatar Aloshi

Themes mostly stable, documentation updated

parent 8bfde969
...@@ -154,10 +154,11 @@ set(ES_HEADERS ...@@ -154,10 +154,11 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.h
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.h
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.h
${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.h ${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h
${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.h ${CMAKE_CURRENT_SOURCE_DIR}/src/XMLReader.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.h
...@@ -172,24 +173,28 @@ set(ES_HEADERS ...@@ -172,24 +173,28 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextListComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ThemeComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameScraper.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperLog.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperLog.h
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.h
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.h
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.h
${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/views/BasicGameListView.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/DetailedGameListView.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/GameListView.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/ViewController.h
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h
...@@ -215,10 +220,11 @@ set(ES_SOURCES ...@@ -215,10 +220,11 @@ set(ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.cpp
${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/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
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimationComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentListComponent.cpp
...@@ -231,27 +237,31 @@ set(ES_SOURCES ...@@ -231,27 +237,31 @@ set(ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ThemeComponent.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameList.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiGameScraper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiInputConfig.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperLog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperLog.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/views/BasicGameListView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/views/DetailedGameListView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/views/GameListView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/views/ViewController.cpp
${CMAKE_CURRENT_SOURCE_DIR}/data/ResourceUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/ResourceUtil.cpp
${CMAKE_CURRENT_SOURCE_DIR}/data/converted/ES_logo_16_png.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/converted/ES_logo_16_png.cpp
${CMAKE_CURRENT_SOURCE_DIR}/data/converted/ES_logo_32_png.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/converted/ES_logo_32_png.cpp
......
Also known as "Really Bad Technical Documentation"
These are mostly notes I create as I go along, marking potential gotchas for people who might try to extend my code.
Some day I'll try and add an overview of the code structure, what each class does, etc.
Development Environment
=======================
I personally launch ES in windowed mode with a smaller resolution than my monitor and with debug text enabled.
`emulationstation --windowed --debug -w 1280 -h 720`
Creating a new GuiComponent
===========================
You probably want to override:
`bool input(InputConfig* config, Input input);`
Check if some input is mapped to some action with `config->isMappedTo("a", input);`.
Check if an input is "pressed" with `input.value != 0` (input.value *can* be negative in the case of axes).
`void update(int deltaTime);`
`deltaTime` is in milliseconds.
`void render(const Eigen::Affine3f& parentTrans);`
You probably want to do `Eigen::Affine3f trans = parentTrans * getTransform();` to get your final "modelview" matrix.
Apply the modelview matrix with `Renderer::setMatrix(const Eigen::Affine3f&)`.
Render any children the component may have with `renderChildren(parentTrans);`.
Creating a new GameListView Class
=================================
1. Don't allow the user to navigate to the root node's parent. If you use a stack of some sort to keep track of past cursor states this will be a natural side effect.
This diff is collapsed.
...@@ -9,8 +9,8 @@ std::string getCleanFileName(const fs::path& path) ...@@ -9,8 +9,8 @@ std::string getCleanFileName(const fs::path& path)
} }
FileData::FileData(FileType type, const fs::path& path) FileData::FileData(FileType type, const fs::path& path, SystemData* system)
: mType(type), mPath(path), mParent(NULL), metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) // metadata is REALLY set in the constructor! : mType(type), mPath(path), mSystem(system), mParent(NULL), metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) // metadata is REALLY set in the constructor!
{ {
// metadata needs at least a name field (since that's what getName() will return) // metadata needs at least a name field (since that's what getName() will return)
if(metadata.get("name").empty()) if(metadata.get("name").empty())
...@@ -21,6 +21,9 @@ FileData::~FileData() ...@@ -21,6 +21,9 @@ FileData::~FileData()
{ {
if(mParent) if(mParent)
mParent->removeChild(this); mParent->removeChild(this);
while(mChildren.size())
delete mChildren.back();
} }
const std::string& FileData::getThumbnailPath() const const std::string& FileData::getThumbnailPath() const
......
...@@ -5,12 +5,21 @@ ...@@ -5,12 +5,21 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "MetaData.h" #include "MetaData.h"
class SystemData;
enum FileType enum FileType
{ {
GAME = 1, // Cannot have children. GAME = 1, // Cannot have children.
FOLDER = 2 FOLDER = 2
}; };
enum FileChangeType
{
FILE_ADDED,
FILE_METADATA_CHANGED,
FILE_REMOVED
};
// Used for loading/saving gamelist.xml. // Used for loading/saving gamelist.xml.
const char* fileTypeToString(FileType type); const char* fileTypeToString(FileType type);
FileType stringToFileType(const char* str); FileType stringToFileType(const char* str);
...@@ -21,7 +30,7 @@ std::string getCleanFileName(const boost::filesystem::path& path); ...@@ -21,7 +30,7 @@ std::string getCleanFileName(const boost::filesystem::path& path);
class FileData class FileData
{ {
public: public:
FileData(FileType type, const boost::filesystem::path& path); FileData(FileType type, const boost::filesystem::path& path, SystemData* system);
virtual ~FileData(); virtual ~FileData();
inline const std::string& getName() const { return metadata.get("name"); } inline const std::string& getName() const { return metadata.get("name"); }
...@@ -29,6 +38,7 @@ public: ...@@ -29,6 +38,7 @@ public:
inline const boost::filesystem::path& getPath() const { return mPath; } inline const boost::filesystem::path& getPath() const { return mPath; }
inline FileData* getParent() const { return mParent; } inline FileData* getParent() const { return mParent; }
inline const std::vector<FileData*>& getChildren() const { return mChildren; } inline const std::vector<FileData*>& getChildren() const { return mChildren; }
inline SystemData* getSystem() const { return mSystem; }
virtual const std::string& getThumbnailPath() const; virtual const std::string& getThumbnailPath() const;
...@@ -57,6 +67,7 @@ public: ...@@ -57,6 +67,7 @@ public:
private: private:
FileType mType; FileType mType;
boost::filesystem::path mPath; boost::filesystem::path mPath;
SystemData* mSystem;
FileData* mParent; FileData* mParent;
std::vector<FileData*> mChildren; std::vector<FileData*> mChildren;
}; };
...@@ -99,6 +99,9 @@ void GuiComponent::addChild(GuiComponent* cmp) ...@@ -99,6 +99,9 @@ void GuiComponent::addChild(GuiComponent* cmp)
void GuiComponent::removeChild(GuiComponent* cmp) void GuiComponent::removeChild(GuiComponent* cmp)
{ {
if(!cmp->getParent())
return;
if(cmp->getParent() != this) if(cmp->getParent() != this)
{ {
LOG(LogError) << "Tried to remove child from incorrect parent!"; LOG(LogError) << "Tried to remove child from incorrect parent!";
......
...@@ -35,8 +35,8 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con ...@@ -35,8 +35,8 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con
mLaunchCommand = command; mLaunchCommand = command;
mPlatformId = platformId; mPlatformId = platformId;
mRootFolder = new FileData(FOLDER, mStartPath); mRootFolder = new FileData(FOLDER, mStartPath, this);
mRootFolder->metadata.set("name", "Search Root"); mRootFolder->metadata.set("name", mFullName);
if(!Settings::getInstance()->getBool("PARSEGAMELISTONLY")) if(!Settings::getInstance()->getBool("PARSEGAMELISTONLY"))
populateFolder(mRootFolder); populateFolder(mRootFolder);
...@@ -45,6 +45,9 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con ...@@ -45,6 +45,9 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con
parseGamelist(this); parseGamelist(this);
mRootFolder->sort(FileSorts::SortTypes.at(0)); mRootFolder->sort(FileSorts::SortTypes.at(0));
mTheme = std::make_shared<ThemeData>();
mTheme->loadFile(getThemePath());
} }
SystemData::~SystemData() SystemData::~SystemData()
...@@ -178,7 +181,7 @@ void SystemData::populateFolder(FileData* folder) ...@@ -178,7 +181,7 @@ void SystemData::populateFolder(FileData* folder)
isGame = false; isGame = false;
if(std::find(mSearchExtensions.begin(), mSearchExtensions.end(), extension) != mSearchExtensions.end()) if(std::find(mSearchExtensions.begin(), mSearchExtensions.end(), extension) != mSearchExtensions.end())
{ {
FileData* newGame = new FileData(GAME, filePath.generic_string()); FileData* newGame = new FileData(GAME, filePath.generic_string(), this);
folder->addChild(newGame); folder->addChild(newGame);
isGame = true; isGame = true;
} }
...@@ -186,7 +189,7 @@ void SystemData::populateFolder(FileData* folder) ...@@ -186,7 +189,7 @@ void SystemData::populateFolder(FileData* folder)
//add directories that also do not match an extension as folders //add directories that also do not match an extension as folders
if(!isGame && fs::is_directory(filePath)) if(!isGame && fs::is_directory(filePath))
{ {
FileData* newFolder = new FileData(FOLDER, filePath.generic_string()); FileData* newFolder = new FileData(FOLDER, filePath.generic_string(), this);
populateFolder(newFolder); populateFolder(newFolder);
//ignore folders that do not contain games //ignore folders that do not contain games
...@@ -347,7 +350,19 @@ std::string SystemData::getGamelistPath() const ...@@ -347,7 +350,19 @@ std::string SystemData::getGamelistPath() const
return filePath.generic_string(); return filePath.generic_string();
} }
bool SystemData::hasGamelist() std::string SystemData::getThemePath() const
{
fs::path filePath;
filePath = mRootFolder->getPath() / "theme.xml";
if(fs::exists(filePath))
return filePath.generic_string();
filePath = getHomePath() + "/.emulationstation/" + getName() + "/theme.xml";
return filePath.generic_string();
}
bool SystemData::hasGamelist() const
{ {
return (fs::exists(getGamelistPath())); return (fs::exists(getGamelistPath()));
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "Window.h" #include "Window.h"
#include "MetaData.h" #include "MetaData.h"
#include "PlatformId.h" #include "PlatformId.h"
#include "ThemeData.h"
class SystemData class SystemData
{ {
...@@ -21,9 +22,11 @@ public: ...@@ -21,9 +22,11 @@ public:
inline const std::string& getStartPath() const { return mStartPath; } inline const std::string& getStartPath() const { return mStartPath; }
inline const std::vector<std::string>& getExtensions() const { return mSearchExtensions; } inline const std::vector<std::string>& getExtensions() const { return mSearchExtensions; }
inline PlatformIds::PlatformId getPlatformId() const { return mPlatformId; } inline PlatformIds::PlatformId getPlatformId() const { return mPlatformId; }
inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
std::string getGamelistPath() const; std::string getGamelistPath() const;
bool hasGamelist(); bool hasGamelist() const;
std::string getThemePath() const;
unsigned int getGameCount() const; unsigned int getGameCount() const;
...@@ -43,6 +46,7 @@ private: ...@@ -43,6 +46,7 @@ private:
std::vector<std::string> mSearchExtensions; std::vector<std::string> mSearchExtensions;
std::string mLaunchCommand; std::string mLaunchCommand;
PlatformIds::PlatformId mPlatformId; PlatformIds::PlatformId mPlatformId;
std::shared_ptr<ThemeData> mTheme;
void populateFolder(FileData* folder); void populateFolder(FileData* folder);
......
#include "ThemeData.h"
#include "Renderer.h"
#include "resources/Font.h"
#include "Sound.h"
#include "resources/TextureResource.h"
#include "Log.h"
#include <boost/filesystem.hpp>
#include <boost/assign.hpp>
#include "pugiXML/pugixml.hpp"
// Defaults
std::map<std::string, FontDef > ThemeData::sDefaultFonts = boost::assign::map_list_of
("listFont", FontDef(0.045f, ""))
("descriptionFont", FontDef(0.035f, ""));
std::map<std::string, unsigned int> ThemeData::sDefaultColors = boost::assign::map_list_of
("listPrimaryColor", 0x0000FFFF)
("listSecondaryColor", 0x00FF00FF)
("listSelectorColor", 0x000000FF)
("listSelectedColor", 0x00000000)
("descriptionColor", 0x48474DFF);
std::map<std::string, ImageDef> ThemeData::sDefaultImages = boost::assign::map_list_of
("backgroundImage", ImageDef("", true))
("headerImage", ImageDef("", false));
std::map<std::string, SoundDef> ThemeData::sDefaultSounds = boost::assign::map_list_of
("scrollSound", SoundDef(""))
("gameSelectSound", SoundDef(""))
("backSound", SoundDef(""))
("menuOpenSound", 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()
{
setDefaults();
std::string defaultDir = getHomePath() + "/.emulationstation/es_theme_default.xml";
if(boost::filesystem::exists(defaultDir))
loadFile(defaultDir);
}
void ThemeData::setDefaults()
{
mFontMap.clear();
mImageMap.clear();
mColorMap.clear();
mSoundMap.clear();
mFontMap = sDefaultFonts;
mImageMap = sDefaultImages;
mColorMap = sDefaultColors;
mSoundMap = sDefaultSounds;
}
unsigned int getHexColor(const char* str, unsigned int defaultColor)
{
if(!str)
return defaultColor;
size_t len = strlen(str);
if(len != 6 && len != 8)
{
LOG(LogError) << "Invalid theme color \"" << str << "\" (must be 6 or 8 characters)";
return defaultColor;
}
unsigned int val;
std::stringstream ss;
ss << str;
ss >> std::hex >> val;
if(len == 6)
val = (val << 8) | 0xFF;
return val;
}
std::string resolvePath(const char* in, const std::string& relative)
{
if(!in || in[0] == '\0')
return in;
boost::filesystem::path relPath(relative);
relPath = relPath.parent_path();
boost::filesystem::path path(in);
// we use boost filesystem here instead of just string checks because
// some directories could theoretically start with ~ or .
if(*path.begin() == "~")
{
path = getHomePath() + (in + 1);
}else if(*path.begin() == ".")
{
path = relPath / (in + 1);
}
return path.generic_string();
}
void ThemeData::loadFile(const std::string& themePath)
{
if(themePath.empty() || !boost::filesystem::exists(themePath))
return;
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(themePath.c_str());
if(!result)
{
LOG(LogWarning) << "Could not parse theme file \"" << themePath << "\":\n " << result.description();
return;
}
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)
{
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;
}
float size = node.child("size").text().as_float(it->second.size);
mFontMap[it->first] = FontDef(size, path);
root.remove_child(node);
}
}
// 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();
}
#pragma once
#include <memory>
#include <map>
#include <string>
#include "resources/Font.h"
#include "resources/TextureResource.h"
#include "Renderer.h"
#include "AudioManager.h"
#include "Sound.h"
struct FontDef
{
FontDef() {}
FontDef(float sz, const std::string& p) : path(p), size(sz) {}
std::string path;
float size;
inline const std::shared_ptr<Font>& get() const { if(!font) font = Font::get((int)(size * Renderer::getScreenHeight()), path); return font; }
private:
mutable std::shared_ptr<Font> font;
};
struct ImageDef
{
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;
};
class ThemeData
{
public:
static const std::shared_ptr<ThemeData>& getDefault();
ThemeData();
void setDefaults();
void loadFile(const std::string& path);
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;
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;
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;
};
...@@ -6,11 +6,14 @@ ...@@ -6,11 +6,14 @@
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include <iomanip> #include <iomanip>
#include "views/ViewController.h"
Window::Window() : mNormalizeNextUpdate(false), mFrameTimeElapsed(0), mFrameCountElapsed(0), mAverageDeltaTime(10), Window::Window() : mNormalizeNextUpdate(false), mFrameTimeElapsed(0), mFrameCountElapsed(0), mAverageDeltaTime(10),
mZoomFactor(1.0f), mCenterPoint(0, 0), mMatrix(Eigen::Affine3f::Identity()), mFadePercent(0.0f), mAllowSleep(true) mZoomFactor(1.0f), mCenterPoint(0, 0), mMatrix(Eigen::Affine3f::Identity()), mFadePercent(0.0f), mAllowSleep(true)
{ {
mInputManager = new InputManager(this); mInputManager = new InputManager(this);
mViewController = new ViewController(this);
pushGui(mViewController);
setCenterPoint(Eigen::Vector2f(Renderer::getScreenWidth() / 2, Renderer::getScreenHeight() / 2)); setCenterPoint(Eigen::Vector2f(Renderer::getScreenWidth() / 2, Renderer::getScreenHeight() / 2