From 7f0aede27446f031f44b15e07935d93358b78e35 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 30 Oct 2020 14:19:21 +0100 Subject: [PATCH] Added gamelist info field which displays some useful information. --- NEWS.md | 1 + THEMES.md | 8 ++++ USERGUIDE.md | 1 + es-app/src/CollectionSystemManager.cpp | 9 ++++ .../src/views/gamelist/BasicGameListView.cpp | 10 +++-- es-app/src/views/gamelist/BasicGameListView.h | 2 +- .../views/gamelist/DetailedGameListView.cpp | 33 +++++++++++++- .../src/views/gamelist/DetailedGameListView.h | 1 + .../src/views/gamelist/GridGameListView.cpp | 43 +++++++++++++++---- es-app/src/views/gamelist/GridGameListView.h | 1 + .../views/gamelist/ISimpleGameListView.cpp | 36 ++++++++++++++++ .../src/views/gamelist/ISimpleGameListView.h | 8 ++++ .../src/views/gamelist/VideoGameListView.cpp | 33 +++++++++++++- es-app/src/views/gamelist/VideoGameListView.h | 1 + es-core/src/ThemeData.cpp | 16 +++---- themes/rbsimple-DE/colors.xml | 3 ++ themes/rbsimple-DE/theme.xml | 16 ++++++- 17 files changed, 197 insertions(+), 25 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9e1aa0d9a..1b07233e8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -46,6 +46,7 @@ Many bugs have been fixed, and numerous features that were only partially implem * Improved input device configuration, and default keyboard mappings are now applied if the keyboard has not been configured by the user * GUI-configurable option to sort favorite games above non-favorite games (favorites marked with stars) * GUI-configurable option to sort folders on top of the gamelists +* Added a gamelist info text field displaying the game count, any applied filters as well as an icon if a folder has been entered * Expanded the metadata for folders and made it possible to mark them as favorites * Added new component GuiComplexTextEditPopup to handle changes to configuration file entries and similar * Speed improvements and optimizations, the application now starts faster and feels more responsive diff --git a/THEMES.md b/THEMES.md index a1ab841f0..1faf31ebf 100644 --- a/THEMES.md +++ b/THEMES.md @@ -366,6 +366,8 @@ Example `navigationsounds.xml`, to be included from the main theme file: * System Logo/Text - 50 * `text name="logoText"` * `image name="logo"` +* Gamelist information - 50 + * `text name="gamelistInfo"` ### Theme variables @@ -431,6 +433,8 @@ Reference - A header image. If a non-empty `path` is specified, `text name="logoText"` will be hidden and this image will be, by default, displayed roughly in its place. * `textlist name="gamelist"` - ALL - The gamelist. `primaryColor` is for games, `secondaryColor` is for folders. Left aligned by default. +* `text name="gamelistInfo"` - ALL + - Displays the game count (all games as well as favorites), any applied filters, and an folder icon if a folder has been entered. * Metadata * Labels @@ -480,6 +484,8 @@ Reference - A header image. If a non-empty `path` is specified, `text name="logoText"` will be hidden and this image will be, by default, displayed roughly in its place. * `textlist name="gamelist"` - ALL - The gamelist. `primaryColor` is for games, `secondaryColor` is for folders. Left aligned by default. +* `text name="gamelistInfo"` - ALL + - Displays the game count (all games as well as favorites), any applied filters, and an folder icon if a folder has been entered. * Metadata * Labels @@ -539,6 +545,8 @@ Reference - Note that many of the default gridtile parameters change the selected gridtile parameters if they are not explicitly set by the theme. For example, changing the background image of the default gridtile also change the background image of the selected gridtile. Refer to the gridtile documentation for more informations. * `gridtile name="selected"` - ALL - See default gridtile description right above. +* `text name="gamelistInfo"` - ALL + - Displays the game count (all games as well as favorites), any applied filters, and an folder icon if a folder has been entered. * Metadata * Labels diff --git a/USERGUIDE.md b/USERGUIDE.md index 2558f1db0..f43dcb037 100644 --- a/USERGUIDE.md +++ b/USERGUIDE.md @@ -70,6 +70,7 @@ It's possible to manually set a specific gamelist view style in the UI settings In additions to the styles just described, there is a **Grid** view style as well, but as of version 1.0.0 this is very limited and not recommended. Future versions of EmulationStation may update this style to a more useful state. +If the theme supports it, there's a gamelist information field displayed in the gamelist view, showing the number of games for the system (total and favorites) as well as a folder icon if a folder has been entered. When applying any filters to the gamelist, the game counter is replaced with the amount of games filtered (as in 'filtered / total games'). This functionality is specific to EmulationStation Desktop Edition so older themes will not support this. ## Help system diff --git a/es-app/src/CollectionSystemManager.cpp b/es-app/src/CollectionSystemManager.cpp index 949ca9b56..40c27527d 100644 --- a/es-app/src/CollectionSystemManager.cpp +++ b/es-app/src/CollectionSystemManager.cpp @@ -944,6 +944,15 @@ void CollectionSystemManager::populateAutoCollection(CollectionSystemData* sysDa if (sysDecl.type == AUTO_LAST_PLAYED) trimCollectionCount(rootFolder, LAST_PLAYED_MAX); + + // For the 'recent' collection we need to populate the gamelist once more 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 (rootFolder->getName() == "recent") { + ViewController::get()->getGameListView(rootFolder->getSystem()).get()-> + onFileChanged(rootFolder->getChildren().front(), false); + } + sysData->isPopulated = true; } diff --git a/es-app/src/views/gamelist/BasicGameListView.cpp b/es-app/src/views/gamelist/BasicGameListView.cpp index f7ef26511..1eac5d8ad 100644 --- a/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/es-app/src/views/gamelist/BasicGameListView.cpp @@ -51,12 +51,14 @@ void BasicGameListView::onFileChanged(FileData* file, bool reloadGameList) void BasicGameListView::populateList(const std::vector& files) { - firstGameEntry = nullptr; + mFirstGameEntry = nullptr; bool favoriteStar = true; bool isEditing = false; std::string editingCollection; std::string inCollectionPrefix; + generateGamelistInfo(files); + if (CollectionSystemManager::get()->isEditing()) { editingCollection = CollectionSystemManager::get()->getEditingCollection(); isEditing = true; @@ -75,8 +77,8 @@ void BasicGameListView::populateList(const std::vector& files) 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); + 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) { @@ -157,7 +159,7 @@ FileData* BasicGameListView::getLastEntry() FileData* BasicGameListView::getFirstGameEntry() { - return firstGameEntry; + return mFirstGameEntry; } void BasicGameListView::addPlaceholder() diff --git a/es-app/src/views/gamelist/BasicGameListView.h b/es-app/src/views/gamelist/BasicGameListView.h index b80377b03..b1aa8f436 100644 --- a/es-app/src/views/gamelist/BasicGameListView.h +++ b/es-app/src/views/gamelist/BasicGameListView.h @@ -48,7 +48,7 @@ protected: TextListComponent mList; // Points to the first game in the list, i.e. the first entry which is of the type 'GAME'. - FileData* firstGameEntry; + FileData* mFirstGameEntry; std::string FAVORITE_CHAR; std::string FOLDER_CHAR; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index 28f1dae3c..3e5e3b54c 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -22,6 +22,7 @@ DetailedGameListView::DetailedGameListView( : BasicGameListView(window, root), mDescContainer(window), mDescription(window), + mGamelistInfo(window), mThumbnail(window), mMarquee(window), @@ -123,6 +124,12 @@ DetailedGameListView::DetailedGameListView( mDescription.setSize(mDescContainer.getSize().x(), 0); mDescContainer.addChild(&mDescription); + mGamelistInfo.setOrigin(0.5f, 0.5f); + mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL)); + mGamelistInfo.setDefaultZIndex(50); + mGamelistInfo.setVisible(true); + addChild(&mGamelistInfo); + initMDLabels(); initMDValues(); } @@ -168,6 +175,8 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); + sortChildren(); } @@ -268,7 +277,7 @@ void DetailedGameListView::updateInfoPanel() // 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" || + if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || (mLastUpdated->getSystem()->isCustomCollection() && mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) hideMetaDataFields = true; @@ -339,6 +348,28 @@ void DetailedGameListView::updateInfoPanel() 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; + + if (mIsFolder) + gamelistInfoString = "\uF07C "; + + if (mIsFiltered) { + gamelistInfoString += "\uF0b0 " + std::to_string(mFilteredGameCount) + " / " + + std::to_string(mGameCount); + } + else { + gamelistInfoString += "\uF11b " + std::to_string(mGameCount); + if (!(file->getSystem()->isCollection() && + file->getSystem()->getFullName() == "favorites")) + gamelistInfoString += " \uF005 " + std::to_string(mFavoritesGameCount); + } + + mGamelistInfo.setValue(gamelistInfoString); + // Fade in the game image. auto func = [this](float t) { mImage.setOpacity(static_cast(Math::lerp( diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index 8613302a5..f43262ca7 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -58,6 +58,7 @@ private: ScrollableContainer mDescContainer; TextComponent mDescription; + TextComponent mGamelistInfo; FileData* mLastUpdated; }; diff --git a/es-app/src/views/gamelist/GridGameListView.cpp b/es-app/src/views/gamelist/GridGameListView.cpp index cfc44e74f..61458446f 100644 --- a/es-app/src/views/gamelist/GridGameListView.cpp +++ b/es-app/src/views/gamelist/GridGameListView.cpp @@ -33,6 +33,7 @@ GridGameListView::GridGameListView( mDescContainer(window), mDescription(window), + mGamelistInfo(window), mLblRating(window), mLblReleaseDate(window), @@ -55,7 +56,7 @@ GridGameListView::GridGameListView( { const float padding = 0.01f; -// Create the correct type of video window. + // Create the correct type of video window. #if defined(_RPI_) if (Settings::getInstance()->getBool("VideoOmxPlayer")) mVideo = new VideoPlayerComponent(window); @@ -138,6 +139,12 @@ GridGameListView::GridGameListView( mVideo->setVisible(false); addChild(mVideo); + mGamelistInfo.setOrigin(0.5f, 0.5f); + mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL)); + mGamelistInfo.setDefaultZIndex(50); + mGamelistInfo.setVisible(true); + addChild(&mGamelistInfo); + initMDLabels(); initMDValues(); updateInfoPanel(); @@ -241,6 +248,8 @@ void GridGameListView::populateList(const std::vector& files) { firstGameEntry = nullptr; + generateGamelistInfo(files); + mGrid.clear(); mHeaderText.setText(mRoot->getSystem()->getFullName()); if (files.size() > 0) { @@ -303,6 +312,8 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) populateList(mRoot->getChildrenListToDisplay()); mGrid.setCursor(file); + mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); + sortChildren(); } @@ -425,15 +436,29 @@ void GridGameListView::updateInfoPanel() fadingOut = true; } else { -// Temporary fix to disable only audio from playing. -// if (!mVideo->setVideo(file->getVideoPath())) -// mVideo->setDefaultVideo(); - -// mVideoPlaying = true; - -// mVideo->setImage(file->getThumbnailPath()); mMarquee.setImage(file->getMarqueePath()); -// 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; + + if (mIsFiltered) { + gamelistInfoString += "\uF0b0 " + std::to_string(mFilteredGameCount) + " / " + + std::to_string(mGameCount); + } + else { + gamelistInfoString += "\uF11b " + std::to_string(mGameCount); + if (!(file->getSystem()->isCollection() && + file->getSystem()->getFullName() == "favorites")) + gamelistInfoString += " \uF005 " + std::to_string(mFavoritesGameCount); + } + + if (mIsFolder) + gamelistInfoString += " \uF07C"; + + mGamelistInfo.setValue(gamelistInfoString); mDescription.setText(file->metadata.get("desc")); mDescContainer.reset(); diff --git a/es-app/src/views/gamelist/GridGameListView.h b/es-app/src/views/gamelist/GridGameListView.h index 6499a77c6..34836ff60 100644 --- a/es-app/src/views/gamelist/GridGameListView.h +++ b/es-app/src/views/gamelist/GridGameListView.h @@ -89,6 +89,7 @@ private: ScrollableContainer mDescContainer; TextComponent mDescription; + TextComponent mGamelistInfo; }; #endif // ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H diff --git a/es-app/src/views/gamelist/ISimpleGameListView.cpp b/es-app/src/views/gamelist/ISimpleGameListView.cpp index caf755bd1..d520ab6c2 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -13,6 +13,7 @@ #include "views/UIModeController.h" #include "views/ViewController.h" #include "CollectionSystemManager.h" +#include "FileFilterIndex.h" #include "Settings.h" #include "Sound.h" #include "SystemData.h" @@ -337,3 +338,38 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) return IGameListView::input(config, input); } + +void ISimpleGameListView::generateGamelistInfo(const std::vector& files) +{ + // Generate data needed for the gamelistInfo field, which is displayed from the + // gamelist interfaces (Detailed/Video/Grid). + mIsFiltered = false; + mIsFolder = false; + + std::pair gameCount; + FileFilterIndex* idx = mRoot->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); + + if (files.size() > 0 && files.front()->getParent() != mRoot && + files.front()->getSystem()->isGroupedCustomCollection()) + gameCount = files.front()->getSystem()->getRootFolder()->getGameCount(); + else + gameCount = mRoot->getGameCount(); + + mGameCount = gameCount.first + gameCount.second; + mFavoritesGameCount = gameCount.second; + mFilteredGameCount = 0; + + if (idx->isFiltered()) { + mIsFiltered = true; + mFilteredGameCount = mRoot->getFilesRecursive(GAME, true, false).size(); + } + + if (files.size() > 0 && files.front()->getParent() != mRoot) + mIsFolder = true; +} diff --git a/es-app/src/views/gamelist/ISimpleGameListView.h b/es-app/src/views/gamelist/ISimpleGameListView.h index 7be72d45a..b25bdb29a 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/es-app/src/views/gamelist/ISimpleGameListView.h @@ -38,12 +38,20 @@ protected: virtual std::string getQuickSystemSelectLeftButton() = 0; virtual void populateList(const std::vector& files) = 0; + void generateGamelistInfo(const std::vector& files); + TextComponent mHeaderText; ImageComponent mHeaderImage; ImageComponent mBackground; std::vector mThemeExtras; std::stack mCursorStack; + + unsigned int mGameCount; + unsigned int mFavoritesGameCount; + unsigned int mFilteredGameCount; + bool mIsFiltered; + bool mIsFolder; }; #endif // ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 411ca61d4..044731898 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -30,6 +30,7 @@ VideoGameListView::VideoGameListView( : BasicGameListView(window, root), mDescContainer(window), mDescription(window), + mGamelistInfo(window), mThumbnail(window), mMarquee(window), @@ -141,6 +142,12 @@ VideoGameListView::VideoGameListView( mDescription.setSize(mDescContainer.getSize().x(), 0); mDescContainer.addChild(&mDescription); + mGamelistInfo.setOrigin(0.5f, 0.5f); + mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL)); + mGamelistInfo.setDefaultZIndex(50); + mGamelistInfo.setVisible(true); + addChild(&mGamelistInfo); + initMDLabels(); initMDValues(); } @@ -193,6 +200,8 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); + sortChildren(); } @@ -293,7 +302,7 @@ void VideoGameListView::updateInfoPanel() // 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" || + if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || (mLastUpdated->getSystem()->isCustomCollection() && mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) hideMetaDataFields = true; @@ -382,6 +391,28 @@ void VideoGameListView::updateInfoPanel() 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; + + if (mIsFolder) + gamelistInfoString = "\uF07C "; + + if (mIsFiltered) { + gamelistInfoString += "\uF0b0 " + std::to_string(mFilteredGameCount) + " / " + + std::to_string(mGameCount); + } + else { + gamelistInfoString += "\uF11b " + std::to_string(mGameCount); + if (!(file->getSystem()->isCollection() && + file->getSystem()->getFullName() == "favorites")) + gamelistInfoString += " \uF005 " + std::to_string(mFavoritesGameCount); + } + + mGamelistInfo.setValue(gamelistInfoString); + // Fade in the game image. auto func = [this](float t) { mVideo->setOpacity((unsigned char)(Math::lerp( diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index 24fe026dd..32d0b771f 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -65,6 +65,7 @@ private: ScrollableContainer mDescContainer; TextComponent mDescription; + TextComponent mGamelistInfo; bool mVideoPlaying; FileData* mLastUpdated; diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 657a55e3b..3b9ad8d55 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -109,14 +109,14 @@ std::map262626 4d4d4d + + 505050 + diff --git a/themes/rbsimple-DE/theme.xml b/themes/rbsimple-DE/theme.xml index 6b849bc56..ea723710b 100644 --- a/themes/rbsimple-DE/theme.xml +++ b/themes/rbsimple-DE/theme.xml @@ -233,6 +233,13 @@ based on: 'recalbox-multi' by the Recalbox community left 0.01 + + ./core/fonts/Exo2-BoldCondensed.otf + 0.025 + 0.2 0.1 + 0.873 0.212 + right + 1 1 0.9135 0.165 @@ -299,9 +306,16 @@ based on: 'recalbox-multi' by the Recalbox community left - 0.05 0.13 + 0.05 0.14 0.05 0.05 + + ./core/fonts/Exo2-BoldCondensed.otf + 0.025 + 0.2 0.1 + 0.165 0.105 + left + 0.792 0.085 0.22 0.07