mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 15:45:38 +00:00
Added variant trigger support.
This commit is contained in:
parent
549d78dfed
commit
fa67018b72
|
@ -239,7 +239,7 @@ SystemData::SystemData(const std::string& name,
|
||||||
mPlaceholder = new FileData(PLACEHOLDER, "<No Entries Found>", getSystemEnvData(), this);
|
mPlaceholder = new FileData(PLACEHOLDER, "<No Entries Found>", getSystemEnvData(), this);
|
||||||
|
|
||||||
setIsGameSystemStatus();
|
setIsGameSystemStatus();
|
||||||
loadTheme();
|
loadTheme(ThemeTriggers::TriggerType::NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemData::~SystemData()
|
SystemData::~SystemData()
|
||||||
|
@ -1304,7 +1304,7 @@ std::pair<unsigned int, unsigned int> SystemData::getDisplayedGameCount() const
|
||||||
return mRootFolder->getGameCount();
|
return mRootFolder->getGameCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemData::loadTheme()
|
void SystemData::loadTheme(ThemeTriggers::TriggerType trigger)
|
||||||
{
|
{
|
||||||
mTheme = std::make_shared<ThemeData>();
|
mTheme = std::make_shared<ThemeData>();
|
||||||
|
|
||||||
|
@ -1354,7 +1354,7 @@ void SystemData::loadTheme()
|
||||||
sysData.insert(std::pair<std::string, std::string>("system.theme.collections", "\b"));
|
sysData.insert(std::pair<std::string, std::string>("system.theme.collections", "\b"));
|
||||||
}
|
}
|
||||||
|
|
||||||
mTheme->loadFile(sysData, path, isCustomCollection());
|
mTheme->loadFile(sysData, path, trigger, isCustomCollection());
|
||||||
}
|
}
|
||||||
catch (ThemeException& e) {
|
catch (ThemeException& e) {
|
||||||
LOG(LogError) << e.what() << " (system \"" << mName << "\", theme \"" << mThemeFolder
|
LOG(LogError) << e.what() << " (system \"" << mName << "\", theme \"" << mThemeFolder
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define ES_APP_SYSTEM_DATA_H
|
#define ES_APP_SYSTEM_DATA_H
|
||||||
|
|
||||||
#include "PlatformId.h"
|
#include "PlatformId.h"
|
||||||
|
#include "ThemeData.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -134,8 +135,8 @@ public:
|
||||||
|
|
||||||
void sortSystem(bool reloadGamelist = true, bool jumpToFirstRow = false);
|
void sortSystem(bool reloadGamelist = true, bool jumpToFirstRow = false);
|
||||||
|
|
||||||
// Load or re-load theme.
|
// Load or reload theme.
|
||||||
void loadTheme();
|
void loadTheme(ThemeTriggers::TriggerType trigger);
|
||||||
|
|
||||||
FileFilterIndex* getIndex() { return mFilterIndex; }
|
FileFilterIndex* getIndex() { return mFilterIndex; }
|
||||||
void onMetaDataSavePoint();
|
void onMetaDataSavePoint();
|
||||||
|
|
|
@ -739,22 +739,22 @@ std::shared_ptr<GamelistView> ViewController::getGamelistView(SystemData* system
|
||||||
// If there's no entry, then create it and return it.
|
// If there's no entry, then create it and return it.
|
||||||
std::shared_ptr<GamelistView> view;
|
std::shared_ptr<GamelistView> view;
|
||||||
|
|
||||||
bool themeHasVideoView {system->getTheme()->hasView("video")};
|
|
||||||
|
|
||||||
// Decide which view style to use.
|
|
||||||
GamelistViewStyle selectedViewStyle {AUTOMATIC};
|
|
||||||
|
|
||||||
std::string viewPreference {Settings::getInstance()->getString("GamelistViewStyle")};
|
|
||||||
if (viewPreference == "basic")
|
|
||||||
selectedViewStyle = BASIC;
|
|
||||||
else if (viewPreference == "detailed")
|
|
||||||
selectedViewStyle = DETAILED;
|
|
||||||
else if (viewPreference == "video")
|
|
||||||
selectedViewStyle = VIDEO;
|
|
||||||
|
|
||||||
if (system->getTheme()->isLegacyTheme()) {
|
if (system->getTheme()->isLegacyTheme()) {
|
||||||
|
const bool themeHasVideoView {system->getTheme()->hasView("video")};
|
||||||
|
|
||||||
|
// Decide which view style to use.
|
||||||
|
GamelistViewStyle selectedViewStyle {AUTOMATIC};
|
||||||
|
|
||||||
|
const std::string& viewPreference {Settings::getInstance()->getString("GamelistViewStyle")};
|
||||||
|
if (viewPreference == "basic")
|
||||||
|
selectedViewStyle = BASIC;
|
||||||
|
else if (viewPreference == "detailed")
|
||||||
|
selectedViewStyle = DETAILED;
|
||||||
|
else if (viewPreference == "video")
|
||||||
|
selectedViewStyle = VIDEO;
|
||||||
|
|
||||||
if (selectedViewStyle == AUTOMATIC) {
|
if (selectedViewStyle == AUTOMATIC) {
|
||||||
std::vector<FileData*> files {
|
const std::vector<FileData*> files {
|
||||||
system->getRootFolder()->getFilesRecursive(GAME | FOLDER)};
|
system->getRootFolder()->getFilesRecursive(GAME | FOLDER)};
|
||||||
|
|
||||||
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
||||||
|
@ -768,29 +768,125 @@ std::shared_ptr<GamelistView> ViewController::getGamelistView(SystemData* system
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Create the view.
|
||||||
|
switch (selectedViewStyle) {
|
||||||
|
case VIDEO: {
|
||||||
|
mState.viewstyle = VIDEO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DETAILED: {
|
||||||
|
mState.viewstyle = DETAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BASIC: {
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (!system->isGroupedCustomCollection())
|
||||||
|
mState.viewstyle = BASIC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Variant triggers.
|
||||||
|
const auto overrides = system->getTheme()->getCurrentThemeSetSelectedVariantOverrides();
|
||||||
|
|
||||||
// Create the view.
|
if (!overrides.empty()) {
|
||||||
switch (selectedViewStyle) {
|
ThemeTriggers::TriggerType noVideosTriggerType {ThemeTriggers::TriggerType::NONE};
|
||||||
case VIDEO: {
|
ThemeTriggers::TriggerType noMediaTriggerType {ThemeTriggers::TriggerType::NONE};
|
||||||
mState.viewstyle = VIDEO;
|
|
||||||
break;
|
const std::vector<FileData*> files {
|
||||||
}
|
system->getRootFolder()->getFilesRecursive(GAME | FOLDER)};
|
||||||
case DETAILED: {
|
|
||||||
mState.viewstyle = DETAILED;
|
if (overrides.find(ThemeTriggers::TriggerType::NO_VIDEOS) != overrides.end()) {
|
||||||
break;
|
noVideosTriggerType = ThemeTriggers::TriggerType::NO_VIDEOS;
|
||||||
}
|
|
||||||
case BASIC: {
|
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
||||||
}
|
if (!(*it)->getVideoPath().empty()) {
|
||||||
default: {
|
noVideosTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
if (!system->isGroupedCustomCollection())
|
break;
|
||||||
mState.viewstyle = BASIC;
|
}
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overrides.find(ThemeTriggers::TriggerType::NO_MEDIA) != overrides.end()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NO_MEDIA;
|
||||||
|
|
||||||
|
for (auto& imageType : overrides.at(ThemeTriggers::TriggerType::NO_MEDIA).second) {
|
||||||
|
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
||||||
|
if (imageType == "miximage") {
|
||||||
|
if (!(*it)->getMiximagePath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "marquee") {
|
||||||
|
if (!(*it)->getMarqueePath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "screenshot") {
|
||||||
|
if (!(*it)->getScreenshotPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "titlescreen") {
|
||||||
|
if (!(*it)->getTitleScreenPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "cover") {
|
||||||
|
if (!(*it)->getCoverPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "backcover") {
|
||||||
|
if (!(*it)->getBackCoverPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "3dbox") {
|
||||||
|
if (!(*it)->get3DBoxPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "physicalmedia") {
|
||||||
|
if (!(*it)->getPhysicalMediaPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "fanart") {
|
||||||
|
if (!(*it)->getFanArtPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imageType == "video") {
|
||||||
|
if (!(*it)->getVideoPath().empty()) {
|
||||||
|
noMediaTriggerType = ThemeTriggers::TriggerType::NONE;
|
||||||
|
goto BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BREAK:
|
||||||
|
// noMedia takes precedence over the noVideos trigger.
|
||||||
|
if (noMediaTriggerType == ThemeTriggers::TriggerType::NO_MEDIA)
|
||||||
|
system->loadTheme(noMediaTriggerType);
|
||||||
|
else
|
||||||
|
system->loadTheme(noVideosTriggerType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view = std::shared_ptr<GamelistView>(new GamelistView(system->getRootFolder()));
|
view = std::shared_ptr<GamelistView>(new GamelistView(system->getRootFolder()));
|
||||||
|
|
||||||
view->setTheme(system->getTheme());
|
view->setTheme(system->getTheme());
|
||||||
|
|
||||||
std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
|
std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
|
||||||
|
@ -970,7 +1066,7 @@ void ViewController::preload()
|
||||||
|
|
||||||
// Load navigation sounds, either from the theme if it supports it, or otherwise from
|
// Load navigation sounds, either from the theme if it supports it, or otherwise from
|
||||||
// the bundled fallback sound files.
|
// the bundled fallback sound files.
|
||||||
bool themeSoundSupport = false;
|
bool themeSoundSupport {false};
|
||||||
for (SystemData* system : SystemData::sSystemVector) {
|
for (SystemData* system : SystemData::sSystemVector) {
|
||||||
if (system->getTheme()->hasView("all")) {
|
if (system->getTheme()->hasView("all")) {
|
||||||
NavigationSounds::getInstance().loadThemeNavigationSounds(system->getTheme().get());
|
NavigationSounds::getInstance().loadThemeNavigationSounds(system->getTheme().get());
|
||||||
|
@ -1000,7 +1096,7 @@ void ViewController::reloadGamelistView(GamelistView* view, bool reloadTheme)
|
||||||
mCurrentView = nullptr;
|
mCurrentView = nullptr;
|
||||||
|
|
||||||
if (reloadTheme)
|
if (reloadTheme)
|
||||||
system->loadTheme();
|
system->loadTheme(ThemeTriggers::TriggerType::NONE);
|
||||||
system->getIndex()->setKidModeFilters();
|
system->getIndex()->setKidModeFilters();
|
||||||
std::shared_ptr<GamelistView> newView {getGamelistView(system)};
|
std::shared_ptr<GamelistView> newView {getGamelistView(system)};
|
||||||
|
|
||||||
|
@ -1047,15 +1143,18 @@ void ViewController::reloadAll()
|
||||||
|
|
||||||
// Load themes, create GamelistViews and reset filters.
|
// Load themes, create GamelistViews and reset filters.
|
||||||
for (auto it = cursorMap.cbegin(); it != cursorMap.cend(); ++it) {
|
for (auto it = cursorMap.cbegin(); it != cursorMap.cend(); ++it) {
|
||||||
it->first->loadTheme();
|
it->first->loadTheme(ThemeTriggers::TriggerType::NONE);
|
||||||
it->first->getIndex()->resetFilters();
|
it->first->getIndex()->resetFilters();
|
||||||
getGamelistView(it->first)->setCursor(it->second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild SystemListView.
|
// Rebuild SystemListView.
|
||||||
mSystemListView.reset();
|
mSystemListView.reset();
|
||||||
getSystemListView();
|
getSystemListView();
|
||||||
|
|
||||||
|
// Restore cursor positions for all systems.
|
||||||
|
for (auto it = cursorMap.cbegin(); it != cursorMap.cend(); ++it)
|
||||||
|
getGamelistView(it->first)->setCursor(it->second);
|
||||||
|
|
||||||
// Update mCurrentView since the pointers changed.
|
// Update mCurrentView since the pointers changed.
|
||||||
if (mState.viewing == GAMELIST) {
|
if (mState.viewing == GAMELIST) {
|
||||||
mCurrentView = getGamelistView(mState.getSystem());
|
mCurrentView = getGamelistView(mState.getSystem());
|
||||||
|
@ -1073,7 +1172,7 @@ void ViewController::reloadAll()
|
||||||
// Load navigation sounds, either from the theme if it supports it, or otherwise from
|
// Load navigation sounds, either from the theme if it supports it, or otherwise from
|
||||||
// the bundled fallback sound files.
|
// the bundled fallback sound files.
|
||||||
NavigationSounds::getInstance().deinit();
|
NavigationSounds::getInstance().deinit();
|
||||||
bool themeSoundSupport = false;
|
bool themeSoundSupport {false};
|
||||||
for (SystemData* system : SystemData::sSystemVector) {
|
for (SystemData* system : SystemData::sSystemVector) {
|
||||||
if (system->getTheme()->hasView("all")) {
|
if (system->getTheme()->hasView("all")) {
|
||||||
NavigationSounds::getInstance().loadThemeNavigationSounds(system->getTheme().get());
|
NavigationSounds::getInstance().loadThemeNavigationSounds(system->getTheme().get());
|
||||||
|
|
|
@ -28,6 +28,18 @@ std::vector<std::string> ThemeData::sSupportedViews {
|
||||||
{"system"},
|
{"system"},
|
||||||
{"gamelist"}};
|
{"gamelist"}};
|
||||||
|
|
||||||
|
std::vector<std::string> ThemeData::sSupportedMediaTypes {
|
||||||
|
{"miximage"},
|
||||||
|
{"marquee"},
|
||||||
|
{"screenshot"},
|
||||||
|
{"titlescreen"},
|
||||||
|
{"cover"},
|
||||||
|
{"backcover"},
|
||||||
|
{"3dbox"},
|
||||||
|
{"physicalmedia"},
|
||||||
|
{"fanart"},
|
||||||
|
{"video"}};
|
||||||
|
|
||||||
std::vector<std::string> ThemeData::sLegacySupportedViews {
|
std::vector<std::string> ThemeData::sLegacySupportedViews {
|
||||||
{"all"},
|
{"all"},
|
||||||
{"system"},
|
{"system"},
|
||||||
|
@ -497,9 +509,11 @@ ThemeData::ThemeData()
|
||||||
|
|
||||||
void ThemeData::loadFile(const std::map<std::string, std::string>& sysDataMap,
|
void ThemeData::loadFile(const std::map<std::string, std::string>& sysDataMap,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
|
const ThemeTriggers::TriggerType trigger,
|
||||||
const bool customCollection)
|
const bool customCollection)
|
||||||
{
|
{
|
||||||
mCustomCollection = customCollection;
|
mCustomCollection = customCollection;
|
||||||
|
mOverrideVariant = "";
|
||||||
|
|
||||||
mPaths.push_back(path);
|
mPaths.push_back(path);
|
||||||
|
|
||||||
|
@ -565,6 +579,12 @@ void ThemeData::loadFile(const std::map<std::string, std::string>& sysDataMap,
|
||||||
mSelectedVariant = mVariants.front();
|
mSelectedVariant = mVariants.front();
|
||||||
// Special shortcut variant to apply configuration to all defined variants.
|
// Special shortcut variant to apply configuration to all defined variants.
|
||||||
mVariants.emplace_back("all");
|
mVariants.emplace_back("all");
|
||||||
|
|
||||||
|
if (trigger != ThemeTriggers::TriggerType::NONE) {
|
||||||
|
auto overrides = getCurrentThemeSetSelectedVariantOverrides();
|
||||||
|
if (overrides.find(trigger) != overrides.end())
|
||||||
|
mOverrideVariant = overrides.at(trigger).first;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCurrentThemeSet->second.capabilities.colorSchemes.size() > 0) {
|
if (mCurrentThemeSet->second.capabilities.colorSchemes.size() > 0) {
|
||||||
|
@ -809,6 +829,21 @@ const std::string ThemeData::getAspectRatioLabel(const std::string& aspectRatio)
|
||||||
return "invalid ratio";
|
return "invalid ratio";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::map<ThemeTriggers::TriggerType, std::pair<std::string, std::vector<std::string>>>
|
||||||
|
ThemeData::getCurrentThemeSetSelectedVariantOverrides()
|
||||||
|
{
|
||||||
|
const auto variantIter = std::find_if(
|
||||||
|
mCurrentThemeSet->second.capabilities.variants.cbegin(),
|
||||||
|
mCurrentThemeSet->second.capabilities.variants.cend(),
|
||||||
|
[this](ThemeVariant currVariant) { return currVariant.name == mSelectedVariant; });
|
||||||
|
|
||||||
|
if (variantIter != mCurrentThemeSet->second.capabilities.variants.cend() &&
|
||||||
|
!(*variantIter).overrides.empty())
|
||||||
|
return (*variantIter).overrides;
|
||||||
|
else
|
||||||
|
return ThemeVariant().overrides;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int ThemeData::getHexColor(const std::string& str)
|
unsigned int ThemeData::getHexColor(const std::string& str)
|
||||||
{
|
{
|
||||||
ThemeException error;
|
ThemeException error;
|
||||||
|
@ -816,20 +851,20 @@ unsigned int ThemeData::getHexColor(const std::string& str)
|
||||||
if (str == "")
|
if (str == "")
|
||||||
throw error << "Empty color property";
|
throw error << "Empty color property";
|
||||||
|
|
||||||
size_t len {str.size()};
|
const size_t length {str.size()};
|
||||||
if (len != 6 && len != 8)
|
if (length != 6 && length != 8)
|
||||||
throw error << "Invalid color property \"" << str
|
throw error << "Invalid color property \"" << str
|
||||||
<< "\" (must be 6 or 8 characters in length)";
|
<< "\" (must be 6 or 8 characters in length)";
|
||||||
|
|
||||||
unsigned int val;
|
unsigned int value;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << str;
|
ss << str;
|
||||||
ss >> std::hex >> val;
|
ss >> std::hex >> value;
|
||||||
|
|
||||||
if (len == 6)
|
if (length == 6)
|
||||||
val = (val << 8) | 0xFF;
|
value = (value << 8) | 0xFF;
|
||||||
|
|
||||||
return val;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ThemeData::resolvePlaceholders(const std::string& in)
|
std::string ThemeData::resolvePlaceholders(const std::string& in)
|
||||||
|
@ -854,18 +889,19 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
{
|
{
|
||||||
ThemeCapability capabilities;
|
ThemeCapability capabilities;
|
||||||
std::vector<std::string> aspectRatiosTemp;
|
std::vector<std::string> aspectRatiosTemp;
|
||||||
|
bool hasTriggers {false};
|
||||||
|
|
||||||
std::string capFile {path + "/capabilities.xml"};
|
const std::string capFile {path + "/capabilities.xml"};
|
||||||
|
|
||||||
if (Utils::FileSystem::isRegularFile(capFile) || Utils::FileSystem::isSymlink(capFile)) {
|
if (Utils::FileSystem::isRegularFile(capFile) || Utils::FileSystem::isSymlink(capFile)) {
|
||||||
capabilities.legacyTheme = false;
|
capabilities.legacyTheme = false;
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res {
|
const pugi::xml_parse_result& res {
|
||||||
doc.load_file(Utils::String::stringToWideString(capFile).c_str())};
|
doc.load_file(Utils::String::stringToWideString(capFile).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res {doc.load_file(capFile.c_str())};
|
const pugi::xml_parse_result& res {doc.load_file(capFile.c_str())};
|
||||||
#endif
|
#endif
|
||||||
if (res.status == pugi::status_no_document_element) {
|
if (res.status == pugi::status_no_document_element) {
|
||||||
LOG(LogDebug) << "Found a capabilities.xml file with no configuration";
|
LOG(LogDebug) << "Found a capabilities.xml file with no configuration";
|
||||||
|
@ -874,7 +910,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
LOG(LogError) << "Couldn't parse capabilities.xml: " << res.description();
|
LOG(LogError) << "Couldn't parse capabilities.xml: " << res.description();
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
pugi::xml_node themeCapabilities {doc.child("themeCapabilities")};
|
const pugi::xml_node& themeCapabilities {doc.child("themeCapabilities")};
|
||||||
if (!themeCapabilities) {
|
if (!themeCapabilities) {
|
||||||
LOG(LogError) << "Missing <themeCapabilities> tag in capabilities.xml";
|
LOG(LogError) << "Missing <themeCapabilities> tag in capabilities.xml";
|
||||||
return capabilities;
|
return capabilities;
|
||||||
|
@ -882,7 +918,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
|
|
||||||
for (pugi::xml_node aspectRatio {themeCapabilities.child("aspectRatio")}; aspectRatio;
|
for (pugi::xml_node aspectRatio {themeCapabilities.child("aspectRatio")}; aspectRatio;
|
||||||
aspectRatio = aspectRatio.next_sibling("aspectRatio")) {
|
aspectRatio = aspectRatio.next_sibling("aspectRatio")) {
|
||||||
std::string value {aspectRatio.text().get()};
|
const std::string& value {aspectRatio.text().get()};
|
||||||
if (std::find_if(sSupportedAspectRatios.cbegin(), sSupportedAspectRatios.cend(),
|
if (std::find_if(sSupportedAspectRatios.cbegin(), sSupportedAspectRatios.cend(),
|
||||||
[&value](const std::pair<std::string, std::string>& entry) {
|
[&value](const std::pair<std::string, std::string>& entry) {
|
||||||
return entry.first == value;
|
return entry.first == value;
|
||||||
|
@ -906,7 +942,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
for (pugi::xml_node variant {themeCapabilities.child("variant")}; variant;
|
for (pugi::xml_node variant {themeCapabilities.child("variant")}; variant;
|
||||||
variant = variant.next_sibling("variant")) {
|
variant = variant.next_sibling("variant")) {
|
||||||
ThemeVariant readVariant;
|
ThemeVariant readVariant;
|
||||||
std::string name {variant.attribute("name").as_string()};
|
const std::string& name {variant.attribute("name").as_string()};
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
LOG(LogWarning)
|
LOG(LogWarning)
|
||||||
<< "Found <variant> tag without name attribute, ignoring entry in \"" << capFile
|
<< "Found <variant> tag without name attribute, ignoring entry in \"" << capFile
|
||||||
|
@ -921,7 +957,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
readVariant.name = name;
|
readVariant.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node labelTag {variant.child("label")};
|
const pugi::xml_node& labelTag {variant.child("label")};
|
||||||
if (labelTag == nullptr) {
|
if (labelTag == nullptr) {
|
||||||
LOG(LogDebug)
|
LOG(LogDebug)
|
||||||
<< "No variant <label> tag found, setting label value to the variant name \""
|
<< "No variant <label> tag found, setting label value to the variant name \""
|
||||||
|
@ -929,7 +965,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
readVariant.label = name;
|
readVariant.label = name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::string labelValue {labelTag.text().as_string()};
|
const std::string& labelValue {labelTag.text().as_string()};
|
||||||
if (labelValue == "") {
|
if (labelValue == "") {
|
||||||
LOG(LogWarning) << "No variant <label> value defined, setting value to "
|
LOG(LogWarning) << "No variant <label> value defined, setting value to "
|
||||||
"the variant name \""
|
"the variant name \""
|
||||||
|
@ -941,9 +977,9 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node selectableTag {variant.child("selectable")};
|
const pugi::xml_node& selectableTag {variant.child("selectable")};
|
||||||
if (selectableTag != nullptr) {
|
if (selectableTag != nullptr) {
|
||||||
std::string value {selectableTag.text().as_string()};
|
const std::string& value {selectableTag.text().as_string()};
|
||||||
if (value.front() == '0' || value.front() == 'f' || value.front() == 'F' ||
|
if (value.front() == '0' || value.front() == 'f' || value.front() == 'F' ||
|
||||||
value.front() == 'n' || value.front() == 'N')
|
value.front() == 'n' || value.front() == 'N')
|
||||||
readVariant.selectable = false;
|
readVariant.selectable = false;
|
||||||
|
@ -951,37 +987,82 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
readVariant.selectable = true;
|
readVariant.selectable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node overrideTag {variant.child("override")};
|
for (pugi::xml_node overrideTag {variant.child("override")}; overrideTag;
|
||||||
if (overrideTag != nullptr) {
|
overrideTag = overrideTag.next_sibling("override")) {
|
||||||
pugi::xml_node triggerTag {overrideTag.child("trigger")};
|
if (overrideTag != nullptr) {
|
||||||
if (triggerTag != nullptr) {
|
std::vector<std::string> mediaTypes;
|
||||||
std::string triggerValue {triggerTag.text().as_string()};
|
const pugi::xml_node& mediaTypeTag {overrideTag.child("mediaType")};
|
||||||
if (triggerValue == "") {
|
if (mediaTypeTag != nullptr) {
|
||||||
LOG(LogWarning)
|
std::string mediaTypeValue {mediaTypeTag.text().as_string()};
|
||||||
<< "No <trigger> tag value defined for variant \"" << readVariant.name
|
for (auto& character : mediaTypeValue) {
|
||||||
<< "\", ignoring entry in \"" << capFile << "\"";
|
if (std::isspace(character))
|
||||||
}
|
character = ',';
|
||||||
else {
|
}
|
||||||
pugi::xml_node useVariantTag {overrideTag.child("useVariant")};
|
mediaTypeValue = Utils::String::replace(mediaTypeValue, ",,", ",");
|
||||||
if (useVariantTag != nullptr) {
|
mediaTypes = Utils::String::delimitedStringToVector(mediaTypeValue, ",");
|
||||||
std::string useVariantValue {useVariantTag.text().as_string()};
|
|
||||||
if (useVariantValue == "") {
|
for (std::string& type : mediaTypes) {
|
||||||
LOG(LogWarning)
|
if (std::find(sSupportedMediaTypes.cbegin(),
|
||||||
<< "No <useVariant> tag value defined for variant \""
|
sSupportedMediaTypes.cend(),
|
||||||
<< readVariant.name << "\", ignoring entry in \"" << capFile
|
type) == sSupportedMediaTypes.cend()) {
|
||||||
<< "\"";
|
LOG(LogError) << "ThemeData::parseThemeCapabilities(): Invalid "
|
||||||
}
|
"override configuration, unsupported "
|
||||||
else {
|
"\"mediaType\" value \""
|
||||||
readVariant.override = true;
|
<< type << "\"";
|
||||||
readVariant.overrideTrigger = triggerValue;
|
mediaTypes.clear();
|
||||||
readVariant.overrideVariant = useVariantValue;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pugi::xml_node& triggerTag {overrideTag.child("trigger")};
|
||||||
|
if (triggerTag != nullptr) {
|
||||||
|
const std::string& triggerValue {triggerTag.text().as_string()};
|
||||||
|
if (triggerValue == "") {
|
||||||
|
LOG(LogWarning) << "No <trigger> tag value defined for variant \""
|
||||||
|
<< readVariant.name << "\", ignoring entry in \""
|
||||||
|
<< capFile << "\"";
|
||||||
|
}
|
||||||
|
else if (triggerValue != "noVideos" && triggerValue != "noMedia") {
|
||||||
|
LOG(LogWarning) << "Invalid <useVariant> tag value \"" << triggerValue
|
||||||
|
<< "\" defined for variant \"" << readVariant.name
|
||||||
|
<< "\", ignoring entry in \"" << capFile << "\"";
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogWarning) << "Found an <override> tag without a corresponding "
|
const pugi::xml_node& useVariantTag {overrideTag.child("useVariant")};
|
||||||
"<useVariant> tag, "
|
if (useVariantTag != nullptr) {
|
||||||
<< "ignoring entry for variant \"" << readVariant.name
|
const std::string& useVariantValue {
|
||||||
<< "\" in \"" << capFile << "\"";
|
useVariantTag.text().as_string()};
|
||||||
|
if (useVariantValue == "") {
|
||||||
|
LOG(LogWarning)
|
||||||
|
<< "No <useVariant> tag value defined for variant \""
|
||||||
|
<< readVariant.name << "\", ignoring entry in \"" << capFile
|
||||||
|
<< "\"";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hasTriggers = true;
|
||||||
|
if (triggerValue == "noVideos") {
|
||||||
|
readVariant
|
||||||
|
.overrides[ThemeTriggers::TriggerType::NO_VIDEOS] =
|
||||||
|
std::make_pair(useVariantValue,
|
||||||
|
std::vector<std::string>());
|
||||||
|
}
|
||||||
|
else if (triggerValue == "noMedia") {
|
||||||
|
if (mediaTypes.empty())
|
||||||
|
mediaTypes.emplace_back("miximage");
|
||||||
|
readVariant
|
||||||
|
.overrides[ThemeTriggers::TriggerType::NO_MEDIA] =
|
||||||
|
std::make_pair(useVariantValue, mediaTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG(LogWarning)
|
||||||
|
<< "Found an <override> tag without a corresponding "
|
||||||
|
"<useVariant> tag, "
|
||||||
|
<< "ignoring entry for variant \"" << readVariant.name
|
||||||
|
<< "\" in \"" << capFile << "\"";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1012,7 +1093,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
for (pugi::xml_node colorScheme {themeCapabilities.child("colorScheme")}; colorScheme;
|
for (pugi::xml_node colorScheme {themeCapabilities.child("colorScheme")}; colorScheme;
|
||||||
colorScheme = colorScheme.next_sibling("colorScheme")) {
|
colorScheme = colorScheme.next_sibling("colorScheme")) {
|
||||||
ThemeColorScheme readColorScheme;
|
ThemeColorScheme readColorScheme;
|
||||||
std::string name {colorScheme.attribute("name").as_string()};
|
const std::string& name {colorScheme.attribute("name").as_string()};
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
LOG(LogWarning)
|
LOG(LogWarning)
|
||||||
<< "Found <colorScheme> tag without name attribute, ignoring entry in \""
|
<< "Found <colorScheme> tag without name attribute, ignoring entry in \""
|
||||||
|
@ -1022,7 +1103,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
readColorScheme.name = name;
|
readColorScheme.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node labelTag {colorScheme.child("label")};
|
const pugi::xml_node& labelTag {colorScheme.child("label")};
|
||||||
if (labelTag == nullptr) {
|
if (labelTag == nullptr) {
|
||||||
LOG(LogDebug) << "No colorScheme <label> tag found, setting label value to the "
|
LOG(LogDebug) << "No colorScheme <label> tag found, setting label value to the "
|
||||||
"color scheme name \""
|
"color scheme name \""
|
||||||
|
@ -1030,7 +1111,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
readColorScheme.label = name;
|
readColorScheme.label = name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::string labelValue {labelTag.text().as_string()};
|
const std::string& labelValue {labelTag.text().as_string()};
|
||||||
if (labelValue == "") {
|
if (labelValue == "") {
|
||||||
LOG(LogWarning) << "No colorScheme <label> value defined, setting value to "
|
LOG(LogWarning) << "No colorScheme <label> value defined, setting value to "
|
||||||
"the color scheme name \""
|
"the color scheme name \""
|
||||||
|
@ -1077,6 +1158,28 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasTriggers) {
|
||||||
|
for (auto& variant : capabilities.variants) {
|
||||||
|
for (auto it = variant.overrides.begin(); it != variant.overrides.end();) {
|
||||||
|
const auto variantIter =
|
||||||
|
std::find_if(capabilities.variants.begin(), capabilities.variants.end(),
|
||||||
|
[it](ThemeVariant currVariant) {
|
||||||
|
return currVariant.name == (*it).second.first;
|
||||||
|
});
|
||||||
|
if (variantIter == capabilities.variants.end()) {
|
||||||
|
LOG(LogWarning)
|
||||||
|
<< "The <useVariant> tag value \"" << (*it).second.first
|
||||||
|
<< "\" does not match any defined variants, ignoring entry in \"" << capFile
|
||||||
|
<< "\"";
|
||||||
|
it = variant.overrides.erase(it);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1214,7 +1317,10 @@ void ThemeData::parseVariants(const pugi::xml_node& root)
|
||||||
<< "\" is not defined in capabilities.xml";
|
<< "\" is not defined in capabilities.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSelectedVariant == viewKey || viewKey == "all") {
|
const std::string variant {mOverrideVariant.empty() ? mSelectedVariant :
|
||||||
|
mOverrideVariant};
|
||||||
|
|
||||||
|
if (variant == viewKey || viewKey == "all") {
|
||||||
parseVariables(node);
|
parseVariables(node);
|
||||||
parseColorSchemes(node);
|
parseColorSchemes(node);
|
||||||
parseIncludes(node);
|
parseIncludes(node);
|
||||||
|
|
|
@ -66,6 +66,15 @@ namespace ThemeFlags
|
||||||
// clang-format on
|
// clang-format on
|
||||||
} // namespace ThemeFlags
|
} // namespace ThemeFlags
|
||||||
|
|
||||||
|
namespace ThemeTriggers
|
||||||
|
{
|
||||||
|
enum class TriggerType {
|
||||||
|
NONE,
|
||||||
|
NO_VIDEOS,
|
||||||
|
NO_MEDIA
|
||||||
|
};
|
||||||
|
} // namespace ThemeTriggers
|
||||||
|
|
||||||
class ThemeException : public std::exception
|
class ThemeException : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -175,13 +184,11 @@ public:
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string label;
|
std::string label;
|
||||||
bool selectable;
|
bool selectable;
|
||||||
bool override;
|
std::map<ThemeTriggers::TriggerType, std::pair<std::string, std::vector<std::string>>>
|
||||||
std::string overrideTrigger;
|
overrides;
|
||||||
std::string overrideVariant;
|
|
||||||
|
|
||||||
ThemeVariant()
|
ThemeVariant()
|
||||||
: selectable {false}
|
: selectable {false}
|
||||||
, override {false}
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -218,6 +225,7 @@ public:
|
||||||
|
|
||||||
void loadFile(const std::map<std::string, std::string>& sysDataMap,
|
void loadFile(const std::map<std::string, std::string>& sysDataMap,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
|
const ThemeTriggers::TriggerType trigger,
|
||||||
const bool customCollection);
|
const bool customCollection);
|
||||||
bool hasView(const std::string& view);
|
bool hasView(const std::string& view);
|
||||||
ThemeView& getViewElements(std::string view) { return mViews[view]; }
|
ThemeView& getViewElements(std::string view) { return mViews[view]; }
|
||||||
|
@ -239,6 +247,8 @@ public:
|
||||||
const static std::string getCurrentThemeSetName() { return mCurrentThemeSet->first; }
|
const static std::string getCurrentThemeSetName() { return mCurrentThemeSet->first; }
|
||||||
|
|
||||||
const bool isLegacyTheme() { return mLegacyTheme; }
|
const bool isLegacyTheme() { return mLegacyTheme; }
|
||||||
|
const std::map<ThemeTriggers::TriggerType, std::pair<std::string, std::vector<std::string>>>
|
||||||
|
getCurrentThemeSetSelectedVariantOverrides();
|
||||||
|
|
||||||
enum ElementPropertyType {
|
enum ElementPropertyType {
|
||||||
NORMALIZED_RECT,
|
NORMALIZED_RECT,
|
||||||
|
@ -280,6 +290,7 @@ private:
|
||||||
const LegacyWorkaround legacyWorkaround);
|
const LegacyWorkaround legacyWorkaround);
|
||||||
|
|
||||||
static std::vector<std::string> sSupportedViews;
|
static std::vector<std::string> sSupportedViews;
|
||||||
|
static std::vector<std::string> sSupportedMediaTypes;
|
||||||
static std::vector<std::string> sLegacySupportedViews;
|
static std::vector<std::string> sLegacySupportedViews;
|
||||||
static std::vector<std::string> sLegacySupportedFeatures;
|
static std::vector<std::string> sLegacySupportedFeatures;
|
||||||
static std::vector<std::string> sLegacyProperties;
|
static std::vector<std::string> sLegacyProperties;
|
||||||
|
@ -298,6 +309,7 @@ private:
|
||||||
std::vector<std::string> mVariants;
|
std::vector<std::string> mVariants;
|
||||||
std::vector<std::string> mColorSchemes;
|
std::vector<std::string> mColorSchemes;
|
||||||
std::string mSelectedVariant;
|
std::string mSelectedVariant;
|
||||||
|
std::string mOverrideVariant;
|
||||||
std::string mSelectedColorScheme;
|
std::string mSelectedColorScheme;
|
||||||
std::string mSelectedAspectRatio;
|
std::string mSelectedAspectRatio;
|
||||||
bool mLegacyTheme;
|
bool mLegacyTheme;
|
||||||
|
|
Loading…
Reference in a new issue