diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index f811de823..2af22e8e4 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -765,6 +765,9 @@ void FileData::countGames(std::pair& gameCount) void FileData::updateLastPlayedList() { + if (mUpdateListCallback) + mUpdateListCallback(); + if (!mUpdateChildrenLastPlayed) return; @@ -780,6 +783,9 @@ void FileData::updateLastPlayedList() void FileData::updateMostPlayedList() { + if (mUpdateListCallback) + mUpdateListCallback(); + if (!mUpdateChildrenMostPlayed) return; diff --git a/es-app/src/FileData.h b/es-app/src/FileData.h index b40659000..b04e3fa9e 100644 --- a/es-app/src/FileData.h +++ b/es-app/src/FileData.h @@ -14,6 +14,7 @@ #include "MetaData.h" #include "utils/FileSystemUtil.h" +#include #include class SystemData; @@ -62,6 +63,7 @@ public: const std::vector& getChildrenMostPlayed() const { return mChildrenMostPlayed; } void setUpdateChildrenLastPlayed(bool state) { mUpdateChildrenLastPlayed = state; } void setUpdateChildrenMostPlayed(bool state) { mUpdateChildrenMostPlayed = state; } + void setUpdateListCallback(const std::function& func) { mUpdateListCallback = func; } const bool getOnlyFoldersFlag() const { return mOnlyFolders; } const bool getHasFoldersFlag() const { return mHasFolders; } @@ -156,6 +158,7 @@ private: std::vector mFilteredChildren; std::vector mChildrenLastPlayed; std::vector mChildrenMostPlayed; + std::function mUpdateListCallback; // The pair includes all games, and favorite games. std::pair mGameCount; bool mOnlyFolders; diff --git a/es-app/src/views/GamelistView.cpp b/es-app/src/views/GamelistView.cpp index c52a34655..8dba275ca 100644 --- a/es-app/src/views/GamelistView.cpp +++ b/es-app/src/views/GamelistView.cpp @@ -107,7 +107,7 @@ void GamelistView::onThemeChanged(const std::shared_ptr& theme) mImageComponents.push_back(std::make_unique()); mImageComponents.back()->setDefaultZIndex(30.0f); mImageComponents.back()->applyTheme(theme, "gamelist", element.first, ALL); - if (mImageComponents.back()->getThemeImageType() != "") + if (mImageComponents.back()->getThemeImageTypes().size() != 0) mImageComponents.back()->setScrollHide(true); addChild(mImageComponents.back().get()); } @@ -116,7 +116,7 @@ void GamelistView::onThemeChanged(const std::shared_ptr& theme) mVideoComponents.back()->setDefaultZIndex(30.0f); addChild(mVideoComponents.back().get()); mVideoComponents.back()->applyTheme(theme, "gamelist", element.first, ALL); - if (mVideoComponents.back()->getThemeImageType() != "") + if (mVideoComponents.back()->getThemeImageTypes().size() != 0) mVideoComponents.back()->setScrollHide(true); } else if (element.second.type == "animation") { @@ -387,50 +387,11 @@ void GamelistView::updateInfoPanel() mRandomGame = CollectionSystemsManager::getInstance()->updateCollectionFolderMetadata( file->getSystem()); if (mRandomGame) { - for (auto& image : mImageComponents) { - if (image->getThemeImageType() == "image") - image->setImage(mRandomGame->getImagePath()); - else if (image->getThemeImageType() == "miximage") - image->setImage(mRandomGame->getMiximagePath()); - else if (image->getThemeImageType() == "marquee") - image->setImage(mRandomGame->getMarqueePath()); - else if (image->getThemeImageType() == "screenshot") - image->setImage(mRandomGame->getScreenshotPath()); - else if (image->getThemeImageType() == "titlescreen") - image->setImage(mRandomGame->getTitleScreenPath()); - else if (image->getThemeImageType() == "cover") - image->setImage(mRandomGame->getCoverPath()); - else if (image->getThemeImageType() == "backcover") - image->setImage(mRandomGame->getBackCoverPath()); - else if (image->getThemeImageType() == "3dbox") - image->setImage(mRandomGame->get3DBoxPath()); - else if (image->getThemeImageType() == "fanart") - image->setImage(mRandomGame->getFanArtPath()); - else if (image->getThemeImageType() == "thumbnail") - image->setImage(mRandomGame->getThumbnailPath()); - } + for (auto& image : mImageComponents) + setGameImage(mRandomGame, image.get()); for (auto& video : mVideoComponents) { - if (video->getThemeImageType() == "image") - video->setImage(mRandomGame->getImagePath()); - else if (video->getThemeImageType() == "miximage") - video->setImage(mRandomGame->getMiximagePath()); - else if (video->getThemeImageType() == "marquee") - video->setImage(mRandomGame->getMarqueePath()); - else if (video->getThemeImageType() == "screenshot") - video->setImage(mRandomGame->getScreenshotPath()); - else if (video->getThemeImageType() == "titlescreen") - video->setImage(mRandomGame->getTitleScreenPath()); - else if (video->getThemeImageType() == "cover") - video->setImage(mRandomGame->getCoverPath()); - else if (video->getThemeImageType() == "backcover") - video->setImage(mRandomGame->getBackCoverPath()); - else if (video->getThemeImageType() == "3dbox") - video->setImage(mRandomGame->get3DBoxPath()); - else if (video->getThemeImageType() == "fanart") - video->setImage(mRandomGame->getFanArtPath()); - else if (video->getThemeImageType() == "thumbnail") - video->setImage(mRandomGame->getThumbnailPath()); + setGameImage(mRandomGame, video.get()); // Always stop the video before setting a new video as it will otherwise // continue to play if it has the same path (i.e. it is the same physical @@ -447,7 +408,7 @@ void GamelistView::updateInfoPanel() } else { for (auto& image : mImageComponents) { - if (image->getThemeImageType() != "") + if (image->getThemeImageTypes().size() != 0) image->setImage(""); } @@ -465,51 +426,11 @@ void GamelistView::updateInfoPanel() } } else { - for (auto& image : mImageComponents) { - if (image->getThemeImageType() == "image") - image->setImage(file->getImagePath()); - else if (image->getThemeImageType() == "miximage") - image->setImage(file->getMiximagePath()); - else if (image->getThemeImageType() == "marquee") - image->setImage(file->getMarqueePath()); - else if (image->getThemeImageType() == "screenshot") - image->setImage(file->getScreenshotPath()); - else if (image->getThemeImageType() == "titlescreen") - image->setImage(file->getTitleScreenPath()); - else if (image->getThemeImageType() == "cover") - image->setImage(file->getCoverPath()); - else if (image->getThemeImageType() == "backcover") - image->setImage(file->getBackCoverPath()); - else if (image->getThemeImageType() == "3dbox") - image->setImage(file->get3DBoxPath()); - else if (image->getThemeImageType() == "fanart") - image->setImage(file->getFanArtPath()); - else if (image->getThemeImageType() == "thumbnail") - image->setImage(file->getThumbnailPath()); - } + for (auto& image : mImageComponents) + setGameImage(file, image.get()); for (auto& video : mVideoComponents) { - if (video->getThemeImageType() == "image") - video->setImage(file->getImagePath()); - else if (video->getThemeImageType() == "miximage") - video->setImage(file->getMiximagePath()); - else if (video->getThemeImageType() == "marquee") - video->setImage(file->getMarqueePath()); - else if (video->getThemeImageType() == "screenshot") - video->setImage(file->getScreenshotPath()); - else if (video->getThemeImageType() == "titlescreen") - video->setImage(file->getTitleScreenPath()); - else if (video->getThemeImageType() == "cover") - video->setImage(file->getCoverPath()); - else if (video->getThemeImageType() == "backcover") - video->setImage(file->getBackCoverPath()); - else if (video->getThemeImageType() == "3dbox") - video->setImage(file->get3DBoxPath()); - else if (video->getThemeImageType() == "fanart") - video->setImage(file->getFanArtPath()); - else if (video->getThemeImageType() == "thumbnail") - video->setImage(file->getThumbnailPath()); - + setGameImage(file, video.get()); video->onHide(); if (video->hasStaticVideo()) @@ -767,3 +688,83 @@ void GamelistView::updateInfoPanel() } } } + +void GamelistView::setGameImage(FileData* file, GuiComponent* comp) +{ + std::string path; + for (auto& imageType : comp->getThemeImageTypes()) { + if (imageType == "image") { + path = file->getImagePath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "miximage") { + path = file->getMiximagePath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "marquee") { + path = file->getMarqueePath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "screenshot") { + path = file->getScreenshotPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "titlescreen") { + path = file->getTitleScreenPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "cover") { + path = file->getCoverPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "backcover") { + path = file->getBackCoverPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "3dbox") { + path = file->get3DBoxPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "fanart") { + path = file->getFanArtPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + else if (imageType == "thumbnail") { + path = file->getThumbnailPath(); + if (path != "") { + comp->setImage(path); + break; + } + } + } + // This is needed so the default image is set if no game media was found. + if (path == "" && comp->getThemeImageTypes().size() > 0) + comp->setImage(""); +} diff --git a/es-app/src/views/GamelistView.h b/es-app/src/views/GamelistView.h index 340637ca1..fb9304070 100644 --- a/es-app/src/views/GamelistView.h +++ b/es-app/src/views/GamelistView.h @@ -57,6 +57,7 @@ public: private: void updateInfoPanel(); + void setGameImage(FileData* file, GuiComponent* comp); // Legacy (backward compatibility) functions. void legacyPopulateFields(); diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 1d2c24a15..2294e7ee2 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -62,8 +62,14 @@ SystemView::~SystemView() void SystemView::goToSystem(SystemData* system, bool animate) { mCarousel->setCursor(system); + + for (auto& selector : mSystemElements[mCarousel->getCursor()].gameSelectors) { + if (selector->getGameSelection() == GameSelectorComponent::GameSelection::RANDOM) + selector->setNeedsRefresh(); + } + + updateGameSelectors(); updateGameCount(); - updateGameSelector(); if (!animate) finishSystemAnimation(0); @@ -174,13 +180,20 @@ HelpStyle SystemView::getHelpStyle() void SystemView::onCursorChanged(const CursorState& /*state*/) { + int cursor {mCarousel->getCursor()}; + + for (auto& selector : mSystemElements[cursor].gameSelectors) { + if (selector->getGameSelection() == GameSelectorComponent::GameSelection::RANDOM) + selector->setNeedsRefresh(); + } + + updateGameSelectors(); updateHelpPrompts(); - updateGameSelector(); int scrollVelocity {mCarousel->getScrollingVelocity()}; float startPos {mCamOffset}; float posMax {static_cast(mCarousel->getNumEntries())}; - float target {static_cast(mCarousel->getCursor())}; + float target {static_cast(cursor)}; // Find the shortest path to the target. float endPos {target}; // Directly. @@ -337,10 +350,11 @@ void SystemView::populate() elements.fullName = it->getFullName(); for (auto& element : theme->getViewElements("system").elements) { if (element.second.type == "gameselector") { - elements.gameSelector = std::make_unique(it); - elements.gameSelector->applyTheme(theme, "system", element.first, - ThemeFlags::ALL); - elements.gameSelector->refreshGames(); + elements.gameSelectors.emplace_back( + std::make_unique(it)); + elements.gameSelectors.back()->applyTheme(theme, "system", element.first, + ThemeFlags::ALL); + elements.gameSelectors.back()->setNeedsRefresh(); } if (element.second.type == "carousel") { mCarousel->applyTheme(theme, "system", element.first, ThemeFlags::ALL); @@ -354,7 +368,7 @@ void SystemView::populate() elements.imageComponents.back()->setDefaultZIndex(30.0f); elements.imageComponents.back()->applyTheme(theme, "system", element.first, ThemeFlags::ALL); - if (elements.imageComponents.back()->getThemeImageType() != "") + if (elements.imageComponents.back()->getThemeImageTypes().size() != 0) elements.imageComponents.back()->setScrollHide(true); elements.children.emplace_back(elements.imageComponents.back().get()); } @@ -426,7 +440,7 @@ void SystemView::populate() } } - updateGameSelector(); + updateGameSelectors(); if (mCarousel->getNumEntries() == 0) { // Something is wrong, there is not a single system to show, check if UI mode is not full. @@ -497,39 +511,182 @@ void SystemView::updateGameCount() } } -void SystemView::updateGameSelector() +void SystemView::updateGameSelectors() { + if (mLegacyMode) + return; + int cursor {mCarousel->getCursor()}; - if (mSystemElements[cursor].gameSelector != nullptr) { - mSystemElements[mCarousel->getCursor()].gameSelector->refreshGames(); - std::vector games {mSystemElements[cursor].gameSelector->getGames()}; + if (mSystemElements[cursor].gameSelectors.size() == 0) + return; + + bool multipleSelectors {mSystemElements[cursor].gameSelectors.size() > 1}; + + for (auto& image : mSystemElements[cursor].imageComponents) { + if (image->getThemeImageTypes().size() == 0) + continue; + GameSelectorComponent* gameSelector {nullptr}; + if (multipleSelectors) { + const std::string& imageSelector {image->getThemeGameSelector()}; + if (imageSelector == "") { + gameSelector = mSystemElements[cursor].gameSelectors.front().get(); + LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector " + "elements defined but image element does not state which one to " + "use, so selecting first entry"; + } + else { + for (auto& selector : mSystemElements[cursor].gameSelectors) { + if (selector->getSelectorName() == imageSelector) + gameSelector = selector.get(); + } + if (gameSelector == nullptr) + gameSelector = mSystemElements[cursor].gameSelectors.front().get(); + } + } + else { + gameSelector = mSystemElements[cursor].gameSelectors.front().get(); + } + gameSelector->refreshGames(); + std::vector games {gameSelector->getGames()}; if (!games.empty()) { - if (!mLegacyMode) { - for (auto& image : mSystemElements[cursor].imageComponents) { - const std::string imageType {image->getThemeImageType()}; - if (imageType == "image") - image->setImage(games.front()->getImagePath()); - else if (image->getThemeImageType() == "miximage") - image->setImage(games.front()->getMiximagePath()); - else if (image->getThemeImageType() == "marquee") - image->setImage(games.front()->getMarqueePath()); - else if (image->getThemeImageType() == "screenshot") - image->setImage(games.front()->getScreenshotPath()); - else if (image->getThemeImageType() == "titlescreen") - image->setImage(games.front()->getTitleScreenPath()); - else if (image->getThemeImageType() == "cover") - image->setImage(games.front()->getCoverPath()); - else if (image->getThemeImageType() == "backcover") - image->setImage(games.front()->getBackCoverPath()); - else if (image->getThemeImageType() == "3dbox") - image->setImage(games.front()->get3DBoxPath()); - else if (image->getThemeImageType() == "fanart") - image->setImage(games.front()->getFanArtPath()); - else if (image->getThemeImageType() == "thumbnail") - image->setImage(games.front()->getThumbnailPath()); + std::string path; + for (auto& imageType : image->getThemeImageTypes()) { + if (imageType == "image") { + path = games.front()->getImagePath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "miximage") { + path = games.front()->getMiximagePath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "marquee") { + path = games.front()->getMarqueePath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "screenshot") { + path = games.front()->getScreenshotPath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "titlescreen") { + path = games.front()->getTitleScreenPath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "cover") { + path = games.front()->getCoverPath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "backcover") { + path = games.front()->getBackCoverPath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "3dbox") { + path = games.front()->get3DBoxPath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "fanart") { + path = games.front()->getFanArtPath(); + if (path != "") { + image->setImage(path); + break; + } + } + else if (imageType == "thumbnail") { + path = games.front()->getThumbnailPath(); + if (path != "") { + image->setImage(path); + break; + } } } + // This is needed so the default image is set if no game media was found. + if (path == "" && image->getThemeImageTypes().size() > 0) + image->setImage(""); + } + else { + image->setImage(""); + } + } + + for (auto& text : mSystemElements[cursor].textComponents) { + if (text->getThemeMetadata() == "") + continue; + GameSelectorComponent* gameSelector {nullptr}; + if (multipleSelectors) { + const std::string& textSelector {text->getThemeGameSelector()}; + if (textSelector == "") { + gameSelector = mSystemElements[cursor].gameSelectors.front().get(); + LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector " + "elements defined but text element does not state which one to " + "use, so selecting first entry"; + } + else { + for (auto& selector : mSystemElements[cursor].gameSelectors) { + if (selector->getSelectorName() == textSelector) + gameSelector = selector.get(); + } + if (gameSelector == nullptr) + gameSelector = mSystemElements[cursor].gameSelectors.front().get(); + } + } + else { + gameSelector = mSystemElements[cursor].gameSelectors.front().get(); + } + gameSelector->refreshGames(); + std::vector games {gameSelector->getGames()}; + if (!games.empty()) { + const std::string metadata {text->getThemeMetadata()}; + if (metadata == "name") + text->setValue(games.front()->metadata.get("name")); + if (metadata == "description") + text->setValue(games.front()->metadata.get("desc")); + if (metadata == "developer") + text->setValue(games.front()->metadata.get("developer")); + if (metadata == "publisher") + text->setValue(games.front()->metadata.get("publisher")); + if (metadata == "genre") + text->setValue(games.front()->metadata.get("genre")); + if (metadata == "players") + text->setValue(games.front()->metadata.get("players")); + if (metadata == "favorite") + text->setValue(games.front()->metadata.get("favorite") == "true" ? "yes" : "no"); + if (metadata == "completed") + text->setValue(games.front()->metadata.get("completed") == "true" ? "yes" : "no"); + if (metadata == "kidgame") + text->setValue(games.front()->metadata.get("kidgame") == "true" ? "yes" : "no"); + if (metadata == "broken") + text->setValue(games.front()->metadata.get("broken") == "true" ? "yes" : "no"); + if (metadata == "playcount") + text->setValue(games.front()->metadata.get("playcount")); + if (metadata == "altemulator") + text->setValue(games.front()->metadata.get("altemulator")); + } + else { + text->setValue(""); } } } diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index 7f3d88ee5..f16da5f70 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -28,7 +28,7 @@ class SystemData; struct SystemViewElements { std::string name; std::string fullName; - std::unique_ptr gameSelector; + std::vector> gameSelectors; std::vector legacyExtras; std::vector children; @@ -77,7 +77,7 @@ protected: private: void populate(); void updateGameCount(); - void updateGameSelector(); + void updateGameSelectors(); void legacyApplyTheme(const std::shared_ptr& theme); void renderElements(const glm::mat4& parentTrans, bool abovePrimary); diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index f83d36678..f0051ea7f 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -364,6 +364,9 @@ void GuiComponent::applyTheme(const std::shared_ptr& theme, mThemeOpacity = 0.0f; else setVisible(true); + + if (properties && elem->has("gameselector")) + mThemeGameSelector = elem->get("gameselector"); } void GuiComponent::updateHelpPrompts() diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 544c5d2d5..8cb03502e 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -207,16 +207,18 @@ public: virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; } virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; } + virtual void setImage(const std::string& path, bool tile = false) {} + // These functions are used to enable and disable options in menus, i.e. switches and similar. virtual bool getEnabled() { return mEnabled; } virtual void setEnabled(bool state) { mEnabled = state; } - const std::string getThemeSystemdata() { return mThemeSystemdata; } + const std::string& getThemeSystemdata() { return mThemeSystemdata; } void setThemeSystemdata(const std::string& text) { mThemeSystemdata = text; } - const std::string getThemeMetadata() { return mThemeMetadata; } + const std::string& getThemeMetadata() { return mThemeMetadata; } void setThemeMetadata(const std::string& text) { mThemeMetadata = text; } - const std::string getThemeImageType() { return mThemeImageType; } - void setThemeImageType(const std::string& text) { mThemeImageType = text; } + const std::vector& getThemeImageTypes() { return mThemeImageTypes; } + const std::string& getThemeGameSelector() { return mThemeGameSelector; } virtual std::shared_ptr getFont() const { return nullptr; } @@ -279,9 +281,10 @@ protected: GuiComponent* mParent; std::vector mChildren; + std::vector mThemeImageTypes; std::string mThemeSystemdata; std::string mThemeMetadata; - std::string mThemeImageType; + std::string mThemeGameSelector; unsigned int mColor; float mSaturation; diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index d383deb92..1a1501931 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -89,6 +89,7 @@ std::map> {"path", PATH}, {"default", PATH}, {"imageType", STRING}, + {"gameselector", STRING}, {"tile", BOOLEAN}, {"interpolation", STRING}, {"color", COLOR}, @@ -161,6 +162,7 @@ std::map> {"text", STRING}, {"systemdata", STRING}, {"metadata", STRING}, + {"gameselector", STRING}, {"container", BOOLEAN}, {"containerScrollSpeed", FLOAT}, {"containerStartDelay", FLOAT}, @@ -281,7 +283,7 @@ std::map> {"zIndex", FLOAT}}}, {"gameselector", {{"selection", STRING}, - {"count", UNSIGNED_INTEGER}}}, + {"gameCount", UNSIGNED_INTEGER}}}, {"helpsystem", {{"pos", NORMALIZED_PAIR}, {"origin", NORMALIZED_PAIR}, diff --git a/es-core/src/components/CarouselComponent.cpp b/es-core/src/components/CarouselComponent.cpp index a1ff64b0f..6226722c8 100644 --- a/es-core/src/components/CarouselComponent.cpp +++ b/es-core/src/components/CarouselComponent.cpp @@ -311,6 +311,7 @@ void CarouselComponent::applyTheme(const std::shared_ptr& theme, mCarouselColor = 0xFFFFFFD8; mCarouselColorEnd = 0xFFFFFFD8; mDefaultZIndex = 50.0f; + mText = ""; if (!elem) return; diff --git a/es-core/src/components/GameSelectorComponent.h b/es-core/src/components/GameSelectorComponent.h index 42fae0fe3..c828fdf17 100644 --- a/es-core/src/components/GameSelectorComponent.h +++ b/es-core/src/components/GameSelectorComponent.h @@ -19,15 +19,30 @@ public: GameSelectorComponent(SystemData* system) : mSystem {system} , mGameSelection {GameSelection::RANDOM} + , mNeedsRefresh {false} , mGameCount {1} { + mSystem->getRootFolder()->setUpdateListCallback([&]() { mNeedsRefresh = true; }); } + enum class GameSelection { + RANDOM, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). + LAST_PLAYED, + MOST_PLAYED + }; + const std::vector& getGames() const { return mGames; } + void setNeedsRefresh() { mNeedsRefresh = true; } + const bool getNeedsRefresh() { return mNeedsRefresh; } + const GameSelection getGameSelection() const { return mGameSelection; } + const std::string& getSelectorName() const { return mSelectorName; } void refreshGames() { + if (!mNeedsRefresh) + return; mGames.clear(); + mNeedsRefresh = false; bool isKidMode {(Settings::getInstance()->getString("UIMode") == "kid" || Settings::getInstance()->getBool("ForceKid"))}; @@ -82,6 +97,10 @@ public: if (!elem) return; + // Remove the leading "gameselector_" part of the element string in order to directly + // match with the optional "gameselector" property used in other elements. + mSelectorName = element.substr(13, std::string::npos); + if (elem->has("selection")) { const std::string selection {elem->get("selection")}; if (selection == "random") { @@ -105,21 +124,17 @@ public: } } - if (elem->has("count")) - mGameCount = glm::clamp(static_cast(elem->get("count")), 1, 30); + if (elem->has("gameCount")) + mGameCount = glm::clamp(static_cast(elem->get("gameCount")), 1, 30); } private: - enum class GameSelection { - RANDOM, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). - LAST_PLAYED, - MOST_PLAYED - }; - SystemData* mSystem; std::vector mGames; + std::string mSelectorName; GameSelection mGameSelection; + bool mNeedsRefresh; int mGameCount; }; diff --git a/es-core/src/components/GridTileComponent.cpp b/es-core/src/components/GridTileComponent.cpp index 7f17650bb..c5a74eec2 100644 --- a/es-core/src/components/GridTileComponent.cpp +++ b/es-core/src/components/GridTileComponent.cpp @@ -148,7 +148,7 @@ bool GridTileComponent::isSelected() const return mSelected; } -void GridTileComponent::setImage(const std::string& path) +void GridTileComponent::setImageOLD(const std::string& path) { mImage->setImage(path); @@ -156,7 +156,7 @@ void GridTileComponent::setImage(const std::string& path) resize(); } -void GridTileComponent::setImage(const std::shared_ptr& texture) +void GridTileComponent::setImageOLD(const std::shared_ptr& texture) { mImage->setImage(texture); diff --git a/es-core/src/components/GridTileComponent.h b/es-core/src/components/GridTileComponent.h index f0b0b2df6..1347b5006 100644 --- a/es-core/src/components/GridTileComponent.h +++ b/es-core/src/components/GridTileComponent.h @@ -39,10 +39,10 @@ public: glm::vec2 getSelectedTileSize() const; bool isSelected() const; - void reset() { setImage(""); } + void reset() { setImageOLD(""); } - void setImage(const std::string& path); - void setImage(const std::shared_ptr& texture); + void setImageOLD(const std::string& path); + void setImageOLD(const std::shared_ptr& texture); void setSelected(bool selected, bool allowAnimation = true, glm::vec3* pPosition = nullptr, diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index cc2ced6c7..155408cc3 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -14,6 +14,7 @@ #include "Window.h" #include "resources/TextureResource.h" #include "utils/CImgUtil.h" +#include "utils/StringUtil.h" glm::ivec2 ImageComponent::getTextureSize() const { @@ -522,8 +523,15 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, setImage(elem->get("path"), tile); } - if (properties & METADATA && elem->has("imageType")) - mThemeImageType = elem->get("imageType"); + if (properties && elem->has("imageType")) { + std::string imageTypes {elem->get("imageType")}; + for (auto& character : imageTypes) { + if (std::isspace(character)) + character = ','; + } + imageTypes = Utils::String::replace(imageTypes, ",,", ","); + mThemeImageTypes = Utils::String::delimitedStringToVector(imageTypes, ","); + } if (properties & COLOR) { if (elem->has("color")) diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 0a8c5c242..e06d41924 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -24,7 +24,7 @@ public: // Loads the image at the given filepath. Will tile if tile is true (retrieves texture // as tiling, creates vertices accordingly). - void setImage(const std::string& path, bool tile = false); + void setImage(const std::string& path, bool tile = false) override; // Loads an image from memory. void setImage(const char* data, size_t length, bool tile = false); // Use an already existing texture. diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 29c7d6768..0844a9fab 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -12,6 +12,7 @@ #include "Window.h" #include "resources/ResourceManager.h" #include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" #include @@ -80,12 +81,17 @@ bool VideoComponent::setVideo(std::string path) void VideoComponent::setImage(const std::string& path, bool tile) { + std::string imagePath {path}; + + if (imagePath == "") + imagePath = mDefaultImagePath; + // Check that the image has changed. - if (path == mStaticImagePath) + if (imagePath == mStaticImagePath) return; - mStaticImage.setImage(path, tile); - mStaticImagePath = path; + mStaticImage.setImage(imagePath, tile); + mStaticImagePath = imagePath; } void VideoComponent::onShow() @@ -268,6 +274,7 @@ void VideoComponent::applyTheme(const std::shared_ptr& theme, if (elem->has("defaultImage")) { mStaticImage.setDefaultImage(elem->get("defaultImage")); mStaticImage.setImage(mStaticImagePath); + mDefaultImagePath = elem->get("defaultImage"); } if (elem->has("path")) @@ -287,8 +294,15 @@ void VideoComponent::applyTheme(const std::shared_ptr& theme, else if (elem->has("showSnapshotDelay")) mConfig.showSnapshotDelay = elem->get("showSnapshotDelay"); - if (properties & METADATA && elem->has("imageType")) - mThemeImageType = elem->get("imageType"); + if (properties && elem->has("imageType")) { + std::string imageTypes {elem->get("imageType")}; + for (auto& character : imageTypes) { + if (std::isspace(character)) + character = ','; + } + imageTypes = Utils::String::replace(imageTypes, ",,", ","); + mThemeImageTypes = Utils::String::delimitedStringToVector(imageTypes, ","); + } if (elem->has("pillarboxes")) mDrawPillarboxes = elem->get("pillarboxes"); diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index 89ad4b94f..cdefc1f80 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -40,7 +40,7 @@ public: // Configures the component to show the static video. void setStaticVideo() { setVideo(mConfig.staticVideoPath); } // Loads a static image that is displayed if the video cannot be played. - void setImage(const std::string& path, bool tile = false); + void setImage(const std::string& path, bool tile = false) override; // Sets whether we're in media viewer mode. void setMediaViewerMode(bool isMediaViewer) { mMediaViewerMode = isMediaViewer; } // Sets whether we're in screensaver mode. @@ -125,6 +125,7 @@ protected: glm::vec2 mVideoAreaSize; std::shared_ptr mTexture; std::string mStaticImagePath; + std::string mDefaultImagePath; std::string mVideoPath; std::string mPlayingVideoPath;