diff --git a/NEWS.md b/NEWS.md index 1e55e4b8b..c8a7215a8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -48,6 +48,7 @@ Many bugs have been fixed, and numerous features that were only partially implem * 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 gamelist filter functionality to include completed and broken games as well as the ability to filter on game names (via free text entry) * 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/USERGUIDE.md b/USERGUIDE.md index c4944649b..849ac1335 100644 --- a/USERGUIDE.md +++ b/USERGUIDE.md @@ -647,7 +647,7 @@ This setting enables the 'Y' button for quickly toggling a game as favorite. Alt **Enable gamelist filters** -Activating or deactivating the ability to filter your gamelists. Can normally be left on. +Activating or deactivating the ability to filter your gamelists. This can normally be left enabled. **Enable quick system select** @@ -896,6 +896,8 @@ Choosing this entry opens a separate screen where it's possible to apply a filte The following filters can be applied: +**Text Filter (Game Name)** + **Favorites** **Genre** @@ -914,9 +916,9 @@ The following filters can be applied: **Hidden** -All possible filter values are assembled from metadata from the actual gamelist, so if there for instance are no games marked as completed, the Completed filter will only have the selectable option 'False', meaning 'True' will be missing. +With the exception of the text filter, all available filter values are assembled from metadata from the actual gamelist, so if there for instance are no games marked as completed, the Completed filter will only have the selectable option 'False', i.e. 'True' will be missing. -Be aware that although folders can have most of these metadata values set, the filters are only applied to files. So if you for example set a filter to only display your favorite games, any folder that contains a favorite game will be displayed, and other folders which are themselves marked as favorite but that do not contain any favorite games will be hidden. +Be aware that although folders can have most of the metadata values set, the filters are only applied to files (this is also true for the text/game name filter). So if you for example set a filter to only display your favorite games, any folder that contains a favorite game will be displayed, and other folders which are themselves marked as favorites but that do not contain any favorite games will be hidden. The filters are always applied for the complete game system, including all folder contents. diff --git a/es-app/src/CollectionSystemManager.cpp b/es-app/src/CollectionSystemManager.cpp index b059538a3..d2d814eb6 100644 --- a/es-app/src/CollectionSystemManager.cpp +++ b/es-app/src/CollectionSystemManager.cpp @@ -463,16 +463,6 @@ void CollectionSystemManager::updateCollectionSystem(FileData* file, CollectionS } } -void CollectionSystemManager::trimCollectionCount(FileData* rootFolder, int limit) -{ - SystemData* curSys = rootFolder->getSystem(); - while (static_cast(rootFolder->getChildrenListToDisplay().size()) > limit) { - CollectionFileData* gameToRemove = - (CollectionFileData*)rootFolder->getChildrenListToDisplay().back(); - ViewController::get()->getGameListView(curSys).get()->remove(gameToRemove, false); - } -} - // Delete all collection files from collection systems related to the source file. void CollectionSystemManager::deleteCollectionFiles(FileData* file) { @@ -742,22 +732,6 @@ SystemData* CollectionSystemManager::getSystemToView(SystemData* sys) return systemToView; } -// Functions below to Handle loading of collection systems, creating empty ones, -// and populating on demand. - -// Loads Automatic Collection systems (All, Favorites, Last Played). -void CollectionSystemManager::initAutoCollectionSystems() -{ - for (std::map::const_iterator - it = mCollectionSystemDeclsIndex.cbegin(); - it != mCollectionSystemDeclsIndex.cend() ; it++ ) { - CollectionSystemDecl sysDecl = it->second; - - if (!sysDecl.isCustom) - createNewCollectionEntry(sysDecl.name, sysDecl); - } -} - // Used to generate a description of the collection, all other metadata fields are hidden. FileData* CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) { @@ -846,6 +820,59 @@ FileData* CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sy return nullptr; } +// Return the unused folders from current theme path. +std::vector CollectionSystemManager::getUnusedSystemsFromTheme() +{ + // Get used systems in es_systems.cfg. + std::vector systemsInUse = getSystemsFromConfig(); + // Get available folders in theme. + std::vector themeSys = getSystemsFromTheme(); + // Get folders assigned to custom collections. + std::vector autoSys = getCollectionThemeFolders(false); + // Get folder assigned to custom collections. + std::vector customSys = getCollectionThemeFolders(true); + // Get folders assigned to user collections. + std::vector userSys = getUserCollectionThemeFolders(); + // Add them all to the list of systems in use. + systemsInUse.insert(systemsInUse.cend(), autoSys.cbegin(), autoSys.cend()); + systemsInUse.insert(systemsInUse.cend(), customSys.cbegin(), customSys.cend()); + systemsInUse.insert(systemsInUse.cend(), userSys.cbegin(), userSys.cend()); + + for (auto sysIt = themeSys.cbegin(); sysIt != themeSys.cend(); ) { + if (std::find(systemsInUse.cbegin(), systemsInUse.cend(), *sysIt) != systemsInUse.cend()) + sysIt = themeSys.erase(sysIt); + else + sysIt++; + } + return themeSys; +} + +SystemData* CollectionSystemManager::addNewCustomCollection(std::string name) +{ + CollectionSystemDecl decl = mCollectionSystemDeclsIndex[myCollectionsName]; + decl.themeFolder = name; + decl.name = name; + decl.longName = name; + + return createNewCollectionEntry(name, decl, true, true); +} + +// Functions below to Handle loading of collection systems, creating empty ones, +// and populating on demand. + +// Loads Automatic Collection systems (All, Favorites, Last Played). +void CollectionSystemManager::initAutoCollectionSystems() +{ + for (std::map::const_iterator + it = mCollectionSystemDeclsIndex.cbegin(); + it != mCollectionSystemDeclsIndex.cend() ; it++ ) { + CollectionSystemDecl sysDecl = it->second; + + if (!sysDecl.isCustom) + createNewCollectionEntry(sysDecl.name, sysDecl); + } +} + void CollectionSystemManager::initCustomCollectionSystems() { std::vector systems = getCollectionsFromConfigFolder(); @@ -863,16 +890,6 @@ SystemData* CollectionSystemManager::getAllGamesCollection() return allSysData->system; } -SystemData* CollectionSystemManager::addNewCustomCollection(std::string name) -{ - CollectionSystemDecl decl = mCollectionSystemDeclsIndex[myCollectionsName]; - decl.themeFolder = name; - decl.name = name; - decl.longName = name; - - return createNewCollectionEntry(name, decl, true, true); -} - // Create a new empty collection system based on the name and declaration. SystemData* CollectionSystemManager::createNewCollectionEntry( std::string name, CollectionSystemDecl sysDecl, bool index, bool custom) @@ -1156,33 +1173,6 @@ std::vector CollectionSystemManager::getSystemsFromTheme() return systems; } -// Return the unused folders from current theme path. -std::vector CollectionSystemManager::getUnusedSystemsFromTheme() -{ - // Get used systems in es_systems.cfg. - std::vector systemsInUse = getSystemsFromConfig(); - // Get available folders in theme. - std::vector themeSys = getSystemsFromTheme(); - // Get folders assigned to custom collections. - std::vector autoSys = getCollectionThemeFolders(false); - // Get folder assigned to custom collections. - std::vector customSys = getCollectionThemeFolders(true); - // Get folders assigned to user collections. - std::vector userSys = getUserCollectionThemeFolders(); - // Add them all to the list of systems in use. - systemsInUse.insert(systemsInUse.cend(), autoSys.cbegin(), autoSys.cend()); - systemsInUse.insert(systemsInUse.cend(), customSys.cbegin(), customSys.cend()); - systemsInUse.insert(systemsInUse.cend(), userSys.cbegin(), userSys.cend()); - - for (auto sysIt = themeSys.cbegin(); sysIt != themeSys.cend(); ) { - if (std::find(systemsInUse.cbegin(), systemsInUse.cend(), *sysIt) != systemsInUse.cend()) - sysIt = themeSys.erase(sysIt); - else - sysIt++; - } - return themeSys; -} - // Return which collection config files exist in the user folder. std::vector CollectionSystemManager::getCollectionsFromConfigFolder() { @@ -1240,6 +1230,16 @@ std::vector CollectionSystemManager::getUserCollectionThemeFolders( return systems; } +void CollectionSystemManager::trimCollectionCount(FileData* rootFolder, int limit) +{ + SystemData* curSys = rootFolder->getSystem(); + while (static_cast(rootFolder->getChildrenListToDisplay().size()) > limit) { + CollectionFileData* gameToRemove = + (CollectionFileData*)rootFolder->getChildrenListToDisplay().back(); + ViewController::get()->getGameListView(curSys).get()->remove(gameToRemove, false); + } +} + // Return whether a specific folder exists in the theme. bool CollectionSystemManager::themeFolderExists(std::string folder) { diff --git a/es-app/src/CollectionSystemManager.h b/es-app/src/CollectionSystemManager.h index e141e8a8c..eb82c6704 100644 --- a/es-app/src/CollectionSystemManager.h +++ b/es-app/src/CollectionSystemManager.h @@ -86,8 +86,8 @@ public: inline std::map getCustomCollectionSystems() { return mCustomCollectionSystemsData; }; inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }; - std::vector getUnusedSystemsFromTheme(); - SystemData* addNewCustomCollection(std::string name); + inline bool isEditing() { return mIsEditingCustom; }; + inline std::string getEditingCollection() { return mEditingCollection; }; bool isThemeGenericCollectionCompatible(bool genericCustomCollections); bool isThemeCustomCollectionCompatible(std::vector stringVector); @@ -95,13 +95,14 @@ public: void setEditMode(std::string collectionName); void exitEditMode(); - inline bool isEditing() { return mIsEditingCustom; }; - inline std::string getEditingCollection() { return mEditingCollection; }; bool inCustomCollection(const std::string& collectionName, FileData* gameFile); bool toggleGameInCollection(FileData* file); SystemData* getSystemToView(SystemData* sys); FileData* updateCollectionFolderMetadata(SystemData* sys); + std::vector getUnusedSystemsFromTheme(); + + SystemData* addNewCustomCollection(std::string name); private: static CollectionSystemManager* sInstance; diff --git a/es-app/src/scrapers/GamesDBJSONScraper.cpp b/es-app/src/scrapers/GamesDBJSONScraper.cpp index db5046535..c87654afc 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.cpp +++ b/es-app/src/scrapers/GamesDBJSONScraper.cpp @@ -18,27 +18,18 @@ #include "Settings.h" #include "SystemData.h" +#include +#include #include #include #include -// When raspbian will get an up to date version of rapidjson we'll be -// able to have it throw in case of error with the following: -//ifndef RAPIDJSON_ASSERT -//#define RAPIDJSON_ASSERT(x) \ -// if (!(x)) { \ -// throw std::runtime_error("rapidjson internal assertion failure: " #x); \ -// } -//#endif // RAPIDJSON_ASSERT - -#include -#include - using namespace PlatformIds; using namespace rapidjson; -namespace { -TheGamesDBJSONRequestResources resources; +namespace +{ + TheGamesDBJSONRequestResources resources; } const std::map gamesdb_new_platformid_map { @@ -117,8 +108,7 @@ const std::map gamesdb_new_platformid_map { { TANDY, "4941" }, }; -void thegamesdb_generate_json_scraper_requests( - const ScraperSearchParams& params, +void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, std::queue>& requests, std::vector& results) { @@ -242,7 +232,7 @@ std::string getDeveloperString(const Value& v) std::string out = ""; bool first = true; - for (int i = 0; i < (int)v.Size(); ++i) { + for (int i = 0; i < static_cast(v.Size()); ++i) { auto mapIt = resources.gamesdb_new_developers_map.find(getIntOrThrow(v[i])); if (mapIt == resources.gamesdb_new_developers_map.cend()) @@ -264,7 +254,7 @@ std::string getPublisherString(const Value& v) std::string out = ""; bool first = true; - for (int i = 0; i < (int)v.Size(); ++i) { + for (int i = 0; i < static_cast(v.Size()); ++i) { auto mapIt = resources.gamesdb_new_publishers_map.find(getIntOrThrow(v[i])); if (mapIt == resources.gamesdb_new_publishers_map.cend()) @@ -286,8 +276,9 @@ std::string getGenreString(const Value& v) std::string out = ""; bool first = true; - for (int i = 0; i < (int)v.Size(); ++i) { + for (int i = 0; i < static_cast(v.Size()); ++i) { auto mapIt = resources.gamesdb_new_genres_map.find(getIntOrThrow(v[i])); + if (mapIt == resources.gamesdb_new_genres_map.cend()) continue; @@ -457,7 +448,7 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr& req, const Value& games = doc["data"]["games"]; resources.ensureResources(); - for (int i = 0; i < (int)games.Size(); ++i) { + for (int i = 0; i < static_cast(games.Size()); ++i) { auto& v = games[i]; try { processGame(v, results); diff --git a/es-app/src/scrapers/GamesDBJSONScraper.h b/es-app/src/scrapers/GamesDBJSONScraper.h index abe120d71..3e174ec3d 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.h +++ b/es-app/src/scrapers/GamesDBJSONScraper.h @@ -13,23 +13,21 @@ #include "scrapers/Scraper.h" namespace pugi { -class xml_document; + class xml_document; } -void thegamesdb_generate_json_scraper_requests( - const ScraperSearchParams& params, +void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, std::queue>& requests, std::vector& results); -void thegamesdb_generate_json_scraper_requests( - const std::string& gameIDs, +void thegamesdb_generate_json_scraper_requests(const std::string& gameIDs, std::queue>& requests, std::vector& results); class TheGamesDBJSONRequest : public ScraperHttpRequest { public: - // ctor for a GetGameList request. + // Constructor for a GetGameList request. TheGamesDBJSONRequest( std::queue>& requestsWrite, std::vector& resultsWrite, @@ -38,18 +36,13 @@ class TheGamesDBJSONRequest : public ScraperHttpRequest mRequestQueue(&requestsWrite) { } - // ctor for a GetGame request - TheGamesDBJSONRequest( - std::vector& resultsWrite, - const std::string& url) - : ScraperHttpRequest(resultsWrite, url), - mRequestQueue(nullptr) + // Constructior for a GetGame request + TheGamesDBJSONRequest(std::vector& resultsWrite, const std::string& url) + : ScraperHttpRequest(resultsWrite, url), mRequestQueue(nullptr) { } protected: - //void retrieveMediaURLs() - void process(const std::unique_ptr& req, std::vector& results) override; bool isGameRequest() { return !mRequestQueue; } diff --git a/es-core/src/components/ComponentGrid.h b/es-core/src/components/ComponentGrid.h index e7732b140..b62d89e6a 100644 --- a/es-core/src/components/ComponentGrid.h +++ b/es-core/src/components/ComponentGrid.h @@ -23,7 +23,6 @@ namespace GridFlags enum Border : unsigned int { BORDER_NONE = 0, - BORDER_TOP = 1, BORDER_BOTTOM = 2, BORDER_LEFT = 4, @@ -96,11 +95,22 @@ private: GridFlags::UpdateType updateType; unsigned int border; - GridEntry(const Vector2i& p = Vector2i::Zero(), const Vector2i& d = Vector2i::Zero(), - const std::shared_ptr& cmp = nullptr, bool f = false, bool r = true, - GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, unsigned int b = - GridFlags::BORDER_NONE) : - pos(p), dim(d), component(cmp), canFocus(f), resize(r), updateType(u), border(b) + GridEntry( + const Vector2i& p = Vector2i::Zero(), + const Vector2i& d = Vector2i::Zero(), + const std::shared_ptr& cmp = nullptr, + bool f = false, + bool r = true, + GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, + unsigned int b = + GridFlags::BORDER_NONE) + : pos(p), + dim(d), + component(cmp), + canFocus(f), + resize(r), + updateType(u), + border(b) {}; operator bool() const diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 13eb6af85..a2f20af20 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -141,7 +141,7 @@ void ImageComponent::resize() mSize[0] = Math::round(mSize.x()); mSize[1] = Math::round(mSize.y()); // mSize.y() should already be rounded. - mTexture->rasterizeAt((size_t)mSize.x(), (size_t)mSize.y()); + mTexture->rasterizeAt(static_cast(mSize.x()), static_cast(mSize.y())); onSizeChanged(); } @@ -461,7 +461,8 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, const s if (elem->has("colorEnd")) setColorShiftEnd(elem->get("colorEnd")); if (elem->has("gradientType")) - setColorGradientHorizontal(!(elem->get("gradientType").compare("horizontal"))); + setColorGradientHorizontal(!(elem-> + get("gradientType").compare("horizontal"))); } } diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index f0872cc8d..95e9497ec 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_IMAGE_COMPONENT_H #define ES_CORE_COMPONENTS_IMAGE_COMPONENT_H -#include "renderers/Renderer.h" #include "math/Vector2i.h" +#include "renderers/Renderer.h" #include "GuiComponent.h" class TextureResource;