Added variant trigger support.

This commit is contained in:
Leon Styhre 2023-01-04 19:01:41 +01:00
parent 549d78dfed
commit fa67018b72
5 changed files with 312 additions and 94 deletions

View file

@ -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

View file

@ -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();

View file

@ -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());

View file

@ -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);

View file

@ -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;