From 596bc5e8af626cfea4cbc919c8070b2e81c8aba5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 10 Sep 2022 11:55:35 +0200 Subject: [PATCH] Removed a lot of deprecated theme engine code from the legacy engine. --- .../legacygamelists/BasicGamelistView.cpp | 356 --------- .../views/legacygamelists/BasicGamelistView.h | 60 -- .../legacygamelists/DetailedGamelistView.cpp | 528 ------------- .../legacygamelists/DetailedGamelistView.h | 73 -- .../legacygamelists/GridGamelistView.cpp | 743 ------------------ .../views/legacygamelists/GridGamelistView.h | 108 --- .../views/legacygamelists/IGamelistView.cpp | 77 -- .../src/views/legacygamelists/IGamelistView.h | 66 -- .../legacygamelists/ISimpleGamelistView.cpp | 583 -------------- .../legacygamelists/ISimpleGamelistView.h | 78 -- .../legacygamelists/VideoGamelistView.cpp | 561 ------------- .../views/legacygamelists/VideoGamelistView.h | 78 -- es-core/src/components/GridTileComponent.cpp | 322 -------- es-core/src/components/GridTileComponent.h | 79 -- es-core/src/components/ImageGridComponent.h | 726 ----------------- 15 files changed, 4438 deletions(-) delete mode 100644 es-app/src/views/legacygamelists/BasicGamelistView.cpp delete mode 100644 es-app/src/views/legacygamelists/BasicGamelistView.h delete mode 100644 es-app/src/views/legacygamelists/DetailedGamelistView.cpp delete mode 100644 es-app/src/views/legacygamelists/DetailedGamelistView.h delete mode 100644 es-app/src/views/legacygamelists/GridGamelistView.cpp delete mode 100644 es-app/src/views/legacygamelists/GridGamelistView.h delete mode 100644 es-app/src/views/legacygamelists/IGamelistView.cpp delete mode 100644 es-app/src/views/legacygamelists/IGamelistView.h delete mode 100644 es-app/src/views/legacygamelists/ISimpleGamelistView.cpp delete mode 100644 es-app/src/views/legacygamelists/ISimpleGamelistView.h delete mode 100644 es-app/src/views/legacygamelists/VideoGamelistView.cpp delete mode 100644 es-app/src/views/legacygamelists/VideoGamelistView.h delete mode 100644 es-core/src/components/GridTileComponent.cpp delete mode 100644 es-core/src/components/GridTileComponent.h delete mode 100644 es-core/src/components/ImageGridComponent.h diff --git a/es-app/src/views/legacygamelists/BasicGamelistView.cpp b/es-app/src/views/legacygamelists/BasicGamelistView.cpp deleted file mode 100644 index fd1307885..000000000 --- a/es-app/src/views/legacygamelists/BasicGamelistView.cpp +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// BasicGamelistView.cpp -// -// Interface that defines a GamelistView of the type 'Basic'. -// - -#include "views/gamelist/BasicGamelistView.h" - -#include "CollectionSystemsManager.h" -#include "Settings.h" -#include "SystemData.h" -#include "UIModeController.h" -#include "utils/FileSystemUtil.h" -#include "views/ViewController.h" - -BasicGamelistView::BasicGamelistView(Window* window, FileData* root) - : ISimpleGamelistView {window, root} - , mList {window} -{ - mList.setSize(mSize.x, mSize.y * 0.8f); - mList.setPosition(0.0f, mSize.y * 0.2f); - mList.setDefaultZIndex(20.0f); - addChild(&mList); - - populateList(root->getChildrenListToDisplay(), root); -} - -void BasicGamelistView::onThemeChanged(const std::shared_ptr& theme) -{ - ISimpleGamelistView::onThemeChanged(theme); - using namespace ThemeFlags; - mList.applyTheme(theme, getName(), "gamelist", ALL); - - sortChildren(); -} - -void BasicGamelistView::onFileChanged(FileData* file, bool reloadGamelist) -{ - if (reloadGamelist) { - // Might switch to a detailed view. - ViewController::getInstance()->reloadGamelistView(this); - return; - } - - ISimpleGamelistView::onFileChanged(file, reloadGamelist); -} - -void BasicGamelistView::populateList(const std::vector& files, FileData* firstEntry) -{ - mFirstGameEntry = nullptr; - bool favoriteStar = true; - bool isEditing = false; - std::string editingCollection; - std::string inCollectionPrefix; - - if (CollectionSystemsManager::getInstance()->isEditing()) { - editingCollection = CollectionSystemsManager::getInstance()->getEditingCollection(); - isEditing = true; - } - - // Read the settings that control whether a unicode star character should be added - // as a prefix to the game name. - if (files.size() > 0) { - if (files.front()->getSystem()->isCustomCollection()) - favoriteStar = Settings::getInstance()->getBool("FavStarCustom"); - else - favoriteStar = Settings::getInstance()->getBool("FavoritesStar"); - } - - mList.clear(); - mHeaderText.setText(mRoot->getSystem()->getFullName()); - if (files.size() > 0) { - for (auto it = files.cbegin(); it != files.cend(); ++it) { - if (!mFirstGameEntry && (*it)->getType() == GAME) - mFirstGameEntry = (*it); - // Add a leading tick mark icon to the game name if it's part of the custom collection - // currently being edited. - if (isEditing && (*it)->getType() == GAME) { - if (CollectionSystemsManager::getInstance()->inCustomCollection(editingCollection, - (*it))) { - if (Settings::getInstance()->getBool("SpecialCharsASCII")) - inCollectionPrefix = "! "; - else - inCollectionPrefix = ViewController::TICKMARK_CHAR + " "; - } - else { - inCollectionPrefix = ""; - } - } - - if ((*it)->getFavorite() && favoriteStar && - mRoot->getSystem()->getName() != "favorites") { - if (Settings::getInstance()->getBool("SpecialCharsASCII")) - mList.add(inCollectionPrefix + "* " + (*it)->getName(), *it, - ((*it)->getType() == FOLDER)); - else - mList.add(inCollectionPrefix + ViewController::FAVORITE_CHAR + " " + - (*it)->getName(), - *it, ((*it)->getType() == FOLDER)); - } - else if ((*it)->getType() == FOLDER && mRoot->getSystem()->getName() != "collections") { - if (Settings::getInstance()->getBool("SpecialCharsASCII")) - mList.add("# " + (*it)->getName(), *it, true); - else - mList.add(ViewController::FOLDER_CHAR + " " + (*it)->getName(), *it, true); - } - else { - mList.add(inCollectionPrefix + (*it)->getName(), *it, ((*it)->getType() == FOLDER)); - } - } - } - else { - addPlaceholder(firstEntry); - } - - generateGamelistInfo(getCursor(), firstEntry); - generateFirstLetterIndex(files); -} - -void BasicGamelistView::setCursor(FileData* cursor) -{ - if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) { - populateList(cursor->getParent()->getChildrenListToDisplay(), cursor->getParent()); - mList.setCursor(cursor); - - // Update our cursor stack in case our cursor just got set to some folder - // we weren't in before. - if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) { - std::stack tmp; - FileData* ptr = cursor->getParent(); - - while (ptr && ptr != mRoot) { - tmp.push(ptr); - ptr = ptr->getParent(); - } - - // Flip the stack and put it in mCursorStack. - mCursorStack = std::stack(); - while (!tmp.empty()) { - mCursorStack.push(tmp.top()); - tmp.pop(); - } - } - } -} - -void BasicGamelistView::addPlaceholder(FileData* firstEntry) -{ - // Empty list, add a placeholder. - FileData* placeholder; - - if (firstEntry && firstEntry->getSystem()->isGroupedCustomCollection()) - placeholder = firstEntry->getSystem()->getPlaceholder(); - else - placeholder = this->mRoot->getSystem()->getPlaceholder(); - - mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER)); -} - -void BasicGamelistView::launch(FileData* game) -{ - // This triggers ViewController to launch the game. - ViewController::getInstance()->triggerGameLaunch(game); -} - -void BasicGamelistView::remove(FileData* game, bool deleteFile) -{ - // Delete the game file on the filesystem. - if (deleteFile) - Utils::FileSystem::removeFile(game->getPath()); - - FileData* parent = game->getParent(); - // Select next element in list, or previous if none. - if (getCursor() == game) { - std::vector siblings = parent->getChildrenListToDisplay(); - auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game); - unsigned int gamePos = static_cast(std::distance(siblings.cbegin(), gameIter)); - if (gameIter != siblings.cend()) { - if ((gamePos + 1) < siblings.size()) - setCursor(siblings.at(gamePos + 1)); - else if (gamePos > 1) - setCursor(siblings.at(gamePos - 1)); - } - } - mList.remove(game); - - if (mList.size() == 0) - addPlaceholder(); - - // If a game has been deleted, immediately remove the entry from gamelist.xml - // regardless of the value of the setting SaveGamelistsMode. - game->setDeletionFlag(true); - parent->getSystem()->writeMetaData(); - - // Remove before repopulating (removes from parent), then update the view. - delete game; - - if (deleteFile) { - parent->sort(parent->getSortTypeFromString(parent->getSortTypeString()), - Settings::getInstance()->getBool("FavoritesFirst")); - onFileChanged(parent, false); - } -} - -void BasicGamelistView::removeMedia(FileData* game) -{ - std::string systemMediaDir = FileData::getMediaDirectory() + game->getSystem()->getName(); - std::string mediaType; - std::string path; - - // Stop the video player, especially important on Windows as the file would otherwise be locked. - onStopVideo(); - - // If there are no media files left in the directory after the deletion, then remove - // the directory too. Remove any empty parent directories as well. - auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType, - std::string path) { - std::string parentPath = Utils::FileSystem::getParent(path); - while (parentPath != systemMediaDir + "/" + mediaType) { - if (Utils::FileSystem::getDirContent(parentPath).size() == 0) { - Utils::FileSystem::removeDirectory(parentPath); - parentPath = Utils::FileSystem::getParent(parentPath); - } - else { - break; - } - } - }; - - // Remove all game media files on the filesystem. - while (Utils::FileSystem::exists(game->getVideoPath())) { - mediaType = "videos"; - path = game->getVideoPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getMiximagePath())) { - mediaType = "miximages"; - path = game->getMiximagePath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getScreenshotPath())) { - mediaType = "screenshots"; - path = game->getScreenshotPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getTitleScreenPath())) { - mediaType = "titlescreens"; - path = game->getTitleScreenPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getCoverPath())) { - mediaType = "covers"; - path = game->getCoverPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getBackCoverPath())) { - mediaType = "backcovers"; - path = game->getBackCoverPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getFanArtPath())) { - mediaType = "fanart"; - path = game->getFanArtPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getMarqueePath())) { - mediaType = "marquees"; - path = game->getMarqueePath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->get3DBoxPath())) { - mediaType = "3dboxes"; - path = game->get3DBoxPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getPhysicalMediaPath())) { - mediaType = "physicalmedia"; - path = game->getPhysicalMediaPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getThumbnailPath())) { - mediaType = "thumbnails"; - path = game->getThumbnailPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } -} - -std::vector BasicGamelistView::getHelpPrompts() -{ - std::vector prompts; - - if (Settings::getInstance()->getBool("QuickSystemSelect") && - SystemData::sSystemVector.size() > 1) - prompts.push_back(HelpPrompt("left/right", "system")); - - if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST) - prompts.push_back(HelpPrompt("a", "enter")); - else - prompts.push_back(HelpPrompt("a", "launch")); - - prompts.push_back(HelpPrompt("b", "back")); - prompts.push_back(HelpPrompt("x", "view media")); - - if (!UIModeController::getInstance()->isUIModeKid()) - prompts.push_back(HelpPrompt("back", "options")); - if (mRoot->getSystem()->isGameSystem() && Settings::getInstance()->getBool("RandomAddButton")) - prompts.push_back(HelpPrompt("thumbstickclick", "random")); - - if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && - !CollectionSystemsManager::getInstance()->isEditing() && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST && - ViewController::getInstance()->getState().viewstyle != ViewController::BASIC) { - prompts.push_back(HelpPrompt("y", "jump to game")); - } - else if (mRoot->getSystem()->isGameSystem() && - (mRoot->getSystem()->getThemeFolder() != "custom-collections" || - !mCursorStack.empty()) && - !UIModeController::getInstance()->isUIModeKid() && - !UIModeController::getInstance()->isUIModeKiosk() && - (Settings::getInstance()->getBool("FavoritesAddButton") || - CollectionSystemsManager::getInstance()->isEditing())) { - std::string prompt = CollectionSystemsManager::getInstance()->getEditingCollection(); - prompts.push_back(HelpPrompt("y", prompt)); - } - else if (mRoot->getSystem()->isGameSystem() && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - CollectionSystemsManager::getInstance()->isEditing()) { - std::string prompt = CollectionSystemsManager::getInstance()->getEditingCollection(); - prompts.push_back(HelpPrompt("y", prompt)); - } - return prompts; -} diff --git a/es-app/src/views/legacygamelists/BasicGamelistView.h b/es-app/src/views/legacygamelists/BasicGamelistView.h deleted file mode 100644 index 26547a0ea..000000000 --- a/es-app/src/views/legacygamelists/BasicGamelistView.h +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// BasicGamelistView.h -// -// Interface that defines a GamelistView of the type 'basic'. -// - -#ifndef ES_APP_VIEWS_GAMELIST_BASIC_GAMELIST_VIEW_H -#define ES_APP_VIEWS_GAMELIST_BASIC_GAMELIST_VIEW_H - -#include "components/TextListComponent.h" -#include "views/gamelist/ISimpleGamelistView.h" - -class BasicGamelistView : public ISimpleGamelistView -{ -public: - BasicGamelistView(Window* window, FileData* root); - - // Called when a FileData* is added, has its metadata changed, or is removed. - void onFileChanged(FileData* file, bool reloadGamelist) override; - - void onThemeChanged(const std::shared_ptr& theme) override; - void setCursor(FileData* cursor) override; - - FileData* getCursor() override { return mList.getSelected(); } - FileData* getNextEntry() override { return mList.getNext(); } - FileData* getPreviousEntry() override { return mList.getPrevious(); } - FileData* getFirstEntry() override { return mList.getFirst(); } - FileData* getLastEntry() override { return mList.getLast(); } - FileData* getFirstGameEntry() override { return mFirstGameEntry; } - - std::string getName() const override { return "basic"; } - - std::vector getHelpPrompts() override; - - bool isListScrolling() override { return mList.isScrolling(); } - void stopListScrolling() override { mList.stopScrolling(); } - - const std::vector& getFirstLetterIndex() override { return mFirstLetterIndex; } - - void addPlaceholder(FileData* firstEntry = nullptr) override; - void launch(FileData* game) override; - -protected: - std::string getQuickSystemSelectRightButton() override { return "right"; } - std::string getQuickSystemSelectLeftButton() override { return "left"; } - void populateList(const std::vector& files, FileData* firstEntry) override; - void remove(FileData* game, bool deleteFile) override; - void removeMedia(FileData* game) override; - - TextListComponent mList; - // Points to the first game in the list, i.e. the first entry which is of the type 'GAME'. - FileData* mFirstGameEntry; - - std::string FAVORITE_CHAR; - std::string FOLDER_CHAR; -}; - -#endif // ES_APP_VIEWS_GAMELIST_BASIC_GAMELIST_VIEW_H diff --git a/es-app/src/views/legacygamelists/DetailedGamelistView.cpp b/es-app/src/views/legacygamelists/DetailedGamelistView.cpp deleted file mode 100644 index 88be4e63a..000000000 --- a/es-app/src/views/legacygamelists/DetailedGamelistView.cpp +++ /dev/null @@ -1,528 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// DetailedGamelistView.cpp -// -// Interface that defines a GamelistView of the type 'detailed'. -// - -#include "views/gamelist/DetailedGamelistView.h" - -#include "CollectionSystemsManager.h" -#include "SystemData.h" -#include "animations/LambdaAnimation.h" -#include "views/ViewController.h" - -#define FADE_IN_START_OPACITY 0.5f -#define FADE_IN_TIME 650 - -DetailedGamelistView::DetailedGamelistView(Window* window, FileData* root) - : BasicGamelistView(window, root) - , mThumbnail(window) - , mMarquee(window) - , mImage(window) - , mLblRating(window) - , mLblReleaseDate(window) - , mLblDeveloper(window) - , mLblPublisher(window) - , mLblGenre(window) - , mLblPlayers(window) - , mLblLastPlayed(window) - , mLblPlayCount(window) - , mRating(window) - , mReleaseDate(window) - , mDeveloper(window) - , mPublisher(window) - , mGenre(window) - , mPlayers(window) - , mLastPlayed(window) - , mPlayCount(window) - , mName(window) - , mBadges(window) - , mDescContainer(window) - , mDescription(window) - , mGamelistInfo(window) - , mLastUpdated(nullptr) -{ - const float padding = 0.01f; - - mList.setPosition(mSize.x * (0.50f + padding), mList.getPosition().y); - mList.setSize(mSize.x * (0.50f - padding), mList.getSize().y); - mList.setAlignment(TextListComponent::ALIGN_LEFT); - mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - - // Thumbnail. - mThumbnail.setOrigin(0.5f, 0.5f); - mThumbnail.setPosition(2.0f, 2.0f); - mThumbnail.setVisible(false); - mThumbnail.setMaxSize(mSize.x * (0.25f - 2.0f * padding), mSize.y * 0.10f); - mThumbnail.setDefaultZIndex(25.0f); - addChild(&mThumbnail); - - // Marquee. - mMarquee.setOrigin(0.5f, 0.5f); - // Default to off the screen. - mMarquee.setPosition(2.0f, 2.0f); - mMarquee.setVisible(false); - mMarquee.setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f); - mMarquee.setDefaultZIndex(35.0f); - addChild(&mMarquee); - - // Image. - mImage.setOrigin(0.5f, 0.5f); - mImage.setPosition(mSize.x * 0.25f, mList.getPosition().y + mSize.y * 0.2125f); - mImage.setMaxSize(mSize.x * (0.50f - 2.0f * padding), mSize.y * 0.4f); - mImage.setDefaultZIndex(30.0f); - addChild(&mImage); - - // Metadata labels + values. - mLblRating.setText("Rating: ", false); - addChild(&mLblRating); - addChild(&mRating); - mLblReleaseDate.setText("Released: ", false); - addChild(&mLblReleaseDate); - addChild(&mReleaseDate); - mLblDeveloper.setText("Developer: ", false); - addChild(&mLblDeveloper); - addChild(&mDeveloper); - mLblPublisher.setText("Publisher: ", false); - addChild(&mLblPublisher); - addChild(&mPublisher); - mLblGenre.setText("Genre: ", false); - addChild(&mLblGenre); - addChild(&mGenre); - mLblPlayers.setText("Players: ", false); - addChild(&mLblPlayers); - addChild(&mPlayers); - mLblLastPlayed.setText("Last played: ", false); - addChild(&mLblLastPlayed); - mLastPlayed.setDisplayRelative(true); - addChild(&mLastPlayed); - mLblPlayCount.setText("Times played: ", false); - addChild(&mLblPlayCount); - addChild(&mPlayCount); - - // Badges. - addChild(&mBadges); - mBadges.setOrigin(0.5f, 0.5f); - mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f); - mBadges.setSize(mSize.x * 0.15f, mSize.y * 0.2f); - mBadges.setDefaultZIndex(50.0f); - - mName.setPosition(mSize.x, mSize.y); - mName.setDefaultZIndex(40.0f); - mName.setColor(0xAAAAAAFF); - mName.setFont(Font::get(FONT_SIZE_MEDIUM)); - mName.setHorizontalAlignment(ALIGN_CENTER); - addChild(&mName); - - mDescContainer.setPosition(mSize.x * padding, mSize.y * 0.65f); - mDescContainer.setSize(mSize.x * (0.50f - 2.0f * padding), - mSize.y - mDescContainer.getPosition().y); - mDescContainer.setAutoScroll(true); - mDescContainer.setDefaultZIndex(40.0f); - addChild(&mDescContainer); - - mDescription.setFont(Font::get(FONT_SIZE_SMALL)); - mDescription.setSize(mDescContainer.getSize().x, 0.0f); - mDescContainer.addChild(&mDescription); - - mGamelistInfo.setOrigin(0.5f, 0.5f); - mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL)); - mGamelistInfo.setDefaultZIndex(50.0f); - mGamelistInfo.setVisible(true); - addChild(&mGamelistInfo); - - initMDLabels(); - initMDValues(); -} - -void DetailedGamelistView::onThemeChanged(const std::shared_ptr& theme) -{ - BasicGamelistView::onThemeChanged(theme); - - using namespace ThemeFlags; - mThumbnail.applyTheme(theme, getName(), "md_thumbnail", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mMarquee.applyTheme(theme, getName(), "md_marquee", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mImage.applyTheme(theme, getName(), "md_image", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mName.applyTheme(theme, getName(), "md_name", ALL); - mBadges.applyTheme(theme, getName(), "md_badges", ALL); - - initMDLabels(); - std::vector labels = getMDLabels(); - assert(labels.size() == 8); - std::vector lblElements = { - "md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", - "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"}; - - for (unsigned int i = 0; i < labels.size(); ++i) - labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); - - initMDValues(); - std::vector values = getMDValues(); - assert(values.size() == 8); - std::vector valElements = {"md_rating", "md_releasedate", "md_developer", - "md_publisher", "md_genre", "md_players", - "md_lastplayed", "md_playcount"}; - - for (unsigned int i = 0; i < values.size(); ++i) - values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); - - mDescContainer.applyTheme(theme, getName(), "md_description", - POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); - mDescription.setSize(mDescContainer.getSize().x, 0.0f); - mDescription.applyTheme( - theme, getName(), "md_description", - ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); - - mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); - // If there is no position defined in the theme for gamelistInfo, then hide it. - if (mGamelistInfo.getPosition() == glm::vec3 {}) - mGamelistInfo.setVisible(false); - else - mGamelistInfo.setVisible(true); - - sortChildren(); -} - -void DetailedGamelistView::initMDLabels() -{ - std::vector components = getMDLabels(); - - const unsigned int colCount = 2; - const unsigned int rowCount = static_cast(components.size() / 2); - - glm::vec3 start {mSize.x * 0.01f, mSize.y * 0.625f, 0.0f}; - - const float colSize = (mSize.x * 0.48f) / colCount; - const float rowPadding = 0.01f * mSize.y; - - for (unsigned int i = 0; i < components.size(); ++i) { - const unsigned int row = i % rowCount; - glm::vec3 pos {}; - if (row == 0) { - pos = start + glm::vec3 {colSize * (i / rowCount), 0.0f, 0.0f}; - } - else { - // Work from the last component. - GuiComponent* lc = components[i - 1]; - pos = lc->getPosition() + glm::vec3 {0.0f, lc->getSize().y + rowPadding, 0.0f}; - } - - components[i]->setFont(Font::get(FONT_SIZE_SMALL)); - components[i]->setPosition(pos); - components[i]->setDefaultZIndex(40.0f); - } -} - -void DetailedGamelistView::initMDValues() -{ - std::vector labels = getMDLabels(); - std::vector values = getMDValues(); - - std::shared_ptr defaultFont = Font::get(FONT_SIZE_SMALL); - mRating.setSize(defaultFont->getHeight() * 5.0f, static_cast(defaultFont->getHeight())); - mReleaseDate.setFont(defaultFont); - mDeveloper.setFont(defaultFont); - mPublisher.setFont(defaultFont); - mGenre.setFont(defaultFont); - mPlayers.setFont(defaultFont); - mLastPlayed.setFont(defaultFont); - mPlayCount.setFont(defaultFont); - - float bottom = 0.0f; - - const float colSize = (mSize.x * 0.48f) / 2.0f; - for (unsigned int i = 0; i < labels.size(); ++i) { - const float heightDiff = (labels[i]->getSize().y - values[i]->getSize().y) / 2.0f; - values[i]->setPosition(labels[i]->getPosition() + - glm::vec3 {labels[i]->getSize().x, heightDiff, 0.0f}); - values[i]->setSize(colSize - labels[i]->getSize().x, values[i]->getSize().y); - values[i]->setDefaultZIndex(40.0f); - - float testBot = values[i]->getPosition().y + values[i]->getSize().y; - - if (testBot > bottom) - bottom = testBot; - } - - mDescContainer.setPosition(mDescContainer.getPosition().x, bottom + mSize.y * 0.01f); - mDescContainer.setSize(mDescContainer.getSize().x, mSize.y - mDescContainer.getPosition().y); -} - -void DetailedGamelistView::updateInfoPanel() -{ - FileData* file = (mList.size() == 0 || mList.isScrolling()) ? nullptr : mList.getSelected(); - - // If the game data has already been rendered to the info panel, then skip it this time. - if (file == mLastUpdated) - return; - - if (!mList.isScrolling()) - mLastUpdated = file; - - bool hideMetaDataFields = false; - - if (file) { - // Always hide the metadata fields if browsing grouped custom collections. - if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) - hideMetaDataFields = true; - else - hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); - - // Always hide the metadata fields for placeholders as well. - if (file->getType() == PLACEHOLDER) { - hideMetaDataFields = true; - mLastUpdated = nullptr; - } - } - - // If we're scrolling, hide the metadata fields if the last game had this options set, - // or if we're in the grouped custom collection view. - if (mList.isScrolling()) - if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || - (mLastUpdated->getSystem()->isCustomCollection() && - mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) - hideMetaDataFields = true; - - if (hideMetaDataFields) { - mLblRating.setVisible(false); - mRating.setVisible(false); - mLblReleaseDate.setVisible(false); - mReleaseDate.setVisible(false); - mLblDeveloper.setVisible(false); - mDeveloper.setVisible(false); - mLblPublisher.setVisible(false); - mPublisher.setVisible(false); - mLblGenre.setVisible(false); - mGenre.setVisible(false); - mLblPlayers.setVisible(false); - mPlayers.setVisible(false); - mLblLastPlayed.setVisible(false); - mLastPlayed.setVisible(false); - mLblPlayCount.setVisible(false); - mPlayCount.setVisible(false); - mBadges.setVisible(false); - } - else { - mLblRating.setVisible(true); - mRating.setVisible(true); - mLblReleaseDate.setVisible(true); - mReleaseDate.setVisible(true); - mLblDeveloper.setVisible(true); - mDeveloper.setVisible(true); - mLblPublisher.setVisible(true); - mPublisher.setVisible(true); - mLblGenre.setVisible(true); - mGenre.setVisible(true); - mLblPlayers.setVisible(true); - mPlayers.setVisible(true); - mLblLastPlayed.setVisible(true); - mLastPlayed.setVisible(true); - mLblPlayCount.setVisible(true); - mPlayCount.setVisible(true); - mBadges.setVisible(true); - } - - bool fadingOut = false; - if (file == nullptr) { - fadingOut = true; - } - else { - // If we're browsing a grouped custom collection, then update the folder metadata - // which will generate a description of three random games and return a pointer to - // the first of these so that we can display its game media. - if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) { - mRandomGame = CollectionSystemsManager::getInstance()->updateCollectionFolderMetadata( - file->getSystem()); - if (mRandomGame) { - mThumbnail.setImage(mRandomGame->getThumbnailPath()); - mMarquee.setImage(mRandomGame->getMarqueePath(), false, true); - mImage.setImage(mRandomGame->getImagePath()); - } - else { - mThumbnail.setImage(""); - mMarquee.setImage(""); - mImage.setImage(""); - } - } - else { - mThumbnail.setImage(file->getThumbnailPath()); - mMarquee.setImage(file->getMarqueePath(), false, true); - mImage.setImage(file->getImagePath()); - } - - // Populate the gamelistInfo field which shows an icon if a folder has been entered - // as well as the game count for the entire system (total and favorites separately). - // If a filter has been applied, then the number of filtered and total games replaces - // the game counter. - std::string gamelistInfoString; - Alignment infoAlign = mGamelistInfo.getHorizontalAlignment(); - - if (mIsFolder && infoAlign == ALIGN_RIGHT) - gamelistInfoString = ViewController::FOLDER_CHAR + " "; - - if (mIsFiltered) { - if (mFilteredGameCountAll == mFilteredGameCount) - gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " / " + - std::to_string(mGameCount); - else - gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " + " + - std::to_string(mFilteredGameCountAll - mFilteredGameCount) + - " / " + std::to_string(mGameCount); - } - else { - gamelistInfoString += - ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount); - if (!(file->getSystem()->isCollection() && - file->getSystem()->getFullName() == "favorites")) - gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + - std::to_string(mFavoritesGameCount); - } - - if (mIsFolder && infoAlign != ALIGN_RIGHT) - gamelistInfoString += " " + ViewController::FOLDER_CHAR; - - mGamelistInfo.setValue(gamelistInfoString); - - // Fade in the game image. - auto func = [this](float t) { - mImage.setOpacity(static_cast( - glm::mix(static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); - }; - mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); - - mDescription.setText(file->metadata.get("desc")); - mDescContainer.reset(); - - mRating.setValue(file->metadata.get("rating")); - mReleaseDate.setValue(file->metadata.get("releasedate")); - mDeveloper.setValue(file->metadata.get("developer")); - mPublisher.setValue(file->metadata.get("publisher")); - mGenre.setValue(file->metadata.get("genre")); - mPlayers.setValue(file->metadata.get("players")); - - // Populate the badge slots based on game metadata. - std::vector badgeSlots; - for (auto badge : mBadges.getBadgeTypes()) { - BadgeComponent::BadgeInfo badgeInfo; - badgeInfo.badgeType = badge; - if (badge == "controller") { - if (file->metadata.get("controller").compare("") != 0) { - badgeInfo.gameController = file->metadata.get("controller"); - badgeSlots.push_back(badgeInfo); - } - } - else if (badge == "altemulator") { - if (file->metadata.get(badge).compare("") != 0) - badgeSlots.push_back(badgeInfo); - } - else { - if (file->metadata.get(badge).compare("true") == 0) - badgeSlots.push_back(badgeInfo); - } - } - mBadges.setBadges(badgeSlots); - - mName.setValue(file->metadata.get("name")); - - if (file->getType() == GAME) { - if (!hideMetaDataFields) { - mLastPlayed.setValue(file->metadata.get("lastplayed")); - mPlayCount.setValue(file->metadata.get("playcount")); - } - } - else if (file->getType() == FOLDER) { - if (!hideMetaDataFields) { - mLastPlayed.setValue(file->metadata.get("lastplayed")); - mLblPlayCount.setVisible(false); - mPlayCount.setVisible(false); - } - } - - fadingOut = false; - } - - std::vector comps = getMDValues(); - comps.push_back(&mThumbnail); - comps.push_back(&mMarquee); - comps.push_back(&mImage); - comps.push_back(&mDescription); - comps.push_back(&mName); - comps.push_back(&mBadges); - std::vector labels = getMDLabels(); - comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - - for (auto it = comps.cbegin(); it != comps.cend(); ++it) { - GuiComponent* comp = *it; - // An animation is playing, then animate if reverse != fadingOut. - // An animation is not playing, then animate if opacity != our target opacity. - if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || - (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { - auto func = [comp](float t) { - comp->setOpacity(static_cast(glm::mix(0.0f, 1.0f, t) * 255)); - }; - comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); - } - } -} - -void DetailedGamelistView::launch(FileData* game) -{ - ViewController::getInstance()->triggerGameLaunch(game); -} - -std::vector DetailedGamelistView::getMDLabels() -{ - std::vector ret; - ret.push_back(&mLblRating); - ret.push_back(&mLblReleaseDate); - ret.push_back(&mLblDeveloper); - ret.push_back(&mLblPublisher); - ret.push_back(&mLblGenre); - ret.push_back(&mLblPlayers); - ret.push_back(&mLblLastPlayed); - ret.push_back(&mLblPlayCount); - return ret; -} - -std::vector DetailedGamelistView::getMDValues() -{ - std::vector ret; - ret.push_back(&mRating); - ret.push_back(&mReleaseDate); - ret.push_back(&mDeveloper); - ret.push_back(&mPublisher); - ret.push_back(&mGenre); - ret.push_back(&mPlayers); - ret.push_back(&mLastPlayed); - ret.push_back(&mPlayCount); - return ret; -} - -void DetailedGamelistView::update(int deltaTime) -{ - BasicGamelistView::update(deltaTime); - mImage.update(deltaTime); - - if (ViewController::getInstance()->getGameLaunchTriggered() && mImage.isAnimationPlaying(0)) - mImage.finishAnimation(0); -} - -void DetailedGamelistView::onShow() -{ - // Reset any Lottie animations. - for (auto extra : mThemeExtras) - extra->resetFileAnimation(); - - mLastUpdated = nullptr; - GuiComponent::onShow(); - updateInfoPanel(); -} diff --git a/es-app/src/views/legacygamelists/DetailedGamelistView.h b/es-app/src/views/legacygamelists/DetailedGamelistView.h deleted file mode 100644 index f5f68df28..000000000 --- a/es-app/src/views/legacygamelists/DetailedGamelistView.h +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// DetailedGamelistView.h -// -// Interface that defines a GamelistView of the type 'detailed'. -// - -#ifndef ES_APP_VIEWS_GAMELIST_DETAILED_GAMELIST_VIEW_H -#define ES_APP_VIEWS_GAMELIST_DETAILED_GAMELIST_VIEW_H - -#include "components/BadgeComponent.h" -#include "components/DateTimeComponent.h" -#include "components/RatingComponent.h" -#include "components/ScrollableContainer.h" -#include "views/gamelist/BasicGamelistView.h" - -class DetailedGamelistView : public BasicGamelistView -{ -public: - DetailedGamelistView(Window* window, FileData* root); - - void onShow() override; - void onThemeChanged(const std::shared_ptr& theme) override; - std::string getName() const override { return "detailed"; } - void launch(FileData* game) override; - - void preloadGamelist() override { updateInfoPanel(); } - -protected: - void update(int deltaTime) override; - -private: - void updateInfoPanel(); - - void initMDLabels(); - void initMDValues(); - - ImageComponent mThumbnail; - ImageComponent mMarquee; - ImageComponent mImage; - - TextComponent mLblRating; - TextComponent mLblReleaseDate; - TextComponent mLblDeveloper; - TextComponent mLblPublisher; - TextComponent mLblGenre; - TextComponent mLblPlayers; - TextComponent mLblLastPlayed; - TextComponent mLblPlayCount; - - RatingComponent mRating; - DateTimeComponent mReleaseDate; - TextComponent mDeveloper; - TextComponent mPublisher; - TextComponent mGenre; - TextComponent mPlayers; - DateTimeComponent mLastPlayed; - TextComponent mPlayCount; - TextComponent mName; - BadgeComponent mBadges; - - std::vector getMDLabels(); - std::vector getMDValues(); - - ScrollableContainer mDescContainer; - TextComponent mDescription; - TextComponent mGamelistInfo; - - FileData* mLastUpdated; -}; - -#endif // ES_APP_VIEWS_GAMELIST_DETAILED_GAMELIST_VIEW_H diff --git a/es-app/src/views/legacygamelists/GridGamelistView.cpp b/es-app/src/views/legacygamelists/GridGamelistView.cpp deleted file mode 100644 index 187e7dd62..000000000 --- a/es-app/src/views/legacygamelists/GridGamelistView.cpp +++ /dev/null @@ -1,743 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// GridGamelistView.cpp -// -// Interface that defines a GamelistView of the type 'grid'. -// - -#include "views/gamelist/GridGamelistView.h" - -#include "CollectionSystemsManager.h" -#include "Settings.h" -#include "Sound.h" -#include "SystemData.h" -#include "UIModeController.h" -#include "animations/LambdaAnimation.h" -#include "views/ViewController.h" - -#define FADE_IN_START_OPACITY 0.5f -#define FADE_IN_TIME 650 - -GridGamelistView::GridGamelistView(Window* window, FileData* root) - : ISimpleGamelistView(window, root) - , mGrid(window) - , mMarquee(window) - , mImage(window) - , mLblRating(window) - , mLblReleaseDate(window) - , mLblDeveloper(window) - , mLblPublisher(window) - , mLblGenre(window) - , mLblPlayers(window) - , mLblLastPlayed(window) - , mLblPlayCount(window) - , mBadges(window) - , mRating(window) - , mReleaseDate(window) - , mDeveloper(window) - , mPublisher(window) - , mGenre(window) - , mPlayers(window) - , mLastPlayed(window) - , mPlayCount(window) - , mName(window) - , mDescContainer(window) - , mDescription(window) - , mGamelistInfo(window) -{ - const float padding = 0.01f; - - mGrid.setPosition(mSize.x * 0.1f, mSize.y * 0.1f); - mGrid.setDefaultZIndex(20.0f); - mGrid.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - addChild(&mGrid); - - populateList(root->getChildrenListToDisplay(), root); - - // Metadata labels + values. - addChild(&mBadges); - mLblRating.setText("Rating: ", false); - addChild(&mLblRating); - addChild(&mRating); - mLblReleaseDate.setText("Released: ", false); - addChild(&mLblReleaseDate); - addChild(&mReleaseDate); - mLblDeveloper.setText("Developer: ", false); - addChild(&mLblDeveloper); - addChild(&mDeveloper); - mLblPublisher.setText("Publisher: ", false); - addChild(&mLblPublisher); - addChild(&mPublisher); - mLblGenre.setText("Genre: ", false); - addChild(&mLblGenre); - addChild(&mGenre); - mLblPlayers.setText("Players: ", false); - addChild(&mLblPlayers); - addChild(&mPlayers); - mLblLastPlayed.setText("Last played: ", false); - addChild(&mLblLastPlayed); - mLastPlayed.setDisplayRelative(true); - addChild(&mLastPlayed); - mLblPlayCount.setText("Times played: ", false); - addChild(&mLblPlayCount); - addChild(&mPlayCount); - - mName.setPosition(mSize.x, mSize.y); - mName.setDefaultZIndex(40.0f); - mName.setColor(0xAAAAAAFF); - mName.setFont(Font::get(FONT_SIZE_MEDIUM)); - mName.setHorizontalAlignment(ALIGN_CENTER); - addChild(&mName); - - mDescContainer.setPosition(mSize.x * padding, mSize.y * 0.65f); - mDescContainer.setSize(mSize.x * (0.50f - 2.0f * padding), - mSize.y - mDescContainer.getPosition().y); - mDescContainer.setAutoScroll(true); - mDescContainer.setDefaultZIndex(40.0f); - addChild(&mDescContainer); - - mDescription.setFont(Font::get(FONT_SIZE_SMALL)); - mDescription.setSize(mDescContainer.getSize().x, 0.0f); - mDescContainer.addChild(&mDescription); - - mMarquee.setOrigin(0.5f, 0.5f); - mMarquee.setPosition(mSize.x * 0.25f, mSize.y * 0.10f); - mMarquee.setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f); - mMarquee.setDefaultZIndex(35.0f); - mMarquee.setVisible(false); - addChild(&mMarquee); - - mImage.setOrigin(0.5f, 0.5f); - mImage.setPosition(2.0f, 2.0f); - mImage.setMaxSize(mSize.x * (0.50f - 2.0f * padding), mSize.y * 0.4f); - mImage.setDefaultZIndex(10.0f); - mImage.setVisible(false); - addChild(&mImage); - - mGamelistInfo.setOrigin(0.5f, 0.5f); - mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL)); - mGamelistInfo.setDefaultZIndex(50.0f); - mGamelistInfo.setVisible(true); - addChild(&mGamelistInfo); - - initMDLabels(); - initMDValues(); - updateInfoPanel(); -} - -void GridGamelistView::onFileChanged(FileData* file, bool reloadGamelist) -{ - if (reloadGamelist) { - // Might switch to a detailed view. - ViewController::getInstance()->reloadGamelistView(this); - return; - } - - ISimpleGamelistView::onFileChanged(file, reloadGamelist); -} - -void GridGamelistView::setCursor(FileData* cursor) -{ - if (!mGrid.setCursor(cursor) && (!cursor->isPlaceHolder())) { - populateList(cursor->getParent()->getChildrenListToDisplay(), cursor->getParent()); - mGrid.setCursor(cursor); - - // Update our cursor stack in case our cursor just got set to some folder - // we weren't in before. - if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) { - std::stack tmp; - FileData* ptr = cursor->getParent(); - while (ptr && ptr != mRoot) { - tmp.push(ptr); - ptr = ptr->getParent(); - } - - // Flip the stack and put it in mCursorStack. - mCursorStack = std::stack(); - while (!tmp.empty()) { - mCursorStack.push(tmp.top()); - tmp.pop(); - } - } - } -} - -bool GridGamelistView::input(InputConfig* config, Input input) -{ - if (input.value == 0 && - (config->isMappedLike("left", input) || config->isMappedLike("right", input) || - (config->isMappedLike("up", input)) || (config->isMappedLike("down", input)))) - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - - if (input.value != 0 && config->isMappedLike("righttrigger", input)) { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - mGrid.setCursor(mGrid.getLast()); - } - - if (input.value != 0 && config->isMappedLike("lefttrigger", input)) { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - mGrid.setCursor(mGrid.getFirst()); - } - - if (config->isMappedLike("left", input) || config->isMappedLike("right", input)) - return GuiComponent::input(config, input); - - return ISimpleGamelistView::input(config, input); -} - -const std::string GridGamelistView::getImagePath(FileData* file) -{ - ImageSource src = mGrid.getImageSource(); - - if (src == ImageSource::IMAGE) - return file->getImagePath(); - else if (src == ImageSource::MIXIMAGE) - return file->getMiximagePath(); - else if (src == ImageSource::SCREENSHOT) - return file->getScreenshotPath(); - else if (src == ImageSource::COVER) - return file->getCoverPath(); - else if (src == ImageSource::MARQUEE) - return file->getMarqueePath(); - else if (src == ImageSource::BOX3D) - return file->get3DBoxPath(); - - // If no thumbnail was found, then use the image media type. - if (file->getThumbnailPath() == "") - return file->getImagePath(); - - return file->getThumbnailPath(); -} - -void GridGamelistView::populateList(const std::vector& files, FileData* firstEntry) -{ - firstGameEntry = nullptr; - - mGrid.clear(); - mHeaderText.setText(mRoot->getSystem()->getFullName()); - if (files.size() > 0) { - for (auto it = files.cbegin(); it != files.cend(); ++it) { - if (!firstGameEntry && (*it)->getType() == GAME) - firstGameEntry = (*it); - mGrid.add((*it)->getName(), getImagePath(*it), *it); - } - } - else { - addPlaceholder(firstEntry); - } - - generateGamelistInfo(getCursor(), firstEntry); - generateFirstLetterIndex(files); -} - -void GridGamelistView::onThemeChanged(const std::shared_ptr& theme) -{ - ISimpleGamelistView::onThemeChanged(theme); - - using namespace ThemeFlags; - mGrid.applyTheme(theme, getName(), "gamegrid", ALL); - mName.applyTheme(theme, getName(), "md_name", ALL); - mMarquee.applyTheme(theme, getName(), "md_marquee", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mImage.applyTheme(theme, getName(), "md_image", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - - initMDLabels(); - std::vector labels = getMDLabels(); - assert(labels.size() == 8); - std::vector lblElements = { - "md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", - "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"}; - - for (unsigned int i = 0; i < labels.size(); ++i) - labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); - - initMDValues(); - std::vector values = getMDValues(); - assert(values.size() == 8); - std::vector valElements = {"md_rating", "md_releasedate", "md_developer", - "md_publisher", "md_genre", "md_players", - "md_lastplayed", "md_playcount"}; - - for (unsigned int i = 0; i < values.size(); ++i) - values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); - - mDescContainer.applyTheme(theme, getName(), "md_description", - POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); - mDescription.setSize(mDescContainer.getSize().x, 0.0f); - mDescription.applyTheme( - theme, getName(), "md_description", - ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); - - // Repopulate list in case a new theme is displaying a different image. - // Preserve selection. - FileData* file = mGrid.getSelected(); - populateList(mRoot->getChildrenListToDisplay(), mRoot); - mGrid.setCursor(file); - - mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); - // If there is no position defined in the theme for gamelistInfo, then hide it. - if (mGamelistInfo.getPosition() == glm::vec3 {}) - mGamelistInfo.setVisible(false); - else - mGamelistInfo.setVisible(true); - - sortChildren(); -} - -void GridGamelistView::onShow() -{ - // Reset any Lottie animations. - for (auto extra : mThemeExtras) - extra->resetFileAnimation(); - - GuiComponent::onShow(); - updateInfoPanel(); -} - -void GridGamelistView::initMDLabels() -{ - std::vector components = getMDLabels(); - - const unsigned int colCount = 2; - const unsigned int rowCount = static_cast(components.size() / 2); - - glm::vec3 start {mSize.x * 0.01f, mSize.y * 0.625f, 0.0f}; - - const float colSize = (mSize.x * 0.48f) / colCount; - const float rowPadding = 0.01f * mSize.y; - - for (unsigned int i = 0; i < components.size(); ++i) { - const unsigned int row = i % rowCount; - glm::vec3 pos {}; - if (row == 0) { - pos = start + glm::vec3 {colSize * (i / rowCount), 0.0f, 0.0f}; - } - else { - // Work from the last component. - GuiComponent* lc = components[i - 1]; - pos = lc->getPosition() + glm::vec3 {0.0f, lc->getSize().y + rowPadding, 0.0f}; - } - - components[i]->setFont(Font::get(FONT_SIZE_SMALL)); - components[i]->setPosition(pos); - components[i]->setDefaultZIndex(40.0f); - } -} - -void GridGamelistView::initMDValues() -{ - std::vector labels = getMDLabels(); - std::vector values = getMDValues(); - - std::shared_ptr defaultFont = Font::get(FONT_SIZE_SMALL); - mRating.setSize(defaultFont->getHeight() * 5.0f, static_cast(defaultFont->getHeight())); - mReleaseDate.setFont(defaultFont); - mDeveloper.setFont(defaultFont); - mPublisher.setFont(defaultFont); - mGenre.setFont(defaultFont); - mPlayers.setFont(defaultFont); - mLastPlayed.setFont(defaultFont); - mPlayCount.setFont(defaultFont); - - float bottom = 0.0f; - - const float colSize = (mSize.x * 0.48f) / 2.0f; - for (unsigned int i = 0; i < labels.size(); ++i) { - const float heightDiff = (labels[i]->getSize().y - values[i]->getSize().y) / 2.0f; - values[i]->setPosition(labels[i]->getPosition() + - glm::vec3 {labels[i]->getSize().x, heightDiff, 0.0f}); - values[i]->setSize(colSize - labels[i]->getSize().x, values[i]->getSize().y); - values[i]->setDefaultZIndex(40.0f); - - float testBot = values[i]->getPosition().y + values[i]->getSize().y; - if (testBot > bottom) - bottom = testBot; - } - - mDescContainer.setPosition(mDescContainer.getPosition().x, bottom + mSize.y * 0.01f); - mDescContainer.setSize(mDescContainer.getSize().x, mSize.y - mDescContainer.getPosition().y); -} - -void GridGamelistView::updateInfoPanel() -{ - FileData* file = (mGrid.size() == 0 || mGrid.isScrolling()) ? nullptr : mGrid.getSelected(); - bool hideMetaDataFields = false; - - if (file) - hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); - - if (hideMetaDataFields) { - mLblRating.setVisible(false); - mRating.setVisible(false); - mLblReleaseDate.setVisible(false); - mReleaseDate.setVisible(false); - mLblDeveloper.setVisible(false); - mDeveloper.setVisible(false); - mLblPublisher.setVisible(false); - mPublisher.setVisible(false); - mLblGenre.setVisible(false); - mGenre.setVisible(false); - mLblPlayers.setVisible(false); - mPlayers.setVisible(false); - mLblLastPlayed.setVisible(false); - mLastPlayed.setVisible(false); - mLblPlayCount.setVisible(false); - mPlayCount.setVisible(false); - } - else { - mLblRating.setVisible(true); - mRating.setVisible(true); - mLblReleaseDate.setVisible(true); - mReleaseDate.setVisible(true); - mLblDeveloper.setVisible(true); - mDeveloper.setVisible(true); - mLblPublisher.setVisible(true); - mPublisher.setVisible(true); - mLblGenre.setVisible(true); - mGenre.setVisible(true); - mLblPlayers.setVisible(true); - mPlayers.setVisible(true); - mLblLastPlayed.setVisible(true); - mLastPlayed.setVisible(true); - mLblPlayCount.setVisible(true); - mPlayCount.setVisible(true); - } - - bool fadingOut = false; - if (file == nullptr) { - fadingOut = true; - } - else { - mMarquee.setImage(file->getMarqueePath(), false, true); - - // Populate the gamelistInfo field which shows an icon if a folder has been entered - // as well as the game count for the entire system (total and favorites separately). - // If a filter has been applied, then the number of filtered and total games replaces - // the game counter. - std::string gamelistInfoString; - Alignment infoAlign = mGamelistInfo.getHorizontalAlignment(); - - if (mIsFolder && infoAlign == ALIGN_RIGHT) - gamelistInfoString = ViewController::FOLDER_CHAR + " "; - - if (mIsFiltered) { - if (mFilteredGameCountAll == mFilteredGameCount) - gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " / " + - std::to_string(mGameCount); - else - gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " + " + - std::to_string(mFilteredGameCountAll - mFilteredGameCount) + - " / " + std::to_string(mGameCount); - } - else { - gamelistInfoString += - ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount); - if (!(file->getSystem()->isCollection() && - file->getSystem()->getFullName() == "favorites")) - gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + - std::to_string(mFavoritesGameCount); - } - - if (mIsFolder && infoAlign != ALIGN_RIGHT) - gamelistInfoString += " " + ViewController::FOLDER_CHAR; - - mGamelistInfo.setValue(gamelistInfoString); - - // Fade in the game image. - auto func = [this](float t) { - mImage.setOpacity(static_cast( - glm::mix(static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); - }; - mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); - - mDescription.setText(file->metadata.get("desc")); - mDescContainer.reset(); - - mRating.setValue(file->metadata.get("rating")); - mReleaseDate.setValue(file->metadata.get("releasedate")); - mDeveloper.setValue(file->metadata.get("developer")); - mPublisher.setValue(file->metadata.get("publisher")); - mGenre.setValue(file->metadata.get("genre")); - mPlayers.setValue(file->metadata.get("players")); - mName.setValue(file->metadata.get("name")); - - if (file->getType() == GAME) { - if (!hideMetaDataFields) { - mLastPlayed.setValue(file->metadata.get("lastplayed")); - mPlayCount.setValue(file->metadata.get("playcount")); - } - } - else if (file->getType() == FOLDER) { - if (!hideMetaDataFields) { - mLastPlayed.setValue(file->metadata.get("lastplayed")); - mLblPlayCount.setVisible(false); - mPlayCount.setVisible(false); - } - } - - fadingOut = false; - } - - std::vector comps = getMDValues(); - comps.push_back(&mDescription); - comps.push_back(&mName); - comps.push_back(&mMarquee); - comps.push_back(&mImage); - std::vector labels = getMDLabels(); - comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - - for (auto it = comps.cbegin(); it != comps.cend(); ++it) { - GuiComponent* comp = *it; - // An animation is playing, then animate if reverse != fadingOut. - // An animation is not playing, then animate if opacity != our target opacity. - if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || - (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { - - // TODO: This does not seem to work, needs to be reviewed later. - // auto func = [comp](float t) { - auto func = [](float t) { - // comp->setOpacity(static_cast(glm::mix(0.0f, 1.0f, t) * 255)); - }; - comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); - } - } -} - -void GridGamelistView::addPlaceholder(FileData* firstEntry) -{ - // Empty list, add a placeholder. - FileData* placeholder; - - if (firstEntry && firstEntry->getSystem()->isGroupedCustomCollection()) - placeholder = firstEntry->getSystem()->getPlaceholder(); - else - placeholder = this->mRoot->getSystem()->getPlaceholder(); - - mGrid.add(placeholder->getName(), "", placeholder); -} - -void GridGamelistView::launch(FileData* game) -{ - // This triggers ViewController to launch the game. - ViewController::getInstance()->triggerGameLaunch(game); -} - -void GridGamelistView::remove(FileData* game, bool deleteFile) -{ - // Delete the game file on the filesystem. - if (deleteFile) - Utils::FileSystem::removeFile(game->getPath()); - - FileData* parent = game->getParent(); - // Select next element in list, or previous if none. - if (getCursor() == game) { - std::vector siblings = parent->getChildrenListToDisplay(); - auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game); - int gamePos = static_cast(std::distance(siblings.cbegin(), gameIter)); - if (gameIter != siblings.cend()) { - if ((gamePos + 1) < static_cast(siblings.size())) - setCursor(siblings.at(gamePos + 1)); - else if ((gamePos - 1) > 0) - setCursor(siblings.at(gamePos - 1)); - } - } - mGrid.remove(game); - - if (mGrid.size() == 0) - addPlaceholder(); - - // If a game has been deleted, immediately remove the entry from gamelist.xml - // regardless of the value of the setting SaveGamelistsMode. - game->setDeletionFlag(true); - parent->getSystem()->writeMetaData(); - - // Remove before repopulating (removes from parent), then update the view. - delete game; - onFileChanged(parent, false); -} - -void GridGamelistView::removeMedia(FileData* game) -{ - std::string systemMediaDir = FileData::getMediaDirectory() + game->getSystem()->getName(); - std::string mediaType; - std::string path; - - // If there are no media files left in the directory after the deletion, then remove - // the directory too. Remove any empty parent directories as well. - auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType, - std::string path) { - std::string parentPath = Utils::FileSystem::getParent(path); - while (parentPath != systemMediaDir + "/" + mediaType) { - if (Utils::FileSystem::getDirContent(parentPath).size() == 0) { - Utils::FileSystem::removeDirectory(parentPath); - parentPath = Utils::FileSystem::getParent(parentPath); - } - else { - break; - } - } - }; - - // Remove all game media files on the filesystem. - while (Utils::FileSystem::exists(game->getVideoPath())) { - mediaType = "videos"; - path = game->getVideoPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getMiximagePath())) { - mediaType = "miximages"; - path = game->getMiximagePath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getScreenshotPath())) { - mediaType = "screenshots"; - path = game->getScreenshotPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getTitleScreenPath())) { - mediaType = "titlescreens"; - path = game->getTitleScreenPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getCoverPath())) { - mediaType = "covers"; - path = game->getCoverPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - if (Utils::FileSystem::exists(game->getBackCoverPath())) { - mediaType = "backcovers"; - path = game->getBackCoverPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getFanArtPath())) { - mediaType = "fanart"; - path = game->getFanArtPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getMarqueePath())) { - mediaType = "marquees"; - path = game->getMarqueePath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->get3DBoxPath())) { - mediaType = "3dboxes"; - path = game->get3DBoxPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getPhysicalMediaPath())) { - mediaType = "physicalmedia"; - path = game->getPhysicalMediaPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } - - while (Utils::FileSystem::exists(game->getThumbnailPath())) { - mediaType = "thumbnails"; - path = game->getThumbnailPath(); - Utils::FileSystem::removeFile(path); - removeEmptyDirFunc(systemMediaDir, mediaType, path); - } -} - -std::vector GridGamelistView::getMDLabels() -{ - std::vector ret; - ret.push_back(&mLblRating); - ret.push_back(&mLblReleaseDate); - ret.push_back(&mLblDeveloper); - ret.push_back(&mLblPublisher); - ret.push_back(&mLblGenre); - ret.push_back(&mLblPlayers); - ret.push_back(&mLblLastPlayed); - ret.push_back(&mLblPlayCount); - return ret; -} - -std::vector GridGamelistView::getMDValues() -{ - std::vector ret; - ret.push_back(&mRating); - ret.push_back(&mReleaseDate); - ret.push_back(&mDeveloper); - ret.push_back(&mPublisher); - ret.push_back(&mGenre); - ret.push_back(&mPlayers); - ret.push_back(&mLastPlayed); - ret.push_back(&mPlayCount); - return ret; -} - -std::vector GridGamelistView::getHelpPrompts() -{ - std::vector prompts; - - if (Settings::getInstance()->getBool("QuickSystemSelect")) - prompts.push_back(HelpPrompt("lr", "system")); - prompts.push_back(HelpPrompt("up/down/left/right", "choose")); - - if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST) - prompts.push_back(HelpPrompt("a", "enter")); - else - prompts.push_back(HelpPrompt("a", "launch")); - - prompts.push_back(HelpPrompt("b", "back")); - - if (mRoot->getSystem()->isGameSystem() && - mRoot->getSystem()->getThemeFolder() != "custom-collections") - prompts.push_back(HelpPrompt("x", "view media")); - - if (mRoot->getSystem()->isGameSystem() && !mCursorStack.empty() && - mRoot->getSystem()->getThemeFolder() == "custom-collections") - prompts.push_back(HelpPrompt("x", "view media")); - - if (!UIModeController::getInstance()->isUIModeKid()) - prompts.push_back(HelpPrompt("back", "options")); - if (mRoot->getSystem()->isGameSystem() && Settings::getInstance()->getBool("RandomAddButton")) - prompts.push_back(HelpPrompt("thumbstickclick", "random")); - - if (mRoot->getSystem()->isGameSystem() && - (mRoot->getSystem()->getThemeFolder() != "custom-collections" || !mCursorStack.empty()) && - !UIModeController::getInstance()->isUIModeKid() && - !UIModeController::getInstance()->isUIModeKiosk() && - (Settings::getInstance()->getBool("FavoritesAddButton") || - CollectionSystemsManager::getInstance()->isEditing())) { - std::string prompt = CollectionSystemsManager::getInstance()->getEditingCollection(); - prompts.push_back(HelpPrompt("y", prompt)); - } - else if (mRoot->getSystem()->isGameSystem() && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - CollectionSystemsManager::getInstance()->isEditing()) { - std::string prompt = CollectionSystemsManager::getInstance()->getEditingCollection(); - prompts.push_back(HelpPrompt("y", prompt)); - } - return prompts; -} - -void GridGamelistView::update(int deltaTime) -{ - // Update. - ISimpleGamelistView::update(deltaTime); -} diff --git a/es-app/src/views/legacygamelists/GridGamelistView.h b/es-app/src/views/legacygamelists/GridGamelistView.h deleted file mode 100644 index eb74025f3..000000000 --- a/es-app/src/views/legacygamelists/GridGamelistView.h +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// GridGamelistView.h -// -// Interface that defines a GamelistView of the type 'grid'. -// - -#ifndef ES_APP_VIEWS_GAMELIST_GRID_GAMELIST_VIEW_H -#define ES_APP_VIEWS_GAMELIST_GRID_GAMELIST_VIEW_H - -#include "components/BadgeComponent.h" -#include "components/DateTimeComponent.h" -#include "components/ImageGridComponent.h" -#include "components/RatingComponent.h" -#include "components/ScrollableContainer.h" -#include "components/VideoComponent.h" -#include "views/gamelist/ISimpleGamelistView.h" - -class GridGamelistView : public ISimpleGamelistView -{ -public: - GridGamelistView(Window* window, FileData* root); - virtual ~GridGamelistView() {} - - // Called when a FileData* is added, has its metadata changed, or is removed. - void onFileChanged(FileData* file, bool reloadGamelist) override; - - void onShow() override; - void onThemeChanged(const std::shared_ptr& theme) override; - void setCursor(FileData* cursor) override; - - FileData* getCursor() override { return mGrid.getSelected(); } - FileData* getNextEntry() override { return mGrid.getNext(); } - FileData* getPreviousEntry() override { return mGrid.getPrevious(); } - FileData* getFirstEntry() override { return mGrid.getFirst(); } - FileData* getLastEntry() override { return mGrid.getLast(); } - FileData* getFirstGameEntry() override { return firstGameEntry; } - - std::string getName() const override { return "grid"; } - - bool input(InputConfig* config, Input input) override; - - std::vector getHelpPrompts() override; - void launch(FileData* game) override; - - bool isListScrolling() override { return mGrid.isScrolling(); } - void stopListScrolling() override - { - mGrid.stopAllAnimations(); - mGrid.stopScrolling(); - } - - const std::vector& getFirstLetterIndex() override { return mFirstLetterIndex; } - - void addPlaceholder(FileData* firstEntry = nullptr) override; - -protected: - std::string getQuickSystemSelectRightButton() override { return "rightshoulder"; } - std::string getQuickSystemSelectLeftButton() override { return "leftshoulder"; } - void populateList(const std::vector& files, FileData* firstEntry) override; - void remove(FileData* game, bool deleteFile) override; - void removeMedia(FileData* game) override; - void update(int deltaTime) override; - - ImageGridComponent mGrid; - // Points to the first game in the list, i.e. the first entry which is of the type 'GAME'. - FileData* firstGameEntry; - -private: - void updateInfoPanel(); - const std::string getImagePath(FileData* file); - - void initMDLabels(); - void initMDValues(); - - ImageComponent mMarquee; - ImageComponent mImage; - - TextComponent mLblRating; - TextComponent mLblReleaseDate; - TextComponent mLblDeveloper; - TextComponent mLblPublisher; - TextComponent mLblGenre; - TextComponent mLblPlayers; - TextComponent mLblLastPlayed; - TextComponent mLblPlayCount; - - BadgeComponent mBadges; - RatingComponent mRating; - DateTimeComponent mReleaseDate; - TextComponent mDeveloper; - TextComponent mPublisher; - TextComponent mGenre; - TextComponent mPlayers; - DateTimeComponent mLastPlayed; - TextComponent mPlayCount; - TextComponent mName; - - std::vector getMDLabels(); - std::vector getMDValues(); - - ScrollableContainer mDescContainer; - TextComponent mDescription; - TextComponent mGamelistInfo; -}; - -#endif // ES_APP_VIEWS_GAMELIST_GRID_GAMELIST_VIEW_H diff --git a/es-app/src/views/legacygamelists/IGamelistView.cpp b/es-app/src/views/legacygamelists/IGamelistView.cpp deleted file mode 100644 index 06b61ac77..000000000 --- a/es-app/src/views/legacygamelists/IGamelistView.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// IGamelistView.cpp -// -// Interface that defines the minimum for a GamelistView. -// - -#include "views/gamelist/IGamelistView.h" - -#include "Sound.h" -#include "UIModeController.h" -#include "Window.h" -#include "guis/GuiGamelistOptions.h" -#include "views/ViewController.h" - -IGamelistView::IGamelistView(Window* window, FileData* root) - : GuiComponent {window} - , mRoot {root} -{ - setSize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); -} - -void IGamelistView::setTheme(const std::shared_ptr& theme) -{ - mTheme = theme; - onThemeChanged(theme); -} - -bool IGamelistView::input(InputConfig* config, Input input) -{ - // Select button opens GuiGamelistOptions. - if (!UIModeController::getInstance()->isUIModeKid() && // Line break. - config->isMappedTo("back", input) && input.value) { - ViewController::getInstance()->cancelViewTransitions(); - stopListScrolling(); - mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem())); - return true; - } - - // Ctrl-R reloads the view when debugging. - else if (Settings::getInstance()->getBool("Debug") && - config->getDeviceId() == DEVICE_KEYBOARD && - (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && input.id == SDLK_r && - input.value != 0) { - LOG(LogDebug) << "IGamelistView::input(): Reloading view"; - ViewController::getInstance()->reloadGamelistView(this, true); - return true; - } - - return GuiComponent::input(config, input); -} - -HelpStyle IGamelistView::getHelpStyle() -{ - HelpStyle style; - style.applyTheme(mTheme, getName()); - return style; -} - -void IGamelistView::render(const glm::mat4& parentTrans) -{ - glm::mat4 trans {parentTrans * getTransform()}; - - float scaleX = trans[0].x; - float scaleY = trans[1].y; - - glm::ivec2 pos {static_cast(std::round(trans[3].x)), - static_cast(std::round(trans[3].y))}; - glm::ivec2 size {static_cast(std::round(mSize.x * scaleX)), - static_cast(std::round(mSize.y * scaleY))}; - - Renderer::pushClipRect(pos, size); - renderChildren(trans); - Renderer::popClipRect(); -} diff --git a/es-app/src/views/legacygamelists/IGamelistView.h b/es-app/src/views/legacygamelists/IGamelistView.h deleted file mode 100644 index bace12eb6..000000000 --- a/es-app/src/views/legacygamelists/IGamelistView.h +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// IGamelistView.h -// -// Interface that defines the minimum for a GamelistView. -// - -#ifndef ES_APP_VIEWS_GAMELIST_IGAMELIST_VIEW_H -#define ES_APP_VIEWS_GAMELIST_IGAMELIST_VIEW_H - -#include "FileData.h" -#include "GuiComponent.h" -#include "renderers/Renderer.h" - -class ThemeData; -class Window; - -// This is an interface that defines the minimum for a GamelistView. -class IGamelistView : public GuiComponent -{ -public: - IGamelistView(Window* window, FileData* root); - virtual ~IGamelistView() {} - - // Called when a FileData* is added, has its metadata changed, or is removed. - virtual void onFileChanged(FileData* file, bool reloadGamelist) = 0; - - // Called whenever the theme changes. - virtual void onThemeChanged(const std::shared_ptr& theme) = 0; - - void setTheme(const std::shared_ptr& theme); - const std::shared_ptr getTheme() const { return mTheme; } - - virtual void preloadGamelist() {}; - - virtual FileData* getCursor() = 0; - virtual void setCursor(FileData*) = 0; - virtual FileData* getNextEntry() = 0; - virtual FileData* getPreviousEntry() = 0; - virtual FileData* getFirstEntry() = 0; - virtual FileData* getLastEntry() = 0; - virtual FileData* getFirstGameEntry() = 0; - virtual const std::vector& getFirstLetterIndex() = 0; - virtual void addPlaceholder(FileData*) = 0; - - virtual void copyCursorHistory(std::vector& cursorHistory) = 0; - virtual void populateCursorHistory(std::vector& cursorHistory) = 0; - - bool input(InputConfig* config, Input input) override; - virtual void remove(FileData* game, bool deleteFile) = 0; - virtual void removeMedia(FileData* game) = 0; - - virtual std::string getName() const = 0; - virtual void launch(FileData* game) = 0; - - HelpStyle getHelpStyle() override; - - void render(const glm::mat4& parentTrans) override; - -protected: - FileData* mRoot; - std::shared_ptr mTheme; -}; - -#endif // ES_APP_VIEWS_GAMELIST_IGAMELIST_VIEW_H diff --git a/es-app/src/views/legacygamelists/ISimpleGamelistView.cpp b/es-app/src/views/legacygamelists/ISimpleGamelistView.cpp deleted file mode 100644 index 24a2a78b3..000000000 --- a/es-app/src/views/legacygamelists/ISimpleGamelistView.cpp +++ /dev/null @@ -1,583 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// ISimpleGamelistView.cpp -// -// Interface that defines a simple gamelist view. -// - -#include "views/gamelist/ISimpleGamelistView.h" - -#include "CollectionSystemsManager.h" -#include "FileFilterIndex.h" -#include "Settings.h" -#include "Sound.h" -#include "SystemData.h" -#include "UIModeController.h" -#include "Window.h" -#include "utils/StringUtil.h" -#include "views/ViewController.h" - -#include "Log.h" - -ISimpleGamelistView::ISimpleGamelistView(Window* window, FileData* root) - : IGamelistView {window, root} - , mHeaderText {window} - , mHeaderImage {window} - , mBackground {window} - , mRandomGame {nullptr} -{ - mHeaderText.setText("Logo Text", false); - mHeaderText.setSize(mSize.x, 0.0f); - mHeaderText.setPosition(0.0f, 0.0f); - mHeaderText.setHorizontalAlignment(ALIGN_CENTER); - mHeaderText.setDefaultZIndex(50.0f); - - mHeaderImage.setResize(0.0f, mSize.y * 0.185f); - mHeaderImage.setOrigin(0.5f, 0.0f); - mHeaderImage.setPosition(mSize.x / 2.0f, 0.0f); - mHeaderImage.setDefaultZIndex(50.0f); - - mBackground.setResize(mSize.x, mSize.y); - mBackground.setDefaultZIndex(0.0f); - - addChild(&mHeaderText); - addChild(&mBackground); -} - -ISimpleGamelistView::~ISimpleGamelistView() -{ - // Remove theme extras. - for (auto extra : mThemeExtras) { - removeChild(extra); - delete extra; - } - mThemeExtras.clear(); -} - -void ISimpleGamelistView::onThemeChanged(const std::shared_ptr& theme) -{ - using namespace ThemeFlags; - mBackground.applyTheme(theme, getName(), "background", ALL); - mHeaderImage.applyTheme(theme, getName(), "logo", ALL); - mHeaderText.applyTheme(theme, getName(), "logoText", ALL); - - // Remove old theme extras. - for (auto extra : mThemeExtras) { - removeChild(extra); - delete extra; - } - mThemeExtras.clear(); - - // Add new theme extras. - mThemeExtras = ThemeData::makeExtras(theme, getName(), mWindow); - for (auto extra : mThemeExtras) - addChild(extra); - - if (mHeaderImage.hasImage()) { - removeChild(&mHeaderText); - addChild(&mHeaderImage); - } - else { - addChild(&mHeaderText); - removeChild(&mHeaderImage); - } -} - -void ISimpleGamelistView::onFileChanged(FileData* file, bool reloadGamelist) -{ - // We could be tricky here to be efficient; - // but this shouldn't happen very often so we'll just always repopulate. - FileData* cursor = getCursor(); - if (!cursor->isPlaceHolder()) { - populateList(cursor->getParent()->getChildrenListToDisplay(), cursor->getParent()); - setCursor(cursor); - } - else { - populateList(mRoot->getChildrenListToDisplay(), mRoot); - setCursor(cursor); - } -} - -bool ISimpleGamelistView::input(InputConfig* config, Input input) -{ - if (input.value != 0) { - if (config->isMappedTo("a", input)) { - FileData* cursor = getCursor(); - if (cursor->getType() == GAME) { - onPauseVideo(); - ViewController::getInstance()->cancelViewTransitions(); - stopListScrolling(); - launch(cursor); - } - else { - // It's a folder. - if (cursor->getChildren().size() > 0) { - ViewController::getInstance()->cancelViewTransitions(); - NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND); - mCursorStack.push(cursor); - populateList(cursor->getChildrenListToDisplay(), cursor); - - FileData* newCursor = nullptr; - std::vector listEntries = cursor->getChildrenListToDisplay(); - // Check if there is an entry in the cursor stack history matching any entry - // in the currect folder. If so, select that entry. - for (auto it = mCursorStackHistory.begin(); // Line break. - it != mCursorStackHistory.end(); ++it) { - if (std::find(listEntries.begin(), listEntries.end(), *it) != - listEntries.end()) { - newCursor = *it; - mCursorStackHistory.erase(it); - break; - } - } - - // If there was no match in the cursor history, simply select the first entry. - if (!newCursor) - newCursor = getCursor(); - setCursor(newCursor); - if (mRoot->getSystem()->getThemeFolder() == "custom-collections") - updateHelpPrompts(); - } - else { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - } - } - - return true; - } - else if (config->isMappedTo("b", input)) { - ViewController::getInstance()->cancelViewTransitions(); - if (mCursorStack.size()) { - // Save the position to the cursor stack history. - mCursorStackHistory.push_back(getCursor()); - NavigationSounds::getInstance().playThemeNavigationSound(BACKSOUND); - populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay(), - mCursorStack.top()->getParent()); - setCursor(mCursorStack.top()); - if (mCursorStack.size() > 0) - mCursorStack.pop(); - if (mRoot->getSystem()->getThemeFolder() == "custom-collections") - updateHelpPrompts(); - } - else { - NavigationSounds::getInstance().playThemeNavigationSound(BACKSOUND); - onPauseVideo(); - onFocusLost(); - stopListScrolling(); - SystemData* systemToView = getCursor()->getSystem(); - if (systemToView->isCustomCollection() && - systemToView->getRootFolder()->getParent()) - ViewController::getInstance()->goToSystemView( - systemToView->getRootFolder()->getParent()->getSystem(), true); - else - ViewController::getInstance()->goToSystemView(systemToView, true); - } - - return true; - } - else if (config->isMappedTo("x", input)) { - if (getCursor()->getType() == PLACEHOLDER) { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - return true; - } - else if (config->isMappedTo("x", input) && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == - ViewController::GAMELIST) { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - // Jump to the randomly selected game. - if (mRandomGame) { - stopListScrolling(); - ViewController::getInstance()->cancelViewTransitions(); - mWindow->startMediaViewer(mRandomGame); - return true; - } - } - else if (mRoot->getSystem()->isGameSystem()) { - stopListScrolling(); - ViewController::getInstance()->cancelViewTransitions(); - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - mWindow->startMediaViewer(getCursor()); - return true; - } - } - else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) { - if (Settings::getInstance()->getBool("QuickSystemSelect") && - SystemData::sSystemVector.size() > 1) { - onPauseVideo(); - onFocusLost(); - stopListScrolling(); - ViewController::getInstance()->goToNextGamelist(); - return true; - } - } - else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) { - if (Settings::getInstance()->getBool("QuickSystemSelect") && - SystemData::sSystemVector.size() > 1) { - onPauseVideo(); - onFocusLost(); - stopListScrolling(); - ViewController::getInstance()->goToPrevGamelist(); - return true; - } - } - else if (Settings::getInstance()->getBool("RandomAddButton") && - (config->isMappedTo("leftthumbstickclick", input) || - config->isMappedTo("rightthumbstickclick", input))) { - if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER) { - stopListScrolling(); - // Jump to a random game. - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - FileData* randomGame = getCursor()->getSystem()->getRandomGame(getCursor()); - if (randomGame) - setCursor(randomGame); - return true; - } - } - else if (config->isMappedTo("y", input) && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - !CollectionSystemsManager::getInstance()->isEditing() && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST) { - // Jump to the randomly selected game. - if (mRandomGame) { - NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND); - // If there is already an mCursorStackHistory entry for the collection, then - // remove it so we don't get multiple entries. - std::vector listEntries = - mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay(); - for (auto it = mCursorStackHistory.begin(); it != mCursorStackHistory.end(); ++it) { - if (std::find(listEntries.begin(), listEntries.end(), *it) != - listEntries.end()) { - mCursorStackHistory.erase(it); - break; - } - } - setCursor(mRandomGame); - updateHelpPrompts(); - } - else { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - } - } - else if (config->isMappedTo("y", input) && - !Settings::getInstance()->getBool("FavoritesAddButton") && - !CollectionSystemsManager::getInstance()->isEditing()) { - return true; - } - else if (config->isMappedTo("y", input) && - !UIModeController::getInstance()->isUIModeKid() && - !UIModeController::getInstance()->isUIModeKiosk()) { - // Notify the user if attempting to add a custom collection to a custom collection. - if (CollectionSystemsManager::getInstance()->isEditing() && - mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && - getCursor()->getParent()->getPath() == "collections") { - NavigationSounds::getInstance().playThemeNavigationSound(FAVORITESOUND); - mWindow->queueInfoPopup("CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS", 4000); - } - // Notify the user if attempting to add a placeholder to a custom collection. - if (CollectionSystemsManager::getInstance()->isEditing() && - mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) { - NavigationSounds::getInstance().playThemeNavigationSound(FAVORITESOUND); - mWindow->queueInfoPopup("CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000); - } - else if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && - getCursor()->getParent()->getPath() != "collections") { - if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER) - NavigationSounds::getInstance().playThemeNavigationSound(FAVORITESOUND); - // When marking or unmarking a game as favorite, don't jump to the new position - // it gets after the gamelist sorting. Instead retain the cursor position in the - // list using the logic below. - FileData* entryToUpdate = getCursor(); - SystemData* system = getCursor()->getSystem(); - bool favoritesSorting; - bool removedLastFavorite = false; - bool selectLastEntry = false; - bool isEditing = CollectionSystemsManager::getInstance()->isEditing(); - bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop"); - // If the current list only contains folders, then treat it as if the folders - // are not sorted on top, this way the logic should work exactly as for mixed - // lists or files-only lists. - if (getCursor()->getType() == FOLDER && foldersOnTop == true) - foldersOnTop = !getCursor()->getParent()->getOnlyFoldersFlag(); - - if (mRoot->getSystem()->isCustomCollection() || - mRoot->getSystem()->getThemeFolder() == "custom-collections") - favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); - else - favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); - - if (favoritesSorting && mRoot->getSystem()->getName() != "recent" && !isEditing) { - FileData* entryToSelect; - // Add favorite flag. - if (!getCursor()->getFavorite()) { - // If it's a folder and folders are sorted on top, select the current entry. - if (foldersOnTop && getCursor()->getType() == FOLDER) { - entryToSelect = getCursor(); - } - // If it's the first entry to be marked as favorite, select the next entry. - else if (getCursor() == getFirstEntry()) { - entryToSelect = getNextEntry(); - } - else if (getCursor() == getLastEntry() && - getPreviousEntry()->getFavorite()) { - entryToSelect = getLastEntry(); - selectLastEntry = true; - } - // If we are on the favorite marking boundary, select the next entry. - else if (getCursor()->getFavorite() != getPreviousEntry()->getFavorite()) { - entryToSelect = getNextEntry(); - } - // If we mark the second entry as favorite and the first entry is not a - // favorite, then select this entry if they are of the same type. - else if (getPreviousEntry() == getFirstEntry() && - getCursor()->getType() == getPreviousEntry()->getType()) { - entryToSelect = getPreviousEntry(); - } - // For all other scenarios try to select the next entry, and if it doesn't - // exist, select the previous entry. - else { - entryToSelect = - getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry(); - } - } - // Remove favorite flag. - else { - // If it's a folder and folders are sorted on top, select the current entry. - if (foldersOnTop && getCursor()->getType() == FOLDER) { - entryToSelect = getCursor(); - } - // If it's the last entry, select the previous entry. - else if (getCursor() == getLastEntry()) { - entryToSelect = getPreviousEntry(); - } - // If we are on the favorite marking boundary, select the previous entry, - // unless folders are sorted on top and the previous entry is a folder. - else if (foldersOnTop && - getCursor()->getFavorite() != getNextEntry()->getFavorite()) { - entryToSelect = getPreviousEntry()->getType() == FOLDER ? - getCursor() : - getPreviousEntry(); - } - // If we are on the favorite marking boundary, select the previous entry. - else if (getCursor()->getFavorite() != getNextEntry()->getFavorite()) { - entryToSelect = getPreviousEntry(); - } - // For all other scenarios try to select the next entry, and if it doesn't - // exist, select the previous entry. - else { - entryToSelect = - getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry(); - } - - // If we removed the last favorite marking, set the flag to jump to the - // first list entry after the sorting has been performed. - if (foldersOnTop && getCursor() == getFirstGameEntry() && - !getNextEntry()->getFavorite()) - removedLastFavorite = true; - else if (getCursor() == getFirstEntry() && !getNextEntry()->getFavorite()) - removedLastFavorite = true; - } - - setCursor(entryToSelect); - system = entryToUpdate->getSystem(); - } - - // Marking folders as favorites don't make them part of any collections, - // so it makes more sense to handle it here than to add the function to - // CollectionSystemsManager. - if (entryToUpdate->getType() == FOLDER) { - if (isEditing) { - mWindow->queueInfoPopup("CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS", 4000); - } - else { - MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata; - if (md->get("favorite") == "false") { - md->set("favorite", "true"); - mWindow->queueInfoPopup( - "MARKED FOLDER '" + - Utils::String::toUpper(Utils::String::removeParenthesis( - entryToUpdate->getName())) + - "' AS FAVORITE", - 4000); - } - else { - md->set("favorite", "false"); - mWindow->queueInfoPopup( - "REMOVED FAVORITE MARKING FOR FOLDER '" + - Utils::String::toUpper(Utils::String::removeParenthesis( - entryToUpdate->getName())) + - "'", - 4000); - } - } - - entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint(); - - getCursor()->getParent()->sort( - mRoot->getSortTypeFromString(mRoot->getSortTypeString()), - Settings::getInstance()->getBool("FavoritesFirst")); - - ViewController::getInstance()->onFileChanged(getCursor(), false); - - // Always jump to the first entry in the gamelist if the last favorite - // was unmarked. We couldn't do this earlier as we didn't have the list - // sorted yet. - if (removedLastFavorite) { - ViewController::getInstance() - ->getGamelistView(entryToUpdate->getSystem()) - ->setCursor(ViewController::getInstance() - ->getGamelistView(entryToUpdate->getSystem()) - ->getFirstEntry()); - } - return true; - } - else if (isEditing && entryToUpdate->metadata.get("nogamecount") == "true") { - mWindow->queueInfoPopup("CAN'T ADD ENTRIES THAT ARE NOT COUNTED " - "AS GAMES TO CUSTOM COLLECTIONS", - 4000); - } - else if (CollectionSystemsManager::getInstance()->toggleGameInCollection( - entryToUpdate)) { - // As the toggling of the game destroyed this object, we need to get the view - // from ViewController instead of using the reference that existed before the - // destruction. Otherwise we get random crashes. - IGamelistView* view = - ViewController::getInstance()->getGamelistView(system).get(); - // Jump to the first entry in the gamelist if the last favorite was unmarked. - if (foldersOnTop && removedLastFavorite && - !entryToUpdate->getSystem()->isCustomCollection()) { - ViewController::getInstance() - ->getGamelistView(entryToUpdate->getSystem()) - ->setCursor(ViewController::getInstance() - ->getGamelistView(entryToUpdate->getSystem()) - ->getFirstGameEntry()); - } - else if (removedLastFavorite && - !entryToUpdate->getSystem()->isCustomCollection()) { - view->setCursor(view->getFirstEntry()); - } - else if (selectLastEntry) { - view->setCursor(view->getLastEntry()); - } - // Display the indication icons which show what games are part of the - // custom collection currently being edited. This is done cheaply using - // onFileChanged() which will trigger populateList(). - if (isEditing) { - for (auto it = SystemData::sSystemVector.begin(); - it != SystemData::sSystemVector.end(); ++it) { - ViewController::getInstance()->getGamelistView((*it))->onFileChanged( - ViewController::getInstance()->getGamelistView((*it))->getCursor(), - false); - } - } - return true; - } - } - else if (config->isMappedTo("y", input) && getCursor()->isPlaceHolder()) { - NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - } - } - } - - return IGamelistView::input(config, input); -} - -void ISimpleGamelistView::generateGamelistInfo(FileData* cursor, FileData* firstEntry) -{ - // Generate data needed for the gamelistInfo field, which is displayed from the - // gamelist interfaces (Detailed/Video/Grid). - mIsFiltered = false; - mIsFolder = false; - FileData* rootFolder = firstEntry->getSystem()->getRootFolder(); - - std::pair gameCount; - FileFilterIndex* idx = rootFolder->getSystem()->getIndex(); - - // For the 'recent' collection we need to recount the games as the collection was - // trimmed down to 50 items. If we don't do this, the game count will not be correct - // as it would include all the games prior to trimming. - if (mRoot->getPath() == "recent") - mRoot->countGames(gameCount); - - gameCount = rootFolder->getGameCount(); - - mGameCount = gameCount.first; - mFavoritesGameCount = gameCount.second; - mFilteredGameCount = 0; - mFilteredGameCountAll = 0; - - if (idx->isFiltered()) { - mIsFiltered = true; - mFilteredGameCount = - static_cast(rootFolder->getFilesRecursive(GAME, true, false).size()); - // Also count the games that are set to not be counted as games, as the filter may - // apply to such entries as well and this will be indicated with a separate '+ XX' - // in the GamelistInfo field. - mFilteredGameCountAll = - static_cast(rootFolder->getFilesRecursive(GAME, true, true).size()); - } - - if (firstEntry->getParent() && firstEntry->getParent()->getType() == FOLDER) - mIsFolder = true; -} - -void ISimpleGamelistView::generateFirstLetterIndex(const std::vector& files) -{ - std::string firstChar; - - bool onlyFavorites = true; - bool onlyFolders = true; - bool hasFavorites = false; - bool hasFolders = false; - bool favoritesSorting = false; - - mFirstLetterIndex.clear(); - - if (files.size() > 0 && files.front()->getSystem()->isCustomCollection()) - favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); - else - favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); - - bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop"); - - // Find out if there are only favorites and/or only folders in the list. - for (auto it = files.begin(); it != files.end(); ++it) { - if (!((*it)->getFavorite())) - onlyFavorites = false; - if (!((*it)->getType() == FOLDER)) - onlyFolders = false; - } - - // Build the index. - for (auto it = files.begin(); it != files.end(); ++it) { - if ((*it)->getType() == FOLDER && (*it)->getFavorite() && favoritesSorting && - !onlyFavorites) { - hasFavorites = true; - } - else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders) { - hasFolders = true; - } - else if ((*it)->getType() == GAME && (*it)->getFavorite() && favoritesSorting && - !onlyFavorites) { - hasFavorites = true; - } - else { - mFirstLetterIndex.push_back(Utils::String::getFirstCharacter((*it)->getSortName())); - } - } - - // Sort and make each entry unique. - std::sort(mFirstLetterIndex.begin(), mFirstLetterIndex.end()); - auto last = std::unique(mFirstLetterIndex.begin(), mFirstLetterIndex.end()); - mFirstLetterIndex.erase(last, mFirstLetterIndex.end()); - - // If there are any favorites and/or folders in the list, insert their respective - // Unicode characters at the beginning of the vector. - if (hasFavorites) - mFirstLetterIndex.insert(mFirstLetterIndex.begin(), ViewController::FAVORITE_CHAR); - - if (hasFolders) - mFirstLetterIndex.insert(mFirstLetterIndex.begin(), ViewController::FOLDER_CHAR); -} diff --git a/es-app/src/views/legacygamelists/ISimpleGamelistView.h b/es-app/src/views/legacygamelists/ISimpleGamelistView.h deleted file mode 100644 index 8cf07e680..000000000 --- a/es-app/src/views/legacygamelists/ISimpleGamelistView.h +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// ISimpleGamelistView.h -// -// Interface that defines a simple gamelist view. -// - -#ifndef ES_APP_VIEWS_GAMELIST_ISIMPLE_GAMELIST_VIEW_H -#define ES_APP_VIEWS_GAMELIST_ISIMPLE_GAMELIST_VIEW_H - -#include "components/ImageComponent.h" -#include "components/TextComponent.h" -#include "views/gamelist/IGamelistView.h" - -#include - -class ISimpleGamelistView : public IGamelistView -{ -public: - ISimpleGamelistView(Window* window, FileData* root); - virtual ~ISimpleGamelistView(); - - // Called when a FileData* is added, has its metadata changed, or is removed. - void onFileChanged(FileData* file, bool reloadGamelist) override; - - // Called whenever the theme changes. - void onThemeChanged(const std::shared_ptr& theme) override; - - virtual FileData* getCursor() override = 0; - virtual void setCursor(FileData*) override = 0; - virtual void addPlaceholder(FileData*) override = 0; - - bool input(InputConfig* config, Input input) override; - virtual void launch(FileData* game) override = 0; - - virtual const std::vector& getFirstLetterIndex() override = 0; - - // These functions are used to retain the folder cursor history, for instance - // during a view reload. The calling function stores the history temporarily. - void copyCursorHistory(std::vector& cursorHistory) override - { - cursorHistory = mCursorStackHistory; - }; - void populateCursorHistory(std::vector& cursorHistory) override - { - mCursorStackHistory = cursorHistory; - }; - -protected: - virtual std::string getQuickSystemSelectRightButton() = 0; - virtual std::string getQuickSystemSelectLeftButton() = 0; - virtual void populateList(const std::vector& files, FileData* firstEntry) = 0; - - void generateGamelistInfo(FileData* cursor, FileData* firstEntry); - void generateFirstLetterIndex(const std::vector& files); - - TextComponent mHeaderText; - ImageComponent mHeaderImage; - ImageComponent mBackground; - - std::vector mThemeExtras; - std::stack mCursorStack; - std::vector mCursorStackHistory; - // This game is randomly selected in the grouped custom collections view. - FileData* mRandomGame; - - std::vector mFirstLetterIndex; - - unsigned int mGameCount; - unsigned int mFavoritesGameCount; - unsigned int mFilteredGameCount; - unsigned int mFilteredGameCountAll; - bool mIsFiltered; - bool mIsFolder; -}; - -#endif // ES_APP_VIEWS_GAMELIST_ISIMPLE_GAMELIST_VIEW_H diff --git a/es-app/src/views/legacygamelists/VideoGamelistView.cpp b/es-app/src/views/legacygamelists/VideoGamelistView.cpp deleted file mode 100644 index fe693c2b8..000000000 --- a/es-app/src/views/legacygamelists/VideoGamelistView.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// VideoGamelistView.cpp -// -// Interface that defines a GamelistView of the type 'video'. -// - -#include "views/gamelist/VideoGamelistView.h" - -#include "CollectionSystemsManager.h" -#include "SystemData.h" -#include "animations/LambdaAnimation.h" -#include "components/VideoFFmpegComponent.h" -#include "utils/FileSystemUtil.h" -#include "views/ViewController.h" - -#define FADE_IN_START_OPACITY 0.5f -#define FADE_IN_TIME 650 - -VideoGamelistView::VideoGamelistView(Window* window, FileData* root) - : BasicGamelistView(window, root) - , mThumbnail(window) - , mMarquee(window) - , mImage(window) - , mVideo(nullptr) - , mLblRating(window) - , mLblReleaseDate(window) - , mLblDeveloper(window) - , mLblPublisher(window) - , mLblGenre(window) - , mLblPlayers(window) - , mLblLastPlayed(window) - , mLblPlayCount(window) - , mRating(window) - , mReleaseDate(window) - , mDeveloper(window) - , mPublisher(window) - , mGenre(window) - , mPlayers(window) - , mLastPlayed(window) - , mPlayCount(window) - , mName(window) - , mBadges(window) - , mDescContainer(window) - , mDescription(window) - , mGamelistInfo(window) - , mVideoPlaying(false) - , mLastUpdated(nullptr) -{ - const float padding = 0.01f; - - // Create the video window. - mVideo = new VideoFFmpegComponent(window); - - mList.setPosition(mSize.x * (0.50f + padding), mList.getPosition().y); - mList.setSize(mSize.x * (0.50f - padding), mList.getSize().y); - mList.setAlignment(TextListComponent::ALIGN_LEFT); - mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - - // Thumbnail. - mThumbnail.setOrigin(0.5f, 0.5f); - mThumbnail.setPosition(2.0f, 2.0f); - mThumbnail.setVisible(false); - mThumbnail.setMaxSize(mSize.x * (0.25f - 2.0f * padding), mSize.y * 0.10f); - mThumbnail.setDefaultZIndex(35.0f); - addChild(&mThumbnail); - - // Marquee. - mMarquee.setOrigin(0.5f, 0.5f); - mMarquee.setPosition(mSize.x * 0.25f, mSize.y * 0.10f); - mMarquee.setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f); - mMarquee.setDefaultZIndex(35.0f); - addChild(&mMarquee); - - // Video. - mVideo->setOrigin(0.5f, 0.5f); - mVideo->setPosition(mSize.x * 0.25f, mSize.y * 0.4f); - mVideo->setSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.4f); - mVideo->setDefaultZIndex(30.0f); - addChild(mVideo); - - // Metadata labels + values. - mLblRating.setText("Rating: ", false); - addChild(&mLblRating); - addChild(&mRating); - mLblReleaseDate.setText("Released: ", false); - addChild(&mLblReleaseDate); - addChild(&mReleaseDate); - mLblDeveloper.setText("Developer: ", false); - addChild(&mLblDeveloper); - addChild(&mDeveloper); - mLblPublisher.setText("Publisher: ", false); - addChild(&mLblPublisher); - addChild(&mPublisher); - mLblGenre.setText("Genre: ", false); - addChild(&mLblGenre); - addChild(&mGenre); - mLblPlayers.setText("Players: ", false); - addChild(&mLblPlayers); - addChild(&mPlayers); - mLblLastPlayed.setText("Last played: ", false); - addChild(&mLblLastPlayed); - mLastPlayed.setDisplayRelative(true); - addChild(&mLastPlayed); - mLblPlayCount.setText("Times played: ", false); - addChild(&mLblPlayCount); - addChild(&mPlayCount); - - // Badges. - addChild(&mBadges); - mBadges.setOrigin(0.5f, 0.5f); - mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f); - mBadges.setSize(mSize.x * 0.15f, mSize.y * 0.2f); - mBadges.setDefaultZIndex(50.0f); - - mName.setPosition(mSize.x, mSize.y); - mName.setDefaultZIndex(40.0f); - mName.setColor(0xAAAAAAFF); - mName.setFont(Font::get(FONT_SIZE_MEDIUM)); - mName.setHorizontalAlignment(ALIGN_CENTER); - addChild(&mName); - - mDescContainer.setPosition(mSize.x * padding, mSize.y * 0.65f); - mDescContainer.setSize(mSize.x * (0.50f - 2.0f * padding), - mSize.y - mDescContainer.getPosition().y); - mDescContainer.setAutoScroll(true); - mDescContainer.setDefaultZIndex(40.0f); - addChild(&mDescContainer); - - mDescription.setFont(Font::get(FONT_SIZE_SMALL)); - mDescription.setSize(mDescContainer.getSize().x, 0.0f); - mDescContainer.addChild(&mDescription); - - mGamelistInfo.setOrigin(0.5f, 0.5f); - mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL)); - mGamelistInfo.setDefaultZIndex(50.0f); - mGamelistInfo.setVisible(true); - addChild(&mGamelistInfo); - - initMDLabels(); - initMDValues(); -} - -VideoGamelistView::~VideoGamelistView() { delete mVideo; } - -void VideoGamelistView::onThemeChanged(const std::shared_ptr& theme) -{ - BasicGamelistView::onThemeChanged(theme); - - using namespace ThemeFlags; - mThumbnail.applyTheme(theme, getName(), "md_thumbnail", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mMarquee.applyTheme(theme, getName(), "md_marquee", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mImage.applyTheme(theme, getName(), "md_image", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); - mVideo->applyTheme(theme, getName(), "md_video", - POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | - VISIBLE); - mName.applyTheme(theme, getName(), "md_name", ALL); - mBadges.applyTheme(theme, getName(), "md_badges", ALL); - - initMDLabels(); - std::vector labels = getMDLabels(); - assert(labels.size() == 8); - std::vector lblElements = { - "md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", - "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"}; - - for (unsigned int i = 0; i < labels.size(); ++i) - labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); - - initMDValues(); - std::vector values = getMDValues(); - assert(values.size() == 8); - std::vector valElements = {"md_rating", "md_releasedate", "md_developer", - "md_publisher", "md_genre", "md_players", - "md_lastplayed", "md_playcount"}; - - for (unsigned int i = 0; i < values.size(); ++i) - values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); - - mDescContainer.applyTheme(theme, getName(), "md_description", - POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); - mDescription.setSize(mDescContainer.getSize().x, 0.0f); - mDescription.applyTheme( - theme, getName(), "md_description", - ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); - - mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); - // If there is no position defined in the theme for gamelistInfo, then hide it. - if (mGamelistInfo.getPosition() == glm::vec3 {}) - mGamelistInfo.setVisible(false); - else - mGamelistInfo.setVisible(true); - - sortChildren(); -} - -void VideoGamelistView::initMDLabels() -{ - std::vector components = getMDLabels(); - - const unsigned int colCount = 2; - const unsigned int rowCount = static_cast(components.size() / 2); - - glm::vec3 start {mSize.x * 0.01f, mSize.y * 0.625f, 0.0f}; - - const float colSize = (mSize.x * 0.48f) / colCount; - const float rowPadding = 0.01f * mSize.y; - - for (unsigned int i = 0; i < components.size(); ++i) { - const unsigned int row = i % rowCount; - glm::vec3 pos {}; - if (row == 0) { - pos = start + glm::vec3 {colSize * (i / rowCount), 0.0f, 0.0f}; - } - else { - // Work from the last component. - GuiComponent* lc = components[i - 1]; - pos = lc->getPosition() + glm::vec3 {0.0f, lc->getSize().y + rowPadding, 0.0f}; - } - - components[i]->setFont(Font::get(FONT_SIZE_SMALL)); - components[i]->setPosition(pos); - components[i]->setDefaultZIndex(40.0f); - } -} - -void VideoGamelistView::initMDValues() -{ - std::vector labels = getMDLabels(); - std::vector values = getMDValues(); - - std::shared_ptr defaultFont = Font::get(FONT_SIZE_SMALL); - mRating.setSize(defaultFont->getHeight() * 5.0f, static_cast(defaultFont->getHeight())); - mReleaseDate.setFont(defaultFont); - mDeveloper.setFont(defaultFont); - mPublisher.setFont(defaultFont); - mGenre.setFont(defaultFont); - mPlayers.setFont(defaultFont); - mLastPlayed.setFont(defaultFont); - mPlayCount.setFont(defaultFont); - - float bottom = 0.0f; - - const float colSize = (mSize.x * 0.48f) / 2.0f; - for (unsigned int i = 0; i < labels.size(); ++i) { - const float heightDiff = (labels[i]->getSize().y - values[i]->getSize().y) / 2.0f; - values[i]->setPosition(labels[i]->getPosition() + - glm::vec3 {labels[i]->getSize().x, heightDiff, 0.0f}); - values[i]->setSize(colSize - labels[i]->getSize().x, values[i]->getSize().y); - values[i]->setDefaultZIndex(40.0f); - - float testBot = values[i]->getPosition().y + values[i]->getSize().y; - - if (testBot > bottom) - bottom = testBot; - } - - mDescContainer.setPosition(mDescContainer.getPosition().x, bottom + mSize.y * 0.01f); - mDescContainer.setSize(mDescContainer.getSize().x, mSize.y - mDescContainer.getPosition().y); -} - -void VideoGamelistView::updateInfoPanel() -{ - FileData* file = (mList.size() == 0 || mList.isScrolling()) ? nullptr : mList.getSelected(); - - // If the game data has already been rendered to the info panel, then skip it this time. - if (file == mLastUpdated) - return; - - if (!mList.isScrolling()) - mLastUpdated = file; - - bool hideMetaDataFields = false; - - if (file) { - // Always hide the metadata fields if browsing grouped custom collections. - if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) - hideMetaDataFields = true; - else - hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); - - // Always hide the metadata fields for placeholders as well. - if (file->getType() == PLACEHOLDER) { - hideMetaDataFields = true; - mLastUpdated = nullptr; - } - } - - // If we're scrolling, hide the metadata fields if the last game had this options set, - // or if we're in the grouped custom collection view. - if (mList.isScrolling()) - if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || - (mLastUpdated->getSystem()->isCustomCollection() && - mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) - hideMetaDataFields = true; - - if (hideMetaDataFields) { - mLblRating.setVisible(false); - mRating.setVisible(false); - mLblReleaseDate.setVisible(false); - mReleaseDate.setVisible(false); - mLblDeveloper.setVisible(false); - mDeveloper.setVisible(false); - mLblPublisher.setVisible(false); - mPublisher.setVisible(false); - mLblGenre.setVisible(false); - mGenre.setVisible(false); - mLblPlayers.setVisible(false); - mPlayers.setVisible(false); - mLblLastPlayed.setVisible(false); - mLastPlayed.setVisible(false); - mLblPlayCount.setVisible(false); - mPlayCount.setVisible(false); - mBadges.setVisible(false); - } - else { - mLblRating.setVisible(true); - mRating.setVisible(true); - mLblReleaseDate.setVisible(true); - mReleaseDate.setVisible(true); - mLblDeveloper.setVisible(true); - mDeveloper.setVisible(true); - mLblPublisher.setVisible(true); - mPublisher.setVisible(true); - mLblGenre.setVisible(true); - mGenre.setVisible(true); - mLblPlayers.setVisible(true); - mPlayers.setVisible(true); - mLblLastPlayed.setVisible(true); - mLastPlayed.setVisible(true); - mLblPlayCount.setVisible(true); - mPlayCount.setVisible(true); - mBadges.setVisible(true); - } - - bool fadingOut = false; - if (file == nullptr) { - mVideoPlaying = false; - fadingOut = true; - } - else { - // If we're browsing a grouped custom collection, then update the folder metadata - // which will generate a description of three random games and return a pointer to - // the first of these so that we can display its game media. - if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) { - mRandomGame = CollectionSystemsManager::getInstance()->updateCollectionFolderMetadata( - file->getSystem()); - if (mRandomGame) { - mThumbnail.setImage(mRandomGame->getThumbnailPath()); - mMarquee.setImage(mRandomGame->getMarqueePath(), false, true); - mVideo->setImage(mRandomGame->getImagePath()); - // 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 video file) as - // the previously set video. - // That may happen when entering a folder with the same name as the first game - // file inside, or as in this case, when entering a custom collection. - mVideo->onHide(); - - if (!mVideo->setVideo(mRandomGame->getVideoPath())) - mVideo->setDefaultVideo(); - } - else { - mThumbnail.setImage(""); - mMarquee.setImage(""); - mVideo->setImage(""); - mVideo->setVideo(""); - mVideo->setDefaultVideo(); - } - } - else { - mThumbnail.setImage(file->getThumbnailPath()); - mMarquee.setImage(file->getMarqueePath(), false, true); - mVideo->setImage(file->getImagePath()); - mVideo->onHide(); - - if (!mVideo->setVideo(file->getVideoPath())) - mVideo->setDefaultVideo(); - } - - mVideoPlaying = true; - - // Populate the gamelistInfo field which shows an icon if a folder has been entered - // as well as the game count for the entire system (total and favorites separately). - // If a filter has been applied, then the number of filtered and total games replaces - // the game counter. - std::string gamelistInfoString; - Alignment infoAlign = mGamelistInfo.getHorizontalAlignment(); - - if (mIsFolder && infoAlign == ALIGN_RIGHT) - gamelistInfoString = ViewController::FOLDER_CHAR + " "; - - if (mIsFiltered) { - if (mFilteredGameCountAll == mFilteredGameCount) - gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " / " + - std::to_string(mGameCount); - else - gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " + " + - std::to_string(mFilteredGameCountAll - mFilteredGameCount) + - " / " + std::to_string(mGameCount); - } - else { - gamelistInfoString += - ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount); - if (!(file->getSystem()->isCollection() && - file->getSystem()->getFullName() == "favorites")) - gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + - std::to_string(mFavoritesGameCount); - } - - if (mIsFolder && infoAlign != ALIGN_RIGHT) - gamelistInfoString += " " + ViewController::FOLDER_CHAR; - - mGamelistInfo.setValue(gamelistInfoString); - - // Fade in the game image. - auto func = [this](float t) { - mVideo->setOpacity(static_cast( - glm::mix(static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); - }; - mVideo->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); - - mDescription.setText(file->metadata.get("desc")); - mDescContainer.reset(); - - mRating.setValue(file->metadata.get("rating")); - mReleaseDate.setValue(file->metadata.get("releasedate")); - mDeveloper.setValue(file->metadata.get("developer")); - mPublisher.setValue(file->metadata.get("publisher")); - mGenre.setValue(file->metadata.get("genre")); - mPlayers.setValue(file->metadata.get("players")); - - // Populate the badge slots based on game metadata. - std::vector badgeSlots; - for (auto badge : mBadges.getBadgeTypes()) { - BadgeComponent::BadgeInfo badgeInfo; - badgeInfo.badgeType = badge; - if (badge == "controller") { - if (file->metadata.get("controller").compare("") != 0) { - badgeInfo.gameController = file->metadata.get("controller"); - badgeSlots.push_back(badgeInfo); - } - } - else if (badge == "altemulator") { - if (file->metadata.get(badge).compare("") != 0) - badgeSlots.push_back(badgeInfo); - } - else { - if (file->metadata.get(badge).compare("true") == 0) - badgeSlots.push_back(badgeInfo); - } - } - mBadges.setBadges(badgeSlots); - - mName.setValue(file->metadata.get("name")); - - if (file->getType() == GAME) { - if (!hideMetaDataFields) { - mLastPlayed.setValue(file->metadata.get("lastplayed")); - mPlayCount.setValue(file->metadata.get("playcount")); - } - } - else if (file->getType() == FOLDER) { - if (!hideMetaDataFields) { - mLastPlayed.setValue(file->metadata.get("lastplayed")); - mLblPlayCount.setVisible(false); - mPlayCount.setVisible(false); - } - } - - fadingOut = false; - } - - std::vector comps = getMDValues(); - comps.push_back(&mThumbnail); - comps.push_back(&mMarquee); - comps.push_back(mVideo); - comps.push_back(&mDescription); - comps.push_back(&mName); - comps.push_back(&mBadges); - std::vector labels = getMDLabels(); - comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - - for (auto it = comps.cbegin(); it != comps.cend(); ++it) { - GuiComponent* comp = *it; - // An animation is playing, then animate if reverse != fadingOut. - // An animation is not playing, then animate if opacity != our target opacity - if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || - (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { - auto func = [comp](float t) { - comp->setOpacity(static_cast(glm::mix(0.0f, 1.0f, t) * 255)); - }; - comp->setAnimation(new LambdaAnimation(func, 200), 0, nullptr, fadingOut); - } - } -} - -void VideoGamelistView::launch(FileData* game) -{ - ViewController::getInstance()->triggerGameLaunch(game); -} - -std::vector VideoGamelistView::getMDLabels() -{ - std::vector ret; - ret.push_back(&mLblRating); - ret.push_back(&mLblReleaseDate); - ret.push_back(&mLblDeveloper); - ret.push_back(&mLblPublisher); - ret.push_back(&mLblGenre); - ret.push_back(&mLblPlayers); - ret.push_back(&mLblLastPlayed); - ret.push_back(&mLblPlayCount); - return ret; -} - -std::vector VideoGamelistView::getMDValues() -{ - std::vector ret; - ret.push_back(&mRating); - ret.push_back(&mReleaseDate); - ret.push_back(&mDeveloper); - ret.push_back(&mPublisher); - ret.push_back(&mGenre); - ret.push_back(&mPlayers); - ret.push_back(&mLastPlayed); - ret.push_back(&mPlayCount); - return ret; -} - -void VideoGamelistView::update(int deltaTime) -{ - if (!mVideoPlaying) - mVideo->onHide(); - else if (mVideoPlaying && !mVideo->isVideoPaused() && !mWindow->isScreensaverActive()) - mVideo->onShow(); - - BasicGamelistView::update(deltaTime); - mVideo->update(deltaTime); - - if (ViewController::getInstance()->getGameLaunchTriggered() && mVideo->isAnimationPlaying(0)) - mVideo->finishAnimation(0); -} - -void VideoGamelistView::onShow() -{ - // Reset any Lottie animations. - for (auto extra : mThemeExtras) - extra->resetFileAnimation(); - - mLastUpdated = nullptr; - GuiComponent::onShow(); - updateInfoPanel(); -} diff --git a/es-app/src/views/legacygamelists/VideoGamelistView.h b/es-app/src/views/legacygamelists/VideoGamelistView.h deleted file mode 100644 index ea4c91674..000000000 --- a/es-app/src/views/legacygamelists/VideoGamelistView.h +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// VideoGamelistView.h -// -// Interface that defines a GamelistView of the type 'video'. -// - -#ifndef ES_APP_VIEWS_GAMELIST_VIDEO_GAMELIST_VIEW_H -#define ES_APP_VIEWS_GAMELIST_VIDEO_GAMELIST_VIEW_H - -#include "components/BadgeComponent.h" -#include "components/DateTimeComponent.h" -#include "components/RatingComponent.h" -#include "components/ScrollableContainer.h" -#include "views/gamelist/BasicGamelistView.h" - -class VideoComponent; - -class VideoGamelistView : public BasicGamelistView -{ -public: - VideoGamelistView(Window* window, FileData* root); - virtual ~VideoGamelistView(); - - void onShow() override; - void onThemeChanged(const std::shared_ptr& theme) override; - std::string getName() const override { return "video"; } - void launch(FileData* game) override; - - void preloadGamelist() override { updateInfoPanel(); } - -protected: - void update(int deltaTime) override; - -private: - void updateInfoPanel(); - - void initMDLabels(); - void initMDValues(); - - ImageComponent mThumbnail; - ImageComponent mMarquee; - ImageComponent mImage; - VideoComponent* mVideo; - - TextComponent mLblRating; - TextComponent mLblReleaseDate; - TextComponent mLblDeveloper; - TextComponent mLblPublisher; - TextComponent mLblGenre; - TextComponent mLblPlayers; - TextComponent mLblLastPlayed; - TextComponent mLblPlayCount; - - RatingComponent mRating; - DateTimeComponent mReleaseDate; - TextComponent mDeveloper; - TextComponent mPublisher; - TextComponent mGenre; - TextComponent mPlayers; - DateTimeComponent mLastPlayed; - TextComponent mPlayCount; - TextComponent mName; - BadgeComponent mBadges; - - std::vector getMDLabels(); - std::vector getMDValues(); - - ScrollableContainer mDescContainer; - TextComponent mDescription; - TextComponent mGamelistInfo; - - bool mVideoPlaying; - FileData* mLastUpdated; -}; - -#endif // ES_APP_VIEWS_GAMELIST_VIDEO_GAMELIST_VIEW_H diff --git a/es-core/src/components/GridTileComponent.cpp b/es-core/src/components/GridTileComponent.cpp deleted file mode 100644 index c5a74eec2..000000000 --- a/es-core/src/components/GridTileComponent.cpp +++ /dev/null @@ -1,322 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// GridTileComponent.cpp -// -// X*Y tile grid, used indirectly by GridGamelistView via ImageGridComponent. -// - -#include "GridTileComponent.h" - -#include "ThemeData.h" -#include "animations/LambdaAnimation.h" -#include "resources/TextureResource.h" - -GridTileComponent::GridTileComponent() - : mBackground {":/graphics/frame.png"} -{ - mDefaultProperties.mSize = getDefaultTileSize(); - mDefaultProperties.mPadding = glm::vec2 {16.0f * Renderer::getScreenWidthModifier(), - 16.0f * Renderer::getScreenHeightModifier()}; - mDefaultProperties.mImageColor = 0xAAAAAABB; - // Attempting to use frame.svg instead causes quite severe performance problems. - mDefaultProperties.mBackgroundImage = ":/graphics/frame.png"; - mDefaultProperties.mBackgroundCornerSize = glm::vec2 {16.0f, 16.0f}; - mDefaultProperties.mBackgroundCenterColor = 0xAAAAEEFF; - mDefaultProperties.mBackgroundEdgeColor = 0xAAAAEEFF; - - mSelectedProperties.mSize = getSelectedTileSize(); - mSelectedProperties.mPadding = mDefaultProperties.mPadding; - mSelectedProperties.mImageColor = 0xFFFFFFFF; - mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage; - mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize; - mSelectedProperties.mBackgroundCenterColor = 0xFFFFFFFF; - mSelectedProperties.mBackgroundEdgeColor = 0xFFFFFFFF; - - mImage = std::make_shared(); - mImage->setOrigin(0.5f, 0.5f); - - mBackground.setOrigin(0.5f, 0.5f); - - addChild(&mBackground); - addChild(&(*mImage)); - - mSelectedZoomPercent = 0; - mSelected = false; - mVisible = false; - - setSelected(false, false); - setVisible(true); -} - -void GridTileComponent::render(const glm::mat4& parentTrans) -{ - glm::mat4 trans {getTransform() * parentTrans}; - - if (mVisible) - renderChildren(trans); -} - -// Update all the tile properties to the new status (selected or default). -void GridTileComponent::update(int deltaTime) -{ - GuiComponent::update(deltaTime); - - calcCurrentProperties(); - - mBackground.setImagePath(mCurrentProperties.mBackgroundImage); - - mImage->setColorShift(mCurrentProperties.mImageColor); - mBackground.setCenterColor(mCurrentProperties.mBackgroundCenterColor); - mBackground.setEdgeColor(mCurrentProperties.mBackgroundEdgeColor); - - resize(); -} - -void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties) -{ - glm::vec2 screen {Renderer::getScreenWidth(), Renderer::getScreenHeight()}; - - if (elem->has("size")) - properties->mSize = elem->get("size") * screen; - - if (elem->has("padding")) - properties->mPadding = elem->get("padding"); - - if (elem->has("imageColor")) - properties->mImageColor = elem->get("imageColor"); - - if (elem->has("backgroundImage")) - properties->mBackgroundImage = elem->get("backgroundImage"); - - if (elem->has("backgroundCornerSize")) - properties->mBackgroundCornerSize = elem->get("backgroundCornerSize"); - - if (elem->has("backgroundColor")) { - properties->mBackgroundCenterColor = elem->get("backgroundColor"); - properties->mBackgroundEdgeColor = elem->get("backgroundColor"); - } - - if (elem->has("backgroundCenterColor")) - properties->mBackgroundCenterColor = elem->get("backgroundCenterColor"); - - if (elem->has("backgroundEdgeColor")) - properties->mBackgroundEdgeColor = elem->get("backgroundEdgeColor"); -} - -void GridTileComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& /*element*/, - unsigned int /*properties*/) -{ - // Apply theme to the default gridtile. - const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile"); - if (elem) - applyThemeToProperties(elem, &mDefaultProperties); - - // Apply theme to the selected gridtile. Note that some of the default gridtile - // properties have influence on the selected gridtile properties. - // See THEMES.md for more information. - elem = theme->getElement(view, "selected", "gridtile"); - - mSelectedProperties.mSize = getSelectedTileSize(); - mSelectedProperties.mPadding = mDefaultProperties.mPadding; - mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage; - mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize; - - if (elem) - applyThemeToProperties(elem, &mSelectedProperties); -} - -glm::vec2 GridTileComponent::getDefaultTileSize() -{ - glm::vec2 screen {glm::vec2(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()))}; - - return screen * 0.22f; -} - -glm::vec2 GridTileComponent::getSelectedTileSize() const -{ - // Return the tile size. - return mDefaultProperties.mSize * 1.2f; -} - -bool GridTileComponent::isSelected() const -{ - // Return whether the tile is selected. - return mSelected; -} - -void GridTileComponent::setImageOLD(const std::string& path) -{ - mImage->setImage(path); - - // Resize now to prevent flickering images when scrolling. - resize(); -} - -void GridTileComponent::setImageOLD(const std::shared_ptr& texture) -{ - mImage->setImage(texture); - - // Resize now to prevent flickering images when scrolling. - resize(); -} - -void GridTileComponent::setSelected(bool selected, - bool allowAnimation, - glm::vec3* pPosition, - bool force) -{ - if (mSelected == selected && !force) - return; - - mSelected = selected; - - if (selected) { - if (pPosition == nullptr || !allowAnimation) { - cancelAnimation(3); - - this->setSelectedZoom(1); - mAnimPosition = {}; - - resize(); - } - else { - mAnimPosition = glm::vec3 {pPosition->x, pPosition->y, pPosition->z}; - - auto func = [this](float t) { - t -= 1; - float pct = glm::mix(0.0f, 1.0f, t * t * t + 1.0f); - this->setSelectedZoom(pct); - }; - - cancelAnimation(3); - setAnimation( - new LambdaAnimation(func, 250), 0, - [this] { - this->setSelectedZoom(1); - mAnimPosition = {}; - }, - false, 3); - } - } - else { - if (!allowAnimation) { - cancelAnimation(3); - this->setSelectedZoom(0); - - resize(); - } - else { - this->setSelectedZoom(1); - - auto func = [this](float t) { - t -= 1.0f; - float pct = glm::mix(0.0f, 1.0f, t * t * t + 1.0f); - this->setSelectedZoom(1.0f - pct); - }; - - cancelAnimation(3); - setAnimation( - new LambdaAnimation(func, 250), 0, [this] { this->setSelectedZoom(0); }, false, 3); - } - } -} - -void GridTileComponent::setSelectedZoom(float percent) -{ - if (mSelectedZoomPercent == percent) - return; - - mSelectedZoomPercent = percent; - resize(); -} - -void GridTileComponent::setVisible(bool visible) { mVisible = visible; } - -void GridTileComponent::resize() -{ - calcCurrentProperties(); - - mImage->setMaxSize(mCurrentProperties.mSize - mCurrentProperties.mPadding * 2.0f); - mBackground.setCornerSize(mCurrentProperties.mBackgroundCornerSize); - mBackground.fitTo(mCurrentProperties.mSize - mBackground.getCornerSize() * 2.0f); -} - -unsigned int mixColors(unsigned int first, unsigned int second, float percent) -{ - unsigned char alpha0 = (first >> 24) & 0xFF; - unsigned char blue0 = (first >> 16) & 0xFF; - unsigned char green0 = (first >> 8) & 0xFF; - unsigned char red0 = first & 0xFF; - - unsigned char alpha1 = (second >> 24) & 0xFF; - unsigned char blue1 = (second >> 16) & 0xFF; - unsigned char green1 = (second >> 8) & 0xFF; - unsigned char red1 = second & 0xFF; - - unsigned char alpha = static_cast(alpha0 * (1.0 - percent) + alpha1 * percent); - unsigned char blue = static_cast(blue0 * (1.0 - percent) + blue1 * percent); - unsigned char green = static_cast(green0 * (1.0 - percent) + green1 * percent); - unsigned char red = static_cast(red0 * (1.0 - percent) + red1 * percent); - - return (alpha << 24) | (blue << 16) | (green << 8) | red; -} - -void GridTileComponent::calcCurrentProperties() -{ - mCurrentProperties = mSelected ? mSelectedProperties : mDefaultProperties; - - float zoomPercentInverse = 1.0f - mSelectedZoomPercent; - - if (mSelectedZoomPercent != 0.0f && mSelectedZoomPercent != 1.0f) { - if (mDefaultProperties.mSize != mSelectedProperties.mSize) - mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse + - mSelectedProperties.mSize * mSelectedZoomPercent; - - if (mDefaultProperties.mPadding != mSelectedProperties.mPadding) - mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse + - mSelectedProperties.mPadding * mSelectedZoomPercent; - - if (mDefaultProperties.mImageColor != mSelectedProperties.mImageColor) - mCurrentProperties.mImageColor = - mixColors(mDefaultProperties.mImageColor, mSelectedProperties.mImageColor, - mSelectedZoomPercent); - - if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize) - mCurrentProperties.mBackgroundCornerSize = - mDefaultProperties.mBackgroundCornerSize * zoomPercentInverse + - mSelectedProperties.mBackgroundCornerSize * mSelectedZoomPercent; - - if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor) - mCurrentProperties.mBackgroundCenterColor = - mixColors(mDefaultProperties.mBackgroundCenterColor, - mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent); - - if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor) - mCurrentProperties.mBackgroundEdgeColor = - mixColors(mDefaultProperties.mBackgroundEdgeColor, - mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent); - } -} - -glm::vec3 GridTileComponent::getBackgroundPosition() -{ - return mBackground.getPosition() + mPosition; -} - -std::shared_ptr GridTileComponent::getTexture() -{ - if (mImage != nullptr) - return mImage->getTexture(); - - return nullptr; -} - -void GridTileComponent::forceSize(glm::vec2 size, float selectedZoom) -{ - mDefaultProperties.mSize = size; - mSelectedProperties.mSize = size * selectedZoom; -} diff --git a/es-core/src/components/GridTileComponent.h b/es-core/src/components/GridTileComponent.h deleted file mode 100644 index 1347b5006..000000000 --- a/es-core/src/components/GridTileComponent.h +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// GridTileComponent.h -// -// X*Y tile grid, used indirectly by GridGamelistView via ImageGridComponent. -// - -#ifndef ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H -#define ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H - -#include "ImageComponent.h" -#include "NinePatchComponent.h" - -struct GridTileProperties { - glm::vec2 mSize; - glm::vec2 mPadding; - unsigned int mImageColor; - std::string mBackgroundImage; - glm::vec2 mBackgroundCornerSize; - unsigned int mBackgroundCenterColor; - unsigned int mBackgroundEdgeColor; -}; - -class GridTileComponent : public GuiComponent -{ -public: - GridTileComponent(); - - void render(const glm::mat4& parentTrans) override; - void applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& element, - unsigned int properties) override; - - // Made this a static function because the ImageGridComponent needs to know the default tile - // max size to calculate the grid dimension before it instantiates the GridTileComponents. - static glm::vec2 getDefaultTileSize(); - glm::vec2 getSelectedTileSize() const; - bool isSelected() const; - - void reset() { setImageOLD(""); } - - void setImageOLD(const std::string& path); - void setImageOLD(const std::shared_ptr& texture); - void setSelected(bool selected, - bool allowAnimation = true, - glm::vec3* pPosition = nullptr, - bool force = false); - void setVisible(bool visible); - - void forceSize(glm::vec2 size, float selectedZoom); - - glm::vec3 getBackgroundPosition(); - - void update(int deltaTime) override; - - std::shared_ptr getTexture(); - -private: - void resize(); - void calcCurrentProperties(); - void setSelectedZoom(float percent); - - std::shared_ptr mImage; - NinePatchComponent mBackground; - - GridTileProperties mDefaultProperties; - GridTileProperties mSelectedProperties; - GridTileProperties mCurrentProperties; - - float mSelectedZoomPercent; - bool mSelected; - bool mVisible; - - glm::vec3 mAnimPosition; -}; - -#endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H diff --git a/es-core/src/components/ImageGridComponent.h b/es-core/src/components/ImageGridComponent.h deleted file mode 100644 index c4ce3bcbd..000000000 --- a/es-core/src/components/ImageGridComponent.h +++ /dev/null @@ -1,726 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// ImageGridComponent.cpp -// -// X*Y image grid, used by GridGamelistView. -// - -#ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H -#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H - -#include "GridTileComponent.h" -#include "Log.h" -#include "animations/LambdaAnimation.h" -#include "components/IList.h" -#include "resources/TextureResource.h" - -#define EXTRAITEMS 2 - -enum ScrollDirection { - SCROLL_VERTICALLY, - SCROLL_HORIZONTALLY -}; - -enum ImageSource { - THUMBNAIL, - IMAGE, - MIXIMAGE, - SCREENSHOT, - COVER, - MARQUEE, - BOX3D -}; - -struct ImageGridData { - std::string texturePath; -}; - -template class ImageGridComponent : public IList -{ -protected: - using IList::mEntries; - using IList::mScrollTier; - using IList::listUpdate; - using IList::listInput; - using IList::listRenderTitleOverlay; - using IList::getTransform; - using IList::mSize; - using IList::mCursor; - using IList::mWindow; - using IList::IList; - -public: - using IList::size; - using IList::isScrolling; - using IList::stopScrolling; - - ImageGridComponent(); - - void add(const std::string& name, const std::string& imagePath, const T& obj); - - bool input(InputConfig* config, Input input) override; - void update(int deltaTime) override; - void render(const glm::mat4& parentTrans) override; - virtual void applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& element, - unsigned int properties) override; - - void onSizeChanged() override; - void setCursorChangedCallback(const std::function& func) - { - mCursorChangedCallback = func; - } - - ImageSource getImageSource() { return mImageSource; } - -protected: - void onCursorChanged(const CursorState& state) override; - -private: - // Tiles. - void buildTiles(); - void updateTiles(bool ascending = true, - bool allowAnimation = true, - bool updateSelectedState = true); - void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState); - void calcGridDimension(); - bool isScrollLoop(); - - bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; } - - // Images and entries. - bool mEntriesDirty; - int mLastCursor; - std::string mDefaultGameTexture; - std::string mDefaultFolderTexture; - - // Tiles. - bool mLastRowPartial; - glm::vec2 mAutoLayout; - float mAutoLayoutZoom; - - glm::vec4 mPadding; - glm::vec2 mMargin; - glm::vec2 mTileSize; - glm::ivec2 mGridDimension; - std::shared_ptr mTheme; - std::vector> mTiles; - - int mStartPosition; - - float mCamera; - float mCameraDirection; - - // Miscellaneous. - bool mAnimate; - bool mCenterSelection; - bool mScrollLoop; - ScrollDirection mScrollDirection; - ImageSource mImageSource; - std::function mCursorChangedCallback; -}; - -template ImageGridComponent::ImageGridComponent() -{ - glm::vec2 screen {Renderer::getScreenWidth(), Renderer::getScreenHeight()}; - - mCamera = 0.0f; - mCameraDirection = 1.0f; - - mAutoLayout = glm::vec2 {}; - mAutoLayoutZoom = 1.0f; - - mStartPosition = 0; - - mEntriesDirty = true; - mLastCursor = 0; - mDefaultGameTexture = ":/graphics/cartridge.svg"; - mDefaultFolderTexture = ":/graphics/folder.svg"; - - mSize = screen * 0.80f; - mMargin = screen * 0.07f; - mPadding = {}; - mTileSize = GridTileComponent::getDefaultTileSize(); - - mAnimate = true; - mCenterSelection = false; - mScrollLoop = false; - mScrollDirection = SCROLL_VERTICALLY; - mImageSource = THUMBNAIL; -} - -template -void ImageGridComponent::add(const std::string& name, const std::string& imagePath, const T& obj) -{ - typename IList::Entry entry; - entry.name = name; - entry.object = obj; - entry.data.texturePath = imagePath; - - static_cast*>(this)->add(entry); - mEntriesDirty = true; -} - -template bool ImageGridComponent::input(InputConfig* config, Input input) -{ - if (input.value != 0) { - int idx = isVertical() ? 0 : 1; - - glm::ivec2 dir {}; - if (config->isMappedLike("up", input)) - dir[1 ^ idx] = -1; - else if (config->isMappedLike("down", input)) - dir[1 ^ idx] = 1; - else if (config->isMappedLike("left", input)) - dir[0 ^ idx] = -1; - else if (config->isMappedLike("right", input)) - dir[0 ^ idx] = 1; - - if (dir != glm::ivec2 {}) { - if (isVertical()) - listInput(dir.x + dir.y * mGridDimension.x); - else - listInput(dir.x + dir.y * mGridDimension.y); - return true; - } - } - else { - if (config->isMappedLike("up", input) || config->isMappedLike("down", input) || - config->isMappedLike("left", input) || config->isMappedLike("right", input)) { - stopScrolling(); - } - } - - return GuiComponent::input(config, input); -} - -template void ImageGridComponent::update(int deltaTime) -{ - GuiComponent::update(deltaTime); - listUpdate(deltaTime); - - for (auto it = mTiles.begin(); it != mTiles.end(); ++it) - (*it)->update(deltaTime); -} - -template void ImageGridComponent::render(const glm::mat4& parentTrans) -{ - glm::mat4 trans {getTransform() * parentTrans}; - glm::mat4 tileTrans {trans}; - - float offsetX {isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x + mMargin.x)}; - float offsetY {isVertical() ? mCamera * mCameraDirection * (mTileSize.y + mMargin.y) : 0.0f}; - - tileTrans = glm::translate(tileTrans, glm::vec3 {offsetX, offsetY, 0.0f}); - - if (mEntriesDirty) { - updateTiles(); - mEntriesDirty = false; - } - - // Create a clipRect to hide tiles used to buffer texture loading. - float scaleX = trans[0].x; - float scaleY = trans[1].y; - - glm::ivec2 pos {static_cast(std::round(trans[3].x)), - static_cast(std::round(trans[3].y))}; - glm::ivec2 size {static_cast(std::round(mSize.x * scaleX)), - static_cast(std::round(mSize.y * scaleY))}; - - Renderer::pushClipRect(pos, size); - - // Render all the tiles but the selected one. - std::shared_ptr selectedTile = nullptr; - for (auto it = mTiles.begin(); it != mTiles.end(); ++it) { - std::shared_ptr tile = (*it); - // If it's the selected image, keep it for later, otherwise render it now. - if (tile->isSelected()) - selectedTile = tile; - else - tile->render(tileTrans); - } - - Renderer::popClipRect(); - - // Render the selected image on top of the others. - if (selectedTile != nullptr) - selectedTile->render(tileTrans); - - listRenderTitleOverlay(trans); - - GuiComponent::renderChildren(trans); -} - -template -void ImageGridComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& element, - unsigned int properties) -{ - // Apply theme to GuiComponent but not the size property, which will be applied - // at the end of this function. - GuiComponent::applyTheme(theme, view, element, properties ^ ThemeFlags::SIZE); - - // Keep the theme pointer to apply it on the tiles later on. - mTheme = theme; - - glm::vec2 screen {static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())}; - - const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid"); - if (elem) { - if (elem->has("margin")) - mMargin = elem->get("margin") * screen; - - if (elem->has("padding")) - mPadding = elem->get("padding") * - glm::vec4 {screen.x, screen.y, screen.x, screen.y}; - - if (elem->has("autoLayout")) - mAutoLayout = elem->get("autoLayout"); - - if (elem->has("autoLayoutSelectedZoom")) - mAutoLayoutZoom = elem->get("autoLayoutSelectedZoom"); - - if (elem->has("imageSource")) { - auto direction = elem->get("imageSource"); - if (direction == "image") - mImageSource = IMAGE; - else if (direction == "miximage") - mImageSource = MIXIMAGE; - else if (direction == "screenshot") - mImageSource = SCREENSHOT; - else if (direction == "cover") - mImageSource = COVER; - else if (direction == "marquee") - mImageSource = MARQUEE; - else if (direction == "3dbox") - mImageSource = BOX3D; - else - mImageSource = THUMBNAIL; - } - else { - mImageSource = THUMBNAIL; - } - - if (elem->has("scrollDirection")) - mScrollDirection = - (ScrollDirection)(elem->get("scrollDirection") == "horizontal"); - - if (elem->has("centerSelection")) { - mCenterSelection = (elem->get("centerSelection")); - - if (elem->has("scrollLoop")) - mScrollLoop = (elem->get("scrollLoop")); - } - - if (elem->has("animate")) - mAnimate = (elem->get("animate")); - else - mAnimate = true; - - if (elem->has("gameImage")) { - std::string path = elem->get("gameImage"); - - if (!ResourceManager::getInstance().fileExists(path)) { - LOG(LogWarning) << "Could not replace default game image, check path: " << path; - } - else { - std::string oldDefaultGameTexture = mDefaultGameTexture; - mDefaultGameTexture = path; - - // mEntries are already loaded at this point, so we need to update them with - // the new game image texture. - for (auto it = mEntries.begin(); it != mEntries.end(); ++it) { - if ((*it).data.texturePath == oldDefaultGameTexture) - (*it).data.texturePath = mDefaultGameTexture; - } - } - } - - if (elem->has("folderImage")) { - std::string path = elem->get("folderImage"); - - if (!ResourceManager::getInstance().fileExists(path)) { - LOG(LogWarning) << "Could not replace default folder image, check path: " << path; - } - else { - std::string oldDefaultFolderTexture = mDefaultFolderTexture; - mDefaultFolderTexture = path; - - // mEntries are already loaded at this point, so we need to update them with - // the new folder image texture. - for (auto it = mEntries.begin(); it != mEntries.end(); ++it) { - if ((*it).data.texturePath == oldDefaultFolderTexture) - (*it).data.texturePath = mDefaultFolderTexture; - } - } - } - } - - // We still need to manually get the grid tile size here, so we can recalculate the new - // grid dimension, and then (re)build the tiles. - elem = theme->getElement(view, "default", "gridtile"); - - mTileSize = elem && elem->has("size") ? elem->get("size") * screen : - GridTileComponent::getDefaultTileSize(); - - // Apply size property which will trigger a call to onSizeChanged() which will build the tiles. - GuiComponent::applyTheme(theme, view, element, ThemeFlags::SIZE); - - // Trigger the call manually if the theme has no "imagegrid" element. - if (!elem) - buildTiles(); -} - -template void ImageGridComponent::onSizeChanged() -{ - buildTiles(); - updateTiles(); -} - -template void ImageGridComponent::onCursorChanged(const CursorState& state) -{ - if (mLastCursor == mCursor) { - if (state == CURSOR_STOPPED && mCursorChangedCallback) - mCursorChangedCallback(state); - return; - } - - bool direction = mCursor >= mLastCursor; - int diff = direction ? mCursor - mLastCursor : mLastCursor - mCursor; - if (isScrollLoop() && diff == static_cast(mEntries.size()) - 1) - direction = !direction; - - int oldStart = mStartPosition; - - int dimScrollable = (isVertical() ? mGridDimension.y : mGridDimension.x) - 2 * EXTRAITEMS; - int dimOpposite = isVertical() ? mGridDimension.x : mGridDimension.y; - - int centralCol = static_cast((static_cast(dimScrollable) - 0.5f) / 2.0f); - int maxCentralCol = dimScrollable / 2; - - int oldCol = (mLastCursor / dimOpposite); - int col = (mCursor / dimOpposite); - - int lastCol = static_cast((mEntries.size() - 1) / dimOpposite); - - int lastScroll = std::max(0, (lastCol + 1 - dimScrollable)); - - float startPos = 0; - float endPos = 1; - - if (reinterpret_cast(this)->isAnimationPlaying(2)) { - startPos = 0; - reinterpret_cast(this)->cancelAnimation(2); - updateTiles(direction, false, false); - } - - if (mAnimate) { - std::shared_ptr oldTile = nullptr; - std::shared_ptr newTile = nullptr; - - int oldIdx = mLastCursor - mStartPosition + (dimOpposite * EXTRAITEMS); - if (oldIdx >= 0 && oldIdx < static_cast(mTiles.size())) - oldTile = mTiles[oldIdx]; - - int newIdx = mCursor - mStartPosition + (dimOpposite * EXTRAITEMS); - if (isScrollLoop()) { - if (newIdx < 0) - newIdx += static_cast(mEntries.size()); - else if (newIdx >= static_cast(mTiles.size())) - newIdx -= static_cast(mEntries.size()); - } - - if (newIdx >= 0 && newIdx < static_cast(mTiles.size())) - newTile = mTiles[newIdx]; - - for (auto it = mTiles.begin(); it != mTiles.end(); ++it) { - if ((*it)->isSelected() && *it != oldTile && *it != newTile) { - startPos = 0; - (*it)->setSelected(false, false, nullptr); - } - } - - glm::vec3 oldPos {}; - - if (oldTile != nullptr && oldTile != newTile) { - oldPos = oldTile->getBackgroundPosition(); - oldTile->setSelected(false, true, nullptr, true); - } - - if (newTile != nullptr) - newTile->setSelected(true, true, oldPos == glm::vec3 {} ? nullptr : &oldPos, true); - } - - int firstVisibleCol = mStartPosition / dimOpposite; - - if ((col < centralCol || (col == 0 && col == centralCol)) && !mCenterSelection) { - mStartPosition = 0; - } - else if ((col - centralCol) > lastScroll && !mCenterSelection && !isScrollLoop()) { - mStartPosition = lastScroll * dimOpposite; - } - else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) || - col == firstVisibleCol + centralCol) { - if (col == firstVisibleCol + maxCentralCol) - mStartPosition = (col - maxCentralCol) * dimOpposite; - else - mStartPosition = (col - centralCol) * dimOpposite; - } - else { - if (oldCol == firstVisibleCol + maxCentralCol) - mStartPosition = (col - maxCentralCol) * dimOpposite; - else - mStartPosition = (col - centralCol) * dimOpposite; - } - - auto lastCursor = mLastCursor; - mLastCursor = mCursor; - - mCameraDirection = direction ? -1.0f : 1.0f; - mCamera = 0; - - if (lastCursor < 0 || !mAnimate) { - updateTiles(direction, mAnimate && (lastCursor >= 0 || isScrollLoop())); - - if (mCursorChangedCallback) - mCursorChangedCallback(state); - - return; - } - - if (mCursorChangedCallback) - mCursorChangedCallback(state); - - bool moveCamera = (oldStart != mStartPosition); - - auto func = [this, startPos, endPos, moveCamera](float t) { - if (!moveCamera) - return; - - t -= 1.0f; - float pct = glm::mix(0.0f, 1.0f, t * t * t + 1.0f); - t = startPos * (1.0f - pct) + endPos * pct; - mCamera = t; - }; - - reinterpret_cast(this)->setAnimation( - new LambdaAnimation(func, 250), 0, - [this, direction] { - mCamera = 0; - updateTiles(direction, false); - }, - false, 2); -} - -// Create and position tiles (mTiles). -template void ImageGridComponent::buildTiles() -{ - mStartPosition = 0; - mTiles.clear(); - - calcGridDimension(); - - if (mCenterSelection) { - int dimScrollable = (isVertical() ? mGridDimension.y : mGridDimension.x) - 2 * EXTRAITEMS; - mStartPosition -= static_cast(floorf(dimScrollable / 2.0f)); - } - - glm::vec2 tileDistance {mTileSize + mMargin}; - - if (mAutoLayout.x != 0.0f && mAutoLayout.y != 0.0f) { - auto x = (mSize.x - (mMargin.x * (mAutoLayout.x - 1.0f)) - mPadding.x - mPadding.z) / - static_cast(mAutoLayout.x); - auto y = (mSize.y - (mMargin.y * (mAutoLayout.y - 1.0f)) - mPadding.y - mPadding.w) / - static_cast(mAutoLayout.y); - - mTileSize = glm::vec2 {x, y}; - tileDistance = mTileSize + mMargin; - } - - bool vert = isVertical(); - glm::vec2 startPosition {mTileSize / 2.0f}; - startPosition.x += mPadding.x; - startPosition.y += mPadding.y; - - int X; - int Y; - - // Layout tile size and position. - for (int y = 0; y < (vert ? mGridDimension.y : mGridDimension.x); ++y) { - for (int x = 0; x < (vert ? mGridDimension.x : mGridDimension.y); ++x) { - // Create tiles. - auto tile = std::make_shared(); - - // In Vertical mode, tiles are ordered from left to right, then from top to bottom. - // In Horizontal mode, tiles are ordered from top to bottom, then from left to right. - X = vert ? x : y - EXTRAITEMS; - Y = vert ? y - EXTRAITEMS : x; - - tile->setPosition(X * tileDistance.x + startPosition.x, - Y * tileDistance.y + startPosition.y); - tile->setOrigin(0.5f, 0.5f); - tile->setImage(""); - - if (mTheme) - tile->applyTheme(mTheme, "grid", "gridtile", ThemeFlags::ALL); - - if (mAutoLayout.x != 0 && mAutoLayout.y != 0.0f) - tile->forceSize(mTileSize, mAutoLayoutZoom); - - mTiles.push_back(tile); - } - } -} - -template -void ImageGridComponent::updateTiles(bool ascending, - bool allowAnimation, - bool updateSelectedState) -{ - if (!mTiles.size()) - return; - - // Stop updating the tiles at highest scroll speed. - if (mScrollTier == 3) { - for (int ti = 0; ti < static_cast(mTiles.size()); ++ti) { - std::shared_ptr tile = mTiles.at(ti); - - tile->setSelected(false); - tile->setImage(mDefaultGameTexture); - tile->setVisible(false); - } - return; - } - - // Temporarily store the previous textures so that they can't be unloaded. - std::vector> previousTextures; - for (int ti = 0; ti < static_cast(mTiles.size()); ++ti) { - std::shared_ptr tile = mTiles.at(ti); - previousTextures.push_back(tile->getTexture()); - } - - // If going down, update from top to bottom. - // If going up, update from bottom to top. - int scrollDirection = ascending ? 1 : -1; - int ti = ascending ? 0 : static_cast(mTiles.size()) - 1; - int end = ascending ? static_cast(mTiles.size()) : -1; - int img = mStartPosition + ti; - - img -= EXTRAITEMS * (isVertical() ? mGridDimension.x : mGridDimension.y); - - // Update the tiles. - while (ti != end) { - updateTileAtPos(ti, img, allowAnimation, updateSelectedState); - - ti += scrollDirection; - img += scrollDirection; - } - - if (updateSelectedState) - mLastCursor = mCursor; - - mLastCursor = mCursor; -} - -template -void ImageGridComponent::updateTileAtPos(int tilePos, - int imgPos, - bool allowAnimation, - bool updateSelectedState) -{ - std::shared_ptr tile = mTiles.at(tilePos); - - if (isScrollLoop()) { - if (imgPos < 0) - imgPos += static_cast(mEntries.size()); - else if (imgPos >= size()) - imgPos -= static_cast(mEntries.size()); - } - - // If we have more tiles than we can display on screen, hide them. - // Same for tiles out of the buffer. - if (imgPos < 0 || imgPos >= size() || tilePos < 0 || - tilePos >= static_cast(mTiles.size())) { - if (updateSelectedState) - tile->setSelected(false, allowAnimation); - tile->reset(); - tile->setVisible(false); - } - else { - tile->setVisible(true); - - std::string imagePath = mEntries.at(imgPos).data.texturePath; - - if (ResourceManager::getInstance().fileExists(imagePath)) - tile->setImage(imagePath); - else if (mEntries.at(imgPos).object->getType() == 2) - tile->setImage(mDefaultFolderTexture); - else - tile->setImage(mDefaultGameTexture); - - if (updateSelectedState) { - if (imgPos == mCursor && mCursor != mLastCursor) { - int dif = mCursor - tilePos; - int idx = mLastCursor - dif; - - if (idx < 0 || idx >= static_cast(mTiles.size())) - idx = 0; - - glm::vec3 pos {mTiles.at(idx)->getBackgroundPosition()}; - tile->setSelected(true, allowAnimation, &pos); - } - else { - tile->setSelected(imgPos == mCursor, allowAnimation); - } - } - } -} - -// Calculate how many tiles of size mTileSize we can fit in a grid of size mSize using -// a margin size of mMargin. -template void ImageGridComponent::calcGridDimension() -{ - // grid_size = columns * tile_size + (columns - 1) * margin - // <=> columns = (grid_size + margin) / (tile_size + margin) - glm::vec2 gridDimension {(mSize + mMargin) / (mTileSize + mMargin)}; - - if (mAutoLayout.x != 0.0f && mAutoLayout.y != 0.0f) - gridDimension = mAutoLayout; - - mLastRowPartial = floorf(gridDimension.y) != gridDimension.y; - - // Ceil y dim so we can display partial last row. - mGridDimension = glm::ivec2 {static_cast(gridDimension.x), - static_cast(ceilf(gridDimension.y))}; - - // Grid dimension validation. - if (mGridDimension.x < 1) { - LOG(LogError) << "Theme defined grid X dimension below 1"; - } - if (mGridDimension.y < 1) { - LOG(LogError) << "Theme defined grid Y dimension below 1"; - } - - // Add extra tiles to both sides. - if (isVertical()) - mGridDimension.y += 2 * EXTRAITEMS; - else - mGridDimension.x += 2 * EXTRAITEMS; -} - -template bool ImageGridComponent::isScrollLoop() -{ - if (!mScrollLoop) - return false; - if (isVertical()) - return (mGridDimension.x * (mGridDimension.y - 2 * EXTRAITEMS)) <= - static_cast(mEntries.size()); - return (mGridDimension.y * (mGridDimension.x - 2 * EXTRAITEMS)) <= - static_cast(mEntries.size()); -} - -#endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H