From f2f7d34bb6f0f8dc43fded16ca11e08d200c9eb9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 May 2020 10:29:29 +0200 Subject: [PATCH] Monster commit. Rewrote gamelist sorting logic and made per-gamelist sort settings session-permanent. Cleaned up a lot of code and started to reformat for 100 characters line length. --- es-app/src/CollectionSystemManager.cpp | 818 +++++++++--------- es-app/src/CollectionSystemManager.h | 45 +- es-app/src/FileData.cpp | 270 +++--- es-app/src/FileData.h | 51 +- es-app/src/Gamelist.cpp | 226 ++--- es-app/src/Gamelist.h | 6 + es-app/src/MetaData.cpp | 63 +- es-app/src/MetaData.h | 38 +- es-app/src/SystemData.cpp | 286 +++--- es-app/src/SystemData.h | 43 +- .../src/guis/GuiCollectionSystemsOptions.cpp | 12 +- es-app/src/guis/GuiCollectionSystemsOptions.h | 2 +- es-app/src/guis/GuiGamelistOptions.cpp | 317 ++++--- es-app/src/guis/GuiGamelistOptions.h | 13 +- es-app/src/guis/GuiMenu.cpp | 342 +++++--- es-app/src/guis/GuiMenu.h | 9 +- es-app/src/views/ViewController.cpp | 314 +++---- es-app/src/views/ViewController.h | 34 +- .../src/views/gamelist/BasicGameListView.cpp | 128 ++- es-app/src/views/gamelist/BasicGameListView.h | 8 +- .../views/gamelist/DetailedGameListView.cpp | 135 +-- .../src/views/gamelist/DetailedGameListView.h | 15 +- .../src/views/gamelist/GridGameListView.cpp | 221 ++--- es-app/src/views/gamelist/GridGameListView.h | 15 +- es-app/src/views/gamelist/IGameListView.cpp | 29 +- es-app/src/views/gamelist/IGameListView.h | 18 +- .../views/gamelist/ISimpleGameListView.cpp | 107 +-- .../src/views/gamelist/ISimpleGameListView.h | 10 +- .../src/views/gamelist/VideoGameListView.cpp | 190 ++-- es-app/src/views/gamelist/VideoGameListView.h | 15 +- es-core/src/GuiComponent.cpp | 168 ++-- es-core/src/GuiComponent.h | 75 +- es-core/src/Settings.cpp | 3 +- es-core/src/guis/GuiComplexTextEditPopup.cpp | 68 +- es-core/src/guis/GuiComplexTextEditPopup.h | 19 +- 35 files changed, 2288 insertions(+), 1825 deletions(-) diff --git a/es-app/src/CollectionSystemManager.cpp b/es-app/src/CollectionSystemManager.cpp index fcd74b050..3718d481e 100644 --- a/es-app/src/CollectionSystemManager.cpp +++ b/es-app/src/CollectionSystemManager.cpp @@ -1,3 +1,22 @@ +// +// CollectionSystemManager.cpp +// +// Manages collections of the following two types: +// 1) Automatically populated (All games, Favorites and Recent/Last Played) +// 2) Custom/user-created (could be any number of these) +// +// The automatic collections are basically virtual systems that have no +// gamelist.xml files and that only exist in memory during the program session. +// SystemData sets up the basic data structures and CollectionSystemManager +// populates and manages the collections. +// +// The custom collections have simple data files which are just lists of ROM files. +// +// In addition to this, CollectionSystemManager also handles some logic for +// normal systems such as adding and removing favorite games, including triggering +// the required re-sort and refresh of the gamelists. +// + #include "CollectionSystemManager.h" #include "guis/GuiInfoPopup.h" @@ -18,29 +37,29 @@ std::string myCollectionsName = "collections"; #define LAST_PLAYED_MAX 50 -/* Handling the getting, initialization, deinitialization, saving and deletion of - * a CollectionSystemManager Instance */ +// Handles the getting, initialization, deinitialization, +// saving and deletion of a CollectionSystemManager instance. CollectionSystemManager* CollectionSystemManager::sInstance = NULL; CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(window) { CollectionSystemDecl systemDecls[] = { - //type name long name //default sort // theme folder // isCustom - { AUTO_ALL_GAMES, "all", "all games", "filename, ascending", "auto-allgames", false }, - { AUTO_LAST_PLAYED, "recent", "last played", "last played, descending", "auto-lastplayed", false }, - { AUTO_FAVORITES, "favorites", "favorites", "filename, ascending", "auto-favorites", false }, - { CUSTOM_COLLECTION, myCollectionsName, "collections", "filename, ascending", "custom-collections", true } + // Type Name Long name Theme folder isCustom + { AUTO_ALL_GAMES, "all", "all games", "auto-allgames", false }, + { AUTO_LAST_PLAYED, "recent", "last played", "auto-lastplayed", false }, + { AUTO_FAVORITES, "favorites", "favorites", "auto-favorites", false }, + { CUSTOM_COLLECTION, myCollectionsName, "collections", "custom-collections", true } }; - // create a map - std::vector tempSystemDecl = std::vector(systemDecls, systemDecls + sizeof(systemDecls) / sizeof(systemDecls[0])); + // Create a map of the collections. + std::vector tempSystemDecl = std::vector + (systemDecls, systemDecls + sizeof(systemDecls) / sizeof(systemDecls[0])); - for (std::vector::const_iterator it = tempSystemDecl.cbegin(); it != tempSystemDecl.cend(); ++it ) - { + for (std::vector::const_iterator it = tempSystemDecl.cbegin(); + it != tempSystemDecl.cend(); ++it ) mCollectionSystemDeclsIndex[(*it).name] = (*it); - } - // creating standard environment data + // Setup the standard environment. mCollectionEnvData = new SystemEnvironmentData; mCollectionEnvData->mStartPath = ""; std::vector exts; @@ -51,7 +70,7 @@ CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(windo mCollectionEnvData->mPlatformIds = allPlatformIds; std::string path = getCollectionsFolder(); - if(!Utils::FileSystem::exists(path)) + if (!Utils::FileSystem::exists(path)) Utils::FileSystem::createDirectory(path); mIsEditingCustom = false; @@ -65,16 +84,15 @@ CollectionSystemManager::~CollectionSystemManager() assert(sInstance == this); removeCollectionsFromDisplayedSystems(); - // iterate the map - for(std::map::const_iterator it = mCustomCollectionSystemsData.cbegin() ; it != mCustomCollectionSystemsData.cend() ; it++ ) - { + // Iterate the map. + for (std::map::const_iterator + it = mCustomCollectionSystemsData.cbegin(); + it != mCustomCollectionSystemsData.cend() ; it++) { if (it->second.isPopulated) - { saveCustomCollection(it->second.system); - } delete it->second.system; } - sInstance = NULL; + sInstance = nullptr; } CollectionSystemManager* CollectionSystemManager::get() @@ -92,144 +110,147 @@ void CollectionSystemManager::init(Window* window) void CollectionSystemManager::deinit() { if (sInstance) - { delete sInstance; - } } void CollectionSystemManager::saveCustomCollection(SystemData* sys) { std::string name = sys->getName(); - std::unordered_map games = sys->getRootFolder()->getChildrenByFilename(); + std::unordered_map + games = sys->getRootFolder()->getChildrenByFilename(); bool found = mCustomCollectionSystemsData.find(name) != mCustomCollectionSystemsData.cend(); if (found) { CollectionSystemData sysData = mCustomCollectionSystemsData.at(name); - if (sysData.needsSave) - { + if (sysData.needsSave) { std::ofstream configFile; configFile.open(getCustomCollectionConfigPath(name)); - for(std::unordered_map::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter) - { + for (std::unordered_map::const_iterator + iter = games.cbegin(); iter != games.cend(); ++iter) { std::string path = iter->first; configFile << path << std::endl; } configFile.close(); } } - else - { + else { LOG(LogError) << "Couldn't find collection to save! " << name; } } -/* Methods to load all Collections into memory, and handle enabling the active ones */ -// loads all Collection Systems +// Functions below to load all collections into memory, and to enable the active ones. + +// Load all collection systems. void CollectionSystemManager::loadCollectionSystems() { initAutoCollectionSystems(); CollectionSystemDecl decl = mCollectionSystemDeclsIndex[myCollectionsName]; mCustomCollectionsBundle = createNewCollectionEntry(decl.name, decl, false); - // we will also load custom systems here + + // We will also load custom systems here. initCustomCollectionSystems(); - if(Settings::getInstance()->getString("CollectionSystemsAuto") != "" || Settings::getInstance()->getString("CollectionSystemsCustom") != "") - { - // Now see which ones are enabled + + if (Settings::getInstance()->getString("CollectionSystemsAuto") != "" || + Settings::getInstance()->getString("CollectionSystemsCustom") != "") { + // Now see which ones are enabled. loadEnabledListFromSettings(); - // add to the main System Vector, and create Views as needed + // Add to the main System Vector, and create Views as needed. updateSystemsList(); } } -// loads settings +// Load settings. void CollectionSystemManager::loadEnabledListFromSettings() { - // we parse the auto collection settings list - std::vector autoSelected = Utils::String::commaStringToVector(Settings::getInstance()->getString("CollectionSystemsAuto"), true); + // We parse the auto collection settings list. + std::vector autoSelected = Utils::String::commaStringToVector( + Settings::getInstance()->getString("CollectionSystemsAuto"), true); - // iterate the map - for(std::map::iterator it = mAutoCollectionSystemsData.begin() ; it != mAutoCollectionSystemsData.end() ; it++ ) - { - it->second.isEnabled = (std::find(autoSelected.cbegin(), autoSelected.cend(), it->first) != autoSelected.cend()); + // Iterate the map. + for (std::map::iterator + it = mAutoCollectionSystemsData.begin(); + it != mAutoCollectionSystemsData.end() ; it++ ) { + + it->second.isEnabled = (std::find(autoSelected.cbegin(), + autoSelected.cend(), it->first) != autoSelected.cend()); } - // we parse the custom collection settings list - std::vector customSelected = Utils::String::commaStringToVector(Settings::getInstance()->getString("CollectionSystemsCustom"), true); + // Parse the custom collection settings list. + std::vector customSelected = Utils::String::commaStringToVector( + Settings::getInstance()->getString("CollectionSystemsCustom"), true); - // iterate the map - for(std::map::iterator it = mCustomCollectionSystemsData.begin() ; it != mCustomCollectionSystemsData.end() ; it++ ) - { - it->second.isEnabled = (std::find(customSelected.cbegin(), customSelected.cend(), it->first) != customSelected.cend()); + // Iterate the map. + for (std::map::iterator + it = mCustomCollectionSystemsData.begin(); + it != mCustomCollectionSystemsData.end() ; it++ ) { + + it->second.isEnabled = (std::find(customSelected.cbegin(), + customSelected.cend(), it->first) != customSelected.cend()); } } -// updates enabled system list in System View +// Update enabled system list in System View. void CollectionSystemManager::updateSystemsList() { - // remove all Collection Systems + // Remove all collection systems. removeCollectionsFromDisplayedSystems(); - // add custom enabled ones + // Add custom enabled collections. addEnabledCollectionsToDisplayedSystems(&mCustomCollectionSystemsData); - if(Settings::getInstance()->getBool("SortAllSystems")) - { - // sort custom individual systems with other systems - std::sort(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(), systemSort); - - // move RetroPie system to end, before auto collections - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); ) - { - if ((*sysIt)->getName() == "retropie") - { - SystemData* retroPieSystem = (*sysIt); - sysIt = SystemData::sSystemVector.erase(sysIt); - SystemData::sSystemVector.push_back(retroPieSystem); - break; - } - else - { - sysIt++; - } - } - } - - if(mCustomCollectionsBundle->getRootFolder()->getChildren().size() > 0) - { - mCustomCollectionsBundle->getRootFolder()->sort(getSortTypeFromString(mCollectionSystemDeclsIndex[myCollectionsName].defaultSort)); + // Sort the bundled custom collections. + if (mCustomCollectionsBundle->getRootFolder()->getChildren().size() > 0) { + mCustomCollectionsBundle->getRootFolder()->sort(getSortTypeFromString( + mCustomCollectionsBundle->getRootFolder()->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); SystemData::sSystemVector.push_back(mCustomCollectionsBundle); } - // add auto enabled ones + // Add auto enabled collections. addEnabledCollectionsToDisplayedSystems(&mAutoCollectionSystemsData); - // create views for collections, before reload - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); sysIt++) - { + // Create views for collections, before reload. + for (auto sysIt = SystemData::sSystemVector.cbegin(); + sysIt != SystemData::sSystemVector.cend(); sysIt++) { if ((*sysIt)->isCollection()) - { ViewController::get()->getGameListView((*sysIt)); - } } - // if we were editing a custom collection, and it's no longer enabled, exit edit mode - if(mIsEditingCustom && !mEditingCollectionSystemData->isEnabled) + // If we were editing a custom collection, and it's no longer enabled, exit edit mode. + if (mIsEditingCustom && !mEditingCollectionSystemData->isEnabled) { exitEditMode(); } } -/* Methods to manage collection files related to a source FileData */ -// updates all collection files related to the source file +// Functions below to manage collection files related to a source FileData. + +// Update all collection files related to the source file. void CollectionSystemManager::refreshCollectionSystems(FileData* file) { if (!file->getSystem()->isGameSystem() || file->getType() != GAME) return; - std::map allCollections; - allCollections.insert(mAutoCollectionSystemsData.cbegin(), mAutoCollectionSystemsData.cend()); - allCollections.insert(mCustomCollectionSystemsData.cbegin(), mCustomCollectionSystemsData.cend()); + // If not a collection but rather a real system, then pretend to be a + // collection in order to be properly processed by updateCollectionSystem(). + // It's seemingly a bit strange, but without rewriting a lot of code for how + // systems and collections are handled, it's likely the best approach. + if (!file->getSystem()->isCollection()) { + CollectionSystemData realSys; + realSys.system = file->getSystem(); + realSys.isEnabled = true; + realSys.isPopulated = true; + realSys.needsSave = false; - for(auto sysDataIt = allCollections.cbegin(); sysDataIt != allCollections.cend(); sysDataIt++) - { + updateCollectionSystem(file, realSys); + } + + std::map allCollections; + allCollections.insert(mAutoCollectionSystemsData.cbegin(), + mAutoCollectionSystemsData.cend()); + allCollections.insert(mCustomCollectionSystemsData.cbegin(), + mCustomCollectionSystemsData.cend()); + + for (auto sysDataIt = allCollections.cbegin(); + sysDataIt != allCollections.cend(); sysDataIt++) { updateCollectionSystem(file, sysDataIt->second); } } @@ -238,130 +259,154 @@ void CollectionSystemManager::updateCollectionSystem(FileData* file, CollectionS { if (sysData.isPopulated) { - // collection files use the full path as key, to avoid clashes + // Collection files use the full path as key, to avoid clashes. std::string key = file->getFullPath(); SystemData* curSys = sysData.system; - const std::unordered_map& children = curSys->getRootFolder()->getChildrenByFilename(); + + const std::unordered_map&children = + curSys->getRootFolder()->getChildrenByFilename(); + bool found = children.find(key) != children.cend(); FileData* rootFolder = curSys->getRootFolder(); FileFilterIndex* fileIndex = curSys->getIndex(); std::string name = curSys->getName(); if (found) { - // if we found it, we need to update it + // If we found it, we need to update it. FileData* collectionEntry = children.at(key); - // remove from index, so we can re-index metadata after refreshing + // Remove it from the index, so we can re-index the metadata after refreshing. fileIndex->removeFromIndex(collectionEntry); collectionEntry->refreshMetadata(); - // found and we are removing + // Found it, and we are removing it. if (name == "favorites" && file->metadata.get("favorite") == "false") { - // need to check if still marked as favorite, if not remove - ViewController::get()->getGameListView(curSys).get()->remove(collectionEntry, false); - - // Send an event when removing from favorites - ViewController::get()->onFileChanged(file, FILE_METADATA_CHANGED); - ViewController::get()->getGameListView(curSys)->onFileChanged(collectionEntry, FILE_METADATA_CHANGED); + // Need to check if it is still marked as favorite, if not remove it. + ViewController::get()-> + getGameListView(curSys).get()->remove(collectionEntry, false); } - else - { - // re-index with new metadata + else { + // Re-index with new metadata. fileIndex->addToIndex(collectionEntry); ViewController::get()->onFileChanged(collectionEntry, FILE_METADATA_CHANGED); } } - else - { - // we didn't find it here - we need to check if we should add it - if (name == "recent" && file->metadata.get("playcount") > "0" && includeFileInAutoCollections(file) || - name == "favorites" && file->metadata.get("favorite") == "true") { + else { + // We didn't find it here - we need to check if we should add it. + if (name == "recent" && file->metadata.get("playcount") > "0" && + includeFileInAutoCollections(file) || name == "favorites" && + file->metadata.get("favorite") == "true") { CollectionFileData* newGame = new CollectionFileData(file, curSys); rootFolder->addChild(newGame); fileIndex->addToIndex(newGame); ViewController::get()->onFileChanged(file, FILE_METADATA_CHANGED); - ViewController::get()->getGameListView(curSys)->onFileChanged(newGame, FILE_METADATA_CHANGED); + ViewController::get()-> + getGameListView(curSys)->onFileChanged(newGame, FILE_METADATA_CHANGED); } } - rootFolder->sort(getSortTypeFromString(mCollectionSystemDeclsIndex[name].defaultSort)); + if (name == "recent") - { + rootFolder->sort(getSortTypeFromString("last played, descending")); + else if (sysData.decl.isCustom == true && + !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) + rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); + else + rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavoritesFirst")); + + if (name == "recent") { trimCollectionCount(rootFolder, LAST_PLAYED_MAX); ViewController::get()->onFileChanged(rootFolder, FILE_METADATA_CHANGED); + + // Select the first row of the gamelist (the game just played). + IGameListView* gameList = + ViewController::get()->getGameListView(getSystemToView(sysData.system)).get(); + FileData* firstRow = + gameList->getCursor()->getParent()->getChildrenListToDisplay()[0]; + gameList->setCursor(firstRow); } - else + else { ViewController::get()->onFileChanged(rootFolder, FILE_SORTED); + std::string teststring1 = rootFolder->getPath(); + // If it's a custom collection and the collections + // are grouped, update the parent instead. + if (sysData.decl.isCustom == true && + Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { + ViewController::get()->onFileChanged( + rootFolder->getParent(), FILE_METADATA_CHANGED); + } + } } } void CollectionSystemManager::trimCollectionCount(FileData* rootFolder, int limit) { SystemData* curSys = rootFolder->getSystem(); - while ((int)rootFolder->getChildrenListToDisplay().size() > limit) - { - CollectionFileData* gameToRemove = (CollectionFileData*)rootFolder->getChildrenListToDisplay().back(); + while ((int)rootFolder->getChildrenListToDisplay().size() > limit) { + CollectionFileData* gameToRemove = + (CollectionFileData*)rootFolder->getChildrenListToDisplay().back(); ViewController::get()->getGameListView(curSys).get()->remove(gameToRemove, false); } } -// deletes all collection files from collection systems related to the source file +// Delete all collection files from collection systems related to the source file. void CollectionSystemManager::deleteCollectionFiles(FileData* file) { - // collection files use the full path as key, to avoid clashes + // Collection files use the full path as key, to avoid clashes. std::string key = file->getFullPath(); - // find games in collection systems - std::map allCollections; - allCollections.insert(mAutoCollectionSystemsData.cbegin(), mAutoCollectionSystemsData.cend()); - allCollections.insert(mCustomCollectionSystemsData.cbegin(), mCustomCollectionSystemsData.cend()); - for(auto sysDataIt = allCollections.begin(); sysDataIt != allCollections.end(); sysDataIt++) - { - if (sysDataIt->second.isPopulated) - { - const std::unordered_map& children = (sysDataIt->second.system)->getRootFolder()->getChildrenByFilename(); + // Find games in collection systems. + std::map allCollections; + allCollections.insert(mAutoCollectionSystemsData.cbegin(), + mAutoCollectionSystemsData.cend()); + allCollections.insert(mCustomCollectionSystemsData.cbegin(), + mCustomCollectionSystemsData.cend()); + + for (auto sysDataIt = allCollections.begin(); sysDataIt != allCollections.end(); sysDataIt++) { + if (sysDataIt->second.isPopulated) { + const std::unordered_map& children = + (sysDataIt->second.system)->getRootFolder()->getChildrenByFilename(); bool found = children.find(key) != children.cend(); if (found) { sysDataIt->second.needsSave = true; FileData* collectionEntry = children.at(key); SystemData* systemViewToUpdate = getSystemToView(sysDataIt->second.system); - ViewController::get()->getGameListView(systemViewToUpdate).get()->remove(collectionEntry, false); + ViewController::get()->getGameListView(systemViewToUpdate).get()-> + remove(collectionEntry, false); } } } } -// returns whether the current theme is compatible with Automatic or Custom Collections +// Return whether the current theme is compatible with Automatic or Custom Collections. bool CollectionSystemManager::isThemeGenericCollectionCompatible(bool genericCustomCollections) { std::vector cfgSys = getCollectionThemeFolders(genericCustomCollections); - for(auto sysIt = cfgSys.cbegin(); sysIt != cfgSys.cend(); sysIt++) - { - if(!themeFolderExists(*sysIt)) + for (auto sysIt = cfgSys.cbegin(); sysIt != cfgSys.cend(); sysIt++) { + if (!themeFolderExists(*sysIt)) return false; } return true; } -bool CollectionSystemManager::isThemeCustomCollectionCompatible(std::vector stringVector) +bool CollectionSystemManager::isThemeCustomCollectionCompatible( + std::vector stringVector) { if (isThemeGenericCollectionCompatible(true)) return true; - // get theme path + // Get theme path. auto themeSets = ThemeData::getThemeSets(); auto set = themeSets.find(Settings::getInstance()->getString("ThemeSet")); - if(set != themeSets.cend()) - { + if (set != themeSets.cend()) { std::string defaultThemeFilePath = set->second.path + "/theme.xml"; if (Utils::FileSystem::exists(defaultThemeFilePath)) - { return true; - } } - for(auto sysIt = stringVector.cbegin(); sysIt != stringVector.cend(); sysIt++) - { - if(!themeFolderExists(*sysIt)) + for (auto sysIt = stringVector.cbegin(); sysIt != stringVector.cend(); sysIt++) { + if (!themeFolderExists(*sysIt)) return false; } return true; @@ -371,54 +416,48 @@ std::string CollectionSystemManager::getValidNewCollectionName(std::string inNam { std::string name = inName; - if(index == 0) - { + if (index == 0) { size_t remove = std::string::npos; - // get valid name - while((remove = name.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-[]() ")) != std::string::npos) - { + // Get valid name. + while ((remove = name.find_first_not_of( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-[]() ")) + != std::string::npos) name.erase(remove, 1); - } } - else - { + else { name += " (" + std::to_string(index) + ")"; } - if(name == "") - { + if (name == "") { name = "New Collection"; } - if(name != inName) - { + if (name != inName) { LOG(LogInfo) << "Had to change name, from: " << inName << " to: " << name; } - // get used systems in es_systems.cfg + // Get used systems from es_systems.cfg. std::vector systemsInUse = getSystemsFromConfig(); - // get folders assigned to custom collections + // Get folders assigned to custom collections. std::vector autoSys = getCollectionThemeFolders(false); - // get folder assigned to custom collections + // Get folder assigned to custom collections. std::vector customSys = getCollectionThemeFolders(true); - // get folders assigned to user collections + // Get folders assigned to user collections. std::vector userSys = getUserCollectionThemeFolders(); - // add them all to the list of systems in use + // 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 = systemsInUse.cbegin(); sysIt != systemsInUse.cend(); sysIt++) - { - if (*sysIt == name) - { - if(index > 0) { + + for (auto sysIt = systemsInUse.cbegin(); sysIt != systemsInUse.cend(); sysIt++) { + if (*sysIt == name) { + if (index > 0) name = name.substr(0, name.size()-4); - } return getValidNewCollectionName(name, index+1); } } - // if it matches one of the custom collections reserved names + // If it matches one of the custom collections reserved names then return it. if (mCollectionSystemDeclsIndex.find(name) != mCollectionSystemDeclsIndex.cend()) return getValidNewCollectionName(name, index+1); return name; @@ -426,8 +465,7 @@ std::string CollectionSystemManager::getValidNewCollectionName(std::string inNam void CollectionSystemManager::setEditMode(std::string collectionName) { - if (mCustomCollectionSystemsData.find(collectionName) == mCustomCollectionSystemsData.cend()) - { + if (mCustomCollectionSystemsData.find(collectionName) == mCustomCollectionSystemsData.cend()) { LOG(LogError) << "Tried to edit a non-existing collection: " << collectionName; return; } @@ -435,20 +473,24 @@ void CollectionSystemManager::setEditMode(std::string collectionName) mEditingCollection = collectionName; CollectionSystemData* sysData = &(mCustomCollectionSystemsData.at(mEditingCollection)); - if (!sysData->isPopulated) - { + if (!sysData->isPopulated) { populateCustomCollection(sysData); } - // if it's bundled, this needs to be the bundle system + // If it's bundled, this needs to be the bundle system. mEditingCollectionSystemData = sysData; - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Editing the '" + Utils::String::toUpper(collectionName) + "' Collection. Add/remove games with Y.", 10000); + GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Editing the '" + + Utils::String::toUpper(collectionName) + + "' Collection. Add/remove games with Y.", 10000); + mWindow->setInfoPopup(s); } void CollectionSystemManager::exitEditMode() { - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Finished editing the '" + mEditingCollection + "' Collection.", 4000); + GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Finished editing the '" + + mEditingCollection + "' Collection.", 4000); + mWindow->setInfoPopup(s); mIsEditingCustom = false; mEditingCollection = "Favorites"; @@ -456,26 +498,25 @@ void CollectionSystemManager::exitEditMode() mEditingCollectionSystemData->system->onMetaDataSavePoint(); } -// adds or removes a game from a specific collection +// Add or remove a game from a specific collection. bool CollectionSystemManager::toggleGameInCollection(FileData* file) { - if (file->getType() == GAME) - { + if (file->getType() == GAME) { GuiInfoPopup* s; bool adding = true; std::string name = file->getName(); std::string sysName = mEditingCollection; - if (mIsEditingCustom) - { + if (mIsEditingCustom) { SystemData* sysData = mEditingCollectionSystemData->system; mEditingCollectionSystemData->needsSave = true; + if (!mEditingCollectionSystemData->isPopulated) - { populateCustomCollection(mEditingCollectionSystemData); - } + std::string key = file->getFullPath(); FileData* rootFolder = sysData->getRootFolder(); - const std::unordered_map& children = rootFolder->getChildrenByFilename(); + const std::unordered_map& + children = rootFolder->getChildrenByFilename(); bool found = children.find(key) != children.cend(); FileFilterIndex* fileIndex = sysData->getIndex(); std::string name = sysData->getName(); @@ -484,62 +525,61 @@ bool CollectionSystemManager::toggleGameInCollection(FileData* file) if (found) { adding = false; - // if we found it, we need to remove it + // If we found it, we need to remove it. FileData* collectionEntry = children.at(key); - // remove from index + // Remove from index. fileIndex->removeFromIndex(collectionEntry); - // remove from bundle index as well, if needed - if(systemViewToUpdate != sysData) - { + // Remove from bundle index as well, if needed. + if (systemViewToUpdate != sysData) systemViewToUpdate->getIndex()->removeFromIndex(collectionEntry); - } - ViewController::get()->getGameListView(systemViewToUpdate).get()->remove(collectionEntry, false); + + ViewController::get()->getGameListView(systemViewToUpdate).get()-> + remove(collectionEntry, false); } - else - { - // we didn't find it here, we should add it + else { + // We didn't find it here, so we should add it. CollectionFileData* newGame = new CollectionFileData(file, sysData); rootFolder->addChild(newGame); fileIndex->addToIndex(newGame); - ViewController::get()->getGameListView(systemViewToUpdate)->onFileChanged(newGame, FILE_METADATA_CHANGED); - rootFolder->sort(getSortTypeFromString(mEditingCollectionSystemData->decl.defaultSort)); - ViewController::get()->onFileChanged(systemViewToUpdate->getRootFolder(), FILE_SORTED); - // add to bundle index as well, if needed - if(systemViewToUpdate != sysData) - { + ViewController::get()->getGameListView(systemViewToUpdate)-> + onFileChanged(newGame, FILE_METADATA_CHANGED); + if (name == "recent") + rootFolder->sort(getSortTypeFromString("last played, descending")); + else + rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavoritesFirst")); + + ViewController::get()->onFileChanged(systemViewToUpdate-> + getRootFolder(), FILE_SORTED); + + // Add to bundle index as well, if needed. + if (systemViewToUpdate != sysData) systemViewToUpdate->getIndex()->addToIndex(newGame); - } } updateCollectionFolderMetadata(sysData); } - else - { + else { file->getSourceFileData()->getSystem()->getIndex()->removeFromIndex(file); MetaDataList* md = &file->getSourceFileData()->metadata; std::string value = md->get("favorite"); - if (value == "false") - { + if (value == "false") { md->set("favorite", "true"); } - else - { + else { adding = false; md->set("favorite", "false"); } + file->getSourceFileData()->getSystem()->getIndex()->addToIndex(file); - file->getSourceFileData()->getSystem()->onMetaDataSavePoint(); - refreshCollectionSystems(file->getSourceFileData()); } if (adding) - { - s = new GuiInfoPopup(mWindow, "Added '" + Utils::String::removeParenthesis(name) + "' to '" + Utils::String::toUpper(sysName) + "'", 4000); - } + s = new GuiInfoPopup(mWindow, "Added '" + Utils::String::removeParenthesis(name) + + "' to '" + Utils::String::toUpper(sysName) + "'", 4000); else - { - s = new GuiInfoPopup(mWindow, "Removed '" + Utils::String::removeParenthesis(name) + "' from '" + Utils::String::toUpper(sysName) + "'", 4000); - } + s = new GuiInfoPopup(mWindow, "Removed '" + Utils::String::removeParenthesis(name) + + "' from '" + Utils::String::toUpper(sysName) + "'", 4000); mWindow->setInfoPopup(s); return true; } @@ -552,34 +592,35 @@ SystemData* CollectionSystemManager::getSystemToView(SystemData* sys) FileData* rootFolder = sys->getRootFolder(); FileData* bundleRootFolder = mCustomCollectionsBundle->getRootFolder(); - const std::unordered_map& bundleChildren = bundleRootFolder->getChildrenByFilename(); + const std::unordered_map& + bundleChildren = bundleRootFolder->getChildrenByFilename(); - // is the rootFolder bundled in the "My Collections" system? + // Is the rootFolder bundled in the "My Collections" system? bool sysFoundInBundle = bundleChildren.find(rootFolder->getKey()) != bundleChildren.cend(); if (sysFoundInBundle && sys->isCollection()) - { systemToView = mCustomCollectionsBundle; - } return systemToView; } -/* Handles loading a collection system, creating an empty one, and populating on demand */ -// loads Automatic Collection systems (All, Favorites, Last Played) +// 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++ ) - { + for (std::map::const_iterator + it = mCollectionSystemDeclsIndex.cbegin(); + it != mCollectionSystemDeclsIndex.cend() ; it++ ) { CollectionSystemDecl sysDecl = it->second; + if (!sysDecl.isCustom) - { createNewCollectionEntry(sysDecl.name, sysDecl); - } } } -// this may come in handy if at any point in time in the future we want to -// automatically generate metadata for a folder +// This may come in handy if at any point in time in the future we want to +// automatically generate metadata for a folder. void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) { FileData* rootFolder = sys->getRootFolder(); @@ -596,12 +637,12 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) std::unordered_map games = rootFolder->getChildrenByFilename(); - if(games.size() > 0) - { + if (games.size() > 0) { std::string games_list = ""; int games_counter = 0; - for(std::unordered_map::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter) - { + for (std::unordered_map::const_iterator + iter = games.cbegin(); iter != games.cend(); ++iter) { + games_counter++; FileData* file = iter->second; @@ -611,14 +652,22 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) std::string new_genre = file->metadata.get("genre"); std::string new_players = file->metadata.get("players"); - rating = (new_rating > rating ? (new_rating != "" ? new_rating : rating) : rating); - players = (new_players > players ? (new_players != "" ? new_players : players) : players); - releasedate = (new_releasedate < releasedate ? (new_releasedate != "" ? new_releasedate : releasedate) : releasedate); - developer = (developer == "None" ? new_developer : (new_developer != developer ? "Various" : new_developer)); - genre = (genre == "None" ? new_genre : (new_genre != genre ? "Various" : new_genre)); + rating = (new_rating > rating ? (new_rating != "" ? + new_rating : rating) : rating); - switch(games_counter) - { + players = (new_players > players ? (new_players != "" ? + new_players : players) : players); + + releasedate = (new_releasedate < releasedate ? (new_releasedate != "" ? + new_releasedate : releasedate) : releasedate); + + developer = (developer == "None" ? new_developer : (new_developer != developer ? + "Various" : new_developer)); + + genre = (genre == "None" ? new_genre : (new_genre != genre ? + "Various" : new_genre)); + + switch (games_counter) { case 2: case 3: games_list += ", "; @@ -630,7 +679,8 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) } } - desc = "This collection contains " + std::to_string(games_counter) + " games, including " + games_list; + desc = "This collection contains " + std::to_string(games_counter) + + " games, including " + games_list; FileData* randomGame = sys->getRandomGame(); @@ -639,7 +689,6 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) image = randomGame->getImagePath(); } - rootFolder->metadata.set("desc", desc); rootFolder->metadata.set("rating", rating); rootFolder->metadata.set("players", players); @@ -654,8 +703,7 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) void CollectionSystemManager::initCustomCollectionSystems() { std::vector systems = getCollectionsFromConfigFolder(); - for (auto nameIt = systems.cbegin(); nameIt != systems.cend(); nameIt++) - { + for (auto nameIt = systems.cbegin(); nameIt != systems.cend(); nameIt++) { addNewCustomCollection(*nameIt); } } @@ -664,9 +712,8 @@ SystemData* CollectionSystemManager::getAllGamesCollection() { CollectionSystemData* allSysData = &mAutoCollectionSystemsData["all"]; if (!allSysData->isPopulated) - { populateAutoCollection(allSysData); - } + return allSysData->system; } @@ -676,13 +723,16 @@ SystemData* CollectionSystemManager::addNewCustomCollection(std::string name) decl.themeFolder = name; decl.name = name; decl.longName = name; + return createNewCollectionEntry(name, decl); } -// creates a new, empty Collection system, based on the name and declaration -SystemData* CollectionSystemManager::createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, bool index) +// Create a new empty collection system based on the name and declaration. +SystemData* CollectionSystemManager::createNewCollectionEntry( + std::string name, CollectionSystemDecl sysDecl, bool index) { - SystemData* newSys = new SystemData(name, sysDecl.longName, mCollectionEnvData, sysDecl.themeFolder, true); + SystemData* newSys = new SystemData( + name, sysDecl.longName, mCollectionEnvData, sysDecl.themeFolder, true); CollectionSystemData newCollectionData; newCollectionData.system = newSys; @@ -691,42 +741,38 @@ SystemData* CollectionSystemManager::createNewCollectionEntry(std::string name, newCollectionData.isPopulated = false; newCollectionData.needsSave = false; - if (index) - { + if (index) { if (!sysDecl.isCustom) - { mAutoCollectionSystemsData[name] = newCollectionData; - } else - { mCustomCollectionSystemsData[name] = newCollectionData; - } } return newSys; } -// populates an Automatic Collection System +// Populate an automatic collection system. void CollectionSystemManager::populateAutoCollection(CollectionSystemData* sysData) { SystemData* newSys = sysData->system; CollectionSystemDecl sysDecl = sysData->decl; FileData* rootFolder = newSys->getRootFolder(); FileFilterIndex* index = newSys->getIndex(); - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); sysIt++) - { - // we won't iterate all collections + for (auto sysIt = SystemData::sSystemVector.cbegin(); + sysIt != SystemData::sSystemVector.cend(); sysIt++) { + // We won't iterate all collections. if ((*sysIt)->isGameSystem() && !(*sysIt)->isCollection()) { std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(GAME); - for(auto gameIt = files.cbegin(); gameIt != files.cend(); gameIt++) - { + for (auto gameIt = files.cbegin(); gameIt != files.cend(); gameIt++) { bool include = includeFileInAutoCollections((*gameIt)); - switch(sysDecl.type) { + + switch (sysDecl.type) { case AUTO_LAST_PLAYED: include = include && (*gameIt)->metadata.get("playcount") > "0"; break; case AUTO_FAVORITES: - // we may still want to add files we don't want in auto collections in "favorites" + // We may still want to add files we don't + // want in auto collections in "favorites" include = (*gameIt)->metadata.get("favorite") == "true"; break; } @@ -739,13 +785,18 @@ void CollectionSystemManager::populateAutoCollection(CollectionSystemData* sysDa } } } - rootFolder->sort(getSortTypeFromString(sysDecl.defaultSort)); + if (rootFolder->getName() == "recent") + rootFolder->sort(getSortTypeFromString("last played, descending")); + else + rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavoritesFirst")); + if (sysDecl.type == AUTO_LAST_PLAYED) trimCollectionCount(rootFolder, LAST_PLAYED_MAX); sysData->isPopulated = true; } -// populates a Custom Collection System +// Populate a custom collection system void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sysData) { SystemData* newSys = sysData->system; @@ -753,8 +804,7 @@ void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sys CollectionSystemDecl sysDecl = sysData->decl; std::string path = getCustomCollectionConfigPath(newSys->getName()); - if(!Utils::FileSystem::exists(path)) - { + if (!Utils::FileSystem::exists(path)) { LOG(LogInfo) << "Couldn't find custom collection config file at " << path; return; } @@ -763,89 +813,90 @@ void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sys FileData* rootFolder = newSys->getRootFolder(); FileFilterIndex* index = newSys->getIndex(); - // get Configuration for this Custom System + // Get configuration for this custom collection. std::ifstream input(path); - // get all files map - std::unordered_map allFilesMap = getAllGamesCollection()->getRootFolder()->getChildrenByFilename(); + // Get all files map. + std::unordered_map + allFilesMap = getAllGamesCollection()->getRootFolder()->getChildrenByFilename(); - // iterate list of files in config file - - for(std::string gameKey; getline(input, gameKey); ) - { + // Iterate list of files in the config file. + for (std::string gameKey; getline(input, gameKey); ) { std::unordered_map::const_iterator it = allFilesMap.find(gameKey); if (it != allFilesMap.cend()) { CollectionFileData* newGame = new CollectionFileData(it->second, newSys); rootFolder->addChild(newGame); index->addToIndex(newGame); } - else - { - LOG(LogInfo) << "Couldn't find game referenced at '" << gameKey << "' for system config '" << path << "'"; + else { + LOG(LogInfo) << "Couldn't find game referenced at '" << gameKey << + "' for system config '" << path << "'"; } } - rootFolder->sort(getSortTypeFromString(sysDecl.defaultSort)); + updateCollectionFolderMetadata(newSys); } -/* Handle System View removal and insertion of Collections */ +// Functions below to handle System View removal and insertion of collections. + void CollectionSystemManager::removeCollectionsFromDisplayedSystems() { - // remove all Collection Systems - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); ) - { + // Remove all collection Systems. + for (auto sysIt = SystemData::sSystemVector.cbegin(); + sysIt != SystemData::sSystemVector.cend(); ) { if ((*sysIt)->isCollection()) - { sysIt = SystemData::sSystemVector.erase(sysIt); - } else - { sysIt++; - } } - // remove all custom collections in bundle - // this should not delete the objects from memory! + // Remove all custom collections in bundle. + // This should not delete the objects from memory! FileData* customRoot = mCustomCollectionsBundle->getRootFolder(); std::vector mChildren = customRoot->getChildren(); - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) - { + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { customRoot->removeChild(*it); } - // clear index + // Clear index. mCustomCollectionsBundle->getIndex()->resetIndex(); - // remove view so it's re-created as needed + // Remove view so it's re-created as needed. ViewController::get()->removeGameListView(mCustomCollectionsBundle); } -void CollectionSystemManager::addEnabledCollectionsToDisplayedSystems(std::map* colSystemData) +void CollectionSystemManager::addEnabledCollectionsToDisplayedSystems( + std::map* colSystemData) { - // add auto enabled ones - for(std::map::iterator it = colSystemData->begin() ; it != colSystemData->end() ; it++ ) - { - if(it->second.isEnabled) - { - // check if populated, otherwise populate - if (!it->second.isPopulated) - { - if(it->second.decl.isCustom) - { + // Add auto enabled collections. + for (std::map::iterator + it = colSystemData->begin() ; it != colSystemData->end() ; it++ ) { + if (it->second.isEnabled) { + // Check if populated, otherwise populate. + if (!it->second.isPopulated) { + if (it->second.decl.isCustom) populateCustomCollection(&(it->second)); - } else - { populateAutoCollection(&(it->second)); + } + // Check if it has its own view. + if (!it->second.decl.isCustom || themeFolderExists(it->first) || + !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { + // Theme folder exists, or we chose not to bundle it under the + // custom-collections system. So we need to create a view. + SystemData::sSystemVector.push_back(it->second.system); + // If this is a non-bundled custom collection, then sort it. + if (it->second.decl.isCustom == true) { + FileData* rootFolder = it->second.system->getRootFolder(); + rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); + // Jump to the first row of the game list + IGameListView* gameList = ViewController::get()-> + getGameListView((it->second.system)).get(); + FileData* firstRow = gameList->getCursor()-> + getParent()->getChildrenListToDisplay()[0]; + gameList->setCursor(firstRow); } } - // check if it has its own view - if(!it->second.decl.isCustom || themeFolderExists(it->first) || !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) - { - // exists theme folder, or we chose not to bundle it under the custom-collections system - // so we need to create a view - SystemData::sSystemVector.push_back(it->second.system); - } - else - { + else { FileData* newSysRootFolder = it->second.system->getRootFolder(); mCustomCollectionsBundle->getRootFolder()->addChild(newSysRootFolder); mCustomCollectionsBundle->getIndex()->importIndex(it->second.system->getIndex()); @@ -854,36 +905,31 @@ void CollectionSystemManager::addEnabledCollectionsToDisplayedSystems(std::map CollectionSystemManager::getSystemsFromConfig() { std::vector systems; std::string path = SystemData::getConfigPath(false); - if(!Utils::FileSystem::exists(path)) - { + if (!Utils::FileSystem::exists(path)) return systems; - } pugi::xml_document doc; pugi::xml_parse_result res = doc.load_file(path.c_str()); - if(!res) - { + if (!res) return systems; - } - //actually read the file + // Actually read the file. pugi::xml_node systemList = doc.child("systemList"); - if(!systemList) - { + if (!systemList) return systems; - } - for(pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system")) - { - // theme folder + for (pugi::xml_node system = systemList.child("system"); + system; system = system.next_sibling("system")) { + // Theme folder. std::string themeFolder = system.child("theme").text().get(); systems.push_back(themeFolder); } @@ -891,44 +937,37 @@ std::vector CollectionSystemManager::getSystemsFromConfig() return systems; } -// gets all folders from the current theme path +// Get all folders from the current theme path. std::vector CollectionSystemManager::getSystemsFromTheme() { std::vector systems; auto themeSets = ThemeData::getThemeSets(); - if(themeSets.empty()) - { - // no theme sets available - return systems; - } + if (themeSets.empty()) + return systems; // No theme sets available. - std::map::const_iterator set = themeSets.find(Settings::getInstance()->getString("ThemeSet")); - if(set == themeSets.cend()) - { - // currently selected theme set is missing, so just pick the first available set + std::map::const_iterator + set = themeSets.find(Settings::getInstance()->getString("ThemeSet")); + if (set == themeSets.cend()) { + // Currently selected theme set is missing, so just pick the first available set. set = themeSets.cbegin(); Settings::getInstance()->setString("ThemeSet", set->first); } std::string themePath = set->second.path; - if (Utils::FileSystem::exists(themePath)) - { + if (Utils::FileSystem::exists(themePath)) { Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(themePath); - for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); it != dirContent.cend(); ++it) - { - if (Utils::FileSystem::isDirectory(*it)) - { - //... here you have a directory + for (Utils::FileSystem::stringList::const_iterator + it = dirContent.cbegin(); it != dirContent.cend(); ++it) { + if (Utils::FileSystem::isDirectory(*it)) { + // ... here you have a directory. std::string folder = *it; folder = folder.substr(themePath.size()+1); - if(Utils::FileSystem::exists(set->second.getThemePath(folder))) - { + if (Utils::FileSystem::exists(set->second.getThemePath(folder))) systems.push_back(folder); - } } } } @@ -936,39 +975,34 @@ std::vector CollectionSystemManager::getSystemsFromTheme() return systems; } -// returns the unused folders from current theme path +// Return the unused folders from current theme path. std::vector CollectionSystemManager::getUnusedSystemsFromTheme() { - // get used systems in es_systems.cfg + // Get used systems in es_systems.cfg. std::vector systemsInUse = getSystemsFromConfig(); - // get available folders in theme + // Get available folders in theme. std::vector themeSys = getSystemsFromTheme(); - // get folders assigned to custom collections + // Get folders assigned to custom collections. std::vector autoSys = getCollectionThemeFolders(false); - // get folder assigned to custom collections + // Get folder assigned to custom collections. std::vector customSys = getCollectionThemeFolders(true); - // get folders assigned to user collections + // Get folders assigned to user collections. std::vector userSys = getUserCollectionThemeFolders(); - // add them all to the list of systems in use + // 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(); ) - { + 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; } -// returns which collection config files exist in the user folder +// Return which collection config files exist in the user folder. std::vector CollectionSystemManager::getCollectionsFromConfigFolder() { std::vector systems; @@ -976,23 +1010,22 @@ std::vector CollectionSystemManager::getCollectionsFromConfigFolder if (Utils::FileSystem::exists(configPath)) { - Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(configPath); - for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); it != dirContent.cend(); ++it) - { - if (Utils::FileSystem::isRegularFile(*it)) - { - // it's a file + Utils::FileSystem::stringList dirContent = + Utils::FileSystem::getDirContent(configPath); + for (Utils::FileSystem::stringList::const_iterator + it = dirContent.cbegin(); it != dirContent.cend(); ++it) { + if (Utils::FileSystem::isRegularFile(*it)) { + // It's a file. std::string filename = Utils::FileSystem::getFileName(*it); - - // need to confirm filename matches config format - if (filename != "custom-.cfg" && Utils::String::startsWith(filename, "custom-") && Utils::String::endsWith(filename, ".cfg")) - { + // Need to confirm filename matches config format. + if (filename != "custom-.cfg" && Utils::String::startsWith( + filename, "custom-") && Utils::String::endsWith(filename, ".cfg")) { filename = filename.substr(7, filename.size()-11); systems.push_back(filename); } - else - { - LOG(LogInfo) << "Found non-collection config file in collections folder: " << filename; + else { + LOG(LogInfo) << "Found non-collection config file in collections folder: " + << filename; } } } @@ -1000,33 +1033,33 @@ std::vector CollectionSystemManager::getCollectionsFromConfigFolder return systems; } -// returns the theme folders for Automatic Collections (All, Favorites, Last Played) or generic Custom Collections folder +// Return the theme folders for automatic collections (All, Favorites, Last Played) +// or a generic custom collections folder. std::vector CollectionSystemManager::getCollectionThemeFolders(bool custom) { std::vector systems; - for(std::map::const_iterator it = mCollectionSystemDeclsIndex.cbegin() ; it != mCollectionSystemDeclsIndex.cend() ; it++ ) - { + for (std::map::const_iterator + it = mCollectionSystemDeclsIndex.cbegin(); + it != mCollectionSystemDeclsIndex.cend() ; it++ ) { CollectionSystemDecl sysDecl = it->second; if (sysDecl.isCustom == custom) - { systems.push_back(sysDecl.themeFolder); - } } return systems; } -// returns the theme folders in use for the user-defined Custom Collections +// Return the theme folders in use for the user-defined custom collections. std::vector CollectionSystemManager::getUserCollectionThemeFolders() { std::vector systems; - for(std::map::const_iterator it = mCustomCollectionSystemsData.cbegin() ; it != mCustomCollectionSystemsData.cend() ; it++ ) - { + for (std::map::const_iterator + it = mCustomCollectionSystemsData.cbegin(); + it != mCustomCollectionSystemsData.cend() ; it++ ) systems.push_back(it->second.decl.themeFolder); - } return systems; } -// returns whether a specific folder exists in the theme +// Return whether a specific folder exists in the theme. bool CollectionSystemManager::themeFolderExists(std::string folder) { std::vector themeSys = getSystemsFromTheme(); @@ -1035,9 +1068,9 @@ bool CollectionSystemManager::themeFolderExists(std::string folder) bool CollectionSystemManager::includeFileInAutoCollections(FileData* file) { - // we exclude non-game files from collections (i.e. "kodi", entries from non-game systems) - // if/when there are more in the future, maybe this can be a more complex method, with a proper list - // but for now a simple string comparison is more performant + // We exclude non-game files from collections (i.e. "kodi", entries from non-game systems). + // If/when there are more in the future, maybe this can be a more complex method, with a + // proper list, but for now a simple string comparison is more performant. return file->getName() != "kodi" && file->getSystem()->isGameSystem(); } @@ -1048,7 +1081,8 @@ std::string getCustomCollectionConfigPath(std::string collectionName) std::string getCollectionsFolder() { - return Utils::FileSystem::getGenericPath(Utils::FileSystem::getHomePath() + "/.emulationstation/collections"); + return Utils::FileSystem::getGenericPath(Utils::FileSystem::getHomePath() + + "/.emulationstation/collections"); } bool systemSort(SystemData* sys1, SystemData* sys2) @@ -1057,3 +1091,17 @@ bool systemSort(SystemData* sys1, SystemData* sys2) std::string name2 = Utils::String::toUpper(sys2->getName()); return name1.compare(name2) < 0; } + +// Return whether the system is a custom collection. +bool CollectionSystemManager::getIsCustomCollection(SystemData* system) +{ + // Iterate the map. + for (std::map::const_iterator + it = mCustomCollectionSystemsData.cbegin(); + it != mCustomCollectionSystemsData.cend() ; it++) { + if (it->second.system == system) + return true; + } + + return false; +} diff --git a/es-app/src/CollectionSystemManager.h b/es-app/src/CollectionSystemManager.h index 23afe1932..ebbab77b4 100644 --- a/es-app/src/CollectionSystemManager.h +++ b/es-app/src/CollectionSystemManager.h @@ -1,3 +1,22 @@ +// +// CollectionSystemManager.h +// +// Manages collections of the following two types: +// 1) Automatically populated (All games, Favorites and Recent/Last Played) +// 2) Custom/user-created (could be any number of these) +// +// The automatic collections are basically virtual systems that have no +// gamelist.xml files and that only exist in memory during the program session. +// SystemData sets up the basic data structures and CollectionSystemManager +// populates and manages the collections. +// +// The custom collections have simple data files which are just lists of ROM files. +// +// In addition to this, CollectionSystemManager also handles some logic for +// normal systems such as adding and removing favorite games, including triggering +// the required re-sort and refresh of the gamelists. +// + #pragma once #ifndef ES_APP_COLLECTION_SYSTEM_MANAGER_H #define ES_APP_COLLECTION_SYSTEM_MANAGER_H @@ -11,26 +30,22 @@ class SystemData; class Window; struct SystemEnvironmentData; -enum CollectionSystemType -{ +enum CollectionSystemType { AUTO_ALL_GAMES, AUTO_LAST_PLAYED, AUTO_FAVORITES, CUSTOM_COLLECTION }; -struct CollectionSystemDecl -{ - CollectionSystemType type; // type of system +struct CollectionSystemDecl { + CollectionSystemType type; std::string name; std::string longName; - std::string defaultSort; std::string themeFolder; bool isCustom; }; -struct CollectionSystemData -{ +struct CollectionSystemData { SystemData* system; CollectionSystemDecl decl; bool isEnabled; @@ -57,8 +72,10 @@ public: void updateCollectionSystem(FileData* file, CollectionSystemData sysData); void deleteCollectionFiles(FileData* file); - inline std::map getAutoCollectionSystems() { return mAutoCollectionSystemsData; }; - inline std::map getCustomCollectionSystems() { return mCustomCollectionSystemsData; }; + inline std::map getAutoCollectionSystems() + { return mAutoCollectionSystemsData; }; + inline std::map getCustomCollectionSystems() + { return mCustomCollectionSystemsData; }; inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }; std::vector getUnusedSystemsFromTheme(); SystemData* addNewCustomCollection(std::string name); @@ -76,6 +93,8 @@ public: SystemData* getSystemToView(SystemData* sys); void updateCollectionFolderMetadata(SystemData* sys); + bool getIsCustomCollection(SystemData* system); + private: static CollectionSystemManager* sInstance; SystemEnvironmentData* mCollectionEnvData; @@ -90,12 +109,14 @@ private: void initAutoCollectionSystems(); void initCustomCollectionSystems(); SystemData* getAllGamesCollection(); - SystemData* createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, bool index = true); + SystemData* createNewCollectionEntry(std::string name, + CollectionSystemDecl sysDecl, bool index = true); void populateAutoCollection(CollectionSystemData* sysData); void populateCustomCollection(CollectionSystemData* sysData); void removeCollectionsFromDisplayedSystems(); - void addEnabledCollectionsToDisplayedSystems(std::map* colSystemData); + void addEnabledCollectionsToDisplayedSystems(std::map* colSystemData); std::vector getSystemsFromConfig(); std::vector getSystemsFromTheme(); diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index dfe881f98..d16666ab9 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -1,3 +1,11 @@ +// +// FileData.cpp +// +// Provides game file data structures and functions to access and sort this information. +// Also provides functions to look up paths to media files and for launching games +// (launching initiated by the ViewController). +// + #include "FileData.h" #include "utils/FileSystemUtil.h" @@ -16,11 +24,22 @@ #include "Window.h" #include -FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData* envData, SystemData* system) - : mType(type), mPath(path), mSystem(system), mEnvData(envData), mSourceFileData(NULL), mParent(NULL), metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) // metadata is REALLY set in the constructor! +FileData::FileData( + FileType type, + const std::string& path, + SystemEnvironmentData* envData, + SystemData* system) + : mType(type), + mPath(path), + mSystem(system), + mEnvData(envData), + mSourceFileData(nullptr), + mParent(nullptr), + // Metadata is REALLY set in the constructor! + metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) { - // metadata needs at least a name field (since that's what getName() will return) - if(metadata.get("name").empty()) + // Metadata needs at least a name field (since that's what getName() will return). + if (metadata.get("name").empty()) metadata.set("name", getDisplayName()); mSystemName = system->getName(); metadata.resetChangedFlag(); @@ -28,10 +47,10 @@ FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData FileData::~FileData() { - if(mParent) + if (mParent) mParent->removeChild(this); - if(mType == GAME) + if (mType == GAME) mSystem->getIndex()->removeFromIndex(this); mChildren.clear(); @@ -40,9 +59,6 @@ FileData::~FileData() std::string FileData::getDisplayName() const { std::string stem = Utils::FileSystem::getStem(mPath); -// if(mSystem && mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO)) -// stem = MameNames::getInstance()->getRealName(stem); - return stem; } @@ -77,25 +93,19 @@ const std::string FileData::getMediaDirectory() const std::string mediaDirSetting = Settings::getInstance()->getString("MediaDirectory"); std::string mediaDirPath = ""; - if(mediaDirSetting == "") - { + if (mediaDirSetting == "") { mediaDirPath = Utils::FileSystem::getHomePath() + "/.emulationstation/downloaded_media/"; } - else - { + else { mediaDirPath = mediaDirSetting; // Expand home symbol if the path starts with ~ - if(mediaDirPath[0] == '~') - { + if (mediaDirPath[0] == '~') { mediaDirPath.erase(0, 1); mediaDirPath.insert(0, Utils::FileSystem::getHomePath()); } - - if(mediaDirPath.back() != '/') - { + if (mediaDirPath.back() != '/') mediaDirPath = mediaDirPath + "/"; - } } return mediaDirPath; @@ -106,21 +116,21 @@ const std::string FileData::getThumbnailPath() const const char* extList[2] = { ".png", ".jpg" }; std::string tempPath = getMediaDirectory() + mSystemName + "/thumbnails/" + getDisplayName(); - // Look for media in the media directory - for(int i = 0; i < 2; i++) - { + // Look for media in the media directory. + for (int i = 0; i < 2; i++) { std::string mediaPath = tempPath + extList[i]; - if(Utils::FileSystem::exists(mediaPath)) + if (Utils::FileSystem::exists(mediaPath)) return mediaPath; } - // No media found in the media directory, so look for local art as well (if configured to do so) - if(Settings::getInstance()->getBool("LocalArt")) + // No media found in the media directory, so look + // for local art as well (if configured to do so). + if (Settings::getInstance()->getBool("LocalArt")) { - for(int i = 0; i < 2; i++) - { - std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-thumbnail" + extList[i]; - if(Utils::FileSystem::exists(localMediaPath)) + for (int i = 0; i < 2; i++) { + std::string localMediaPath = mEnvData->mStartPath + "/images/" + + getDisplayName() + "-thumbnail" + extList[i]; + if (Utils::FileSystem::exists(localMediaPath)) return localMediaPath; } } @@ -133,21 +143,21 @@ const std::string FileData::getVideoPath() const const char* extList[5] = { ".avi", ".mkv", ".mov", ".mp4", ".wmv" }; std::string tempPath = getMediaDirectory() + mSystemName + "/videos/" + getDisplayName(); - // Look for media in the media directory - for(int i = 0; i < 5; i++) - { + // Look for media in the media directory. + for (int i = 0; i < 5; i++) { std::string mediaPath = tempPath + extList[i]; - if(Utils::FileSystem::exists(mediaPath)) + if (Utils::FileSystem::exists(mediaPath)) return mediaPath; } - // No media found in the media directory, so look for local art as well (if configured to do so) - if(Settings::getInstance()->getBool("LocalArt")) + // No media found in the media directory, so look + // for local art as well (if configured to do so). + if (Settings::getInstance()->getBool("LocalArt")) { - for(int i = 0; i < 5; i++) - { - std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-video" + extList[i]; - if(Utils::FileSystem::exists(localMediaPath)) + for (int i = 0; i < 5; i++) { + std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + + "-video" + extList[i]; + if (Utils::FileSystem::exists(localMediaPath)) return localMediaPath; } } @@ -160,21 +170,21 @@ const std::string FileData::getMarqueePath() const const char* extList[2] = { ".png", ".jpg" }; std::string tempPath = getMediaDirectory() + mSystemName + "/marquees/" + getDisplayName(); - // Look for media in the media directory - for(int i = 0; i < 2; i++) - { + // Look for media in the media directory. + for (int i = 0; i < 2; i++) { std::string mediaPath = tempPath + extList[i]; - if(Utils::FileSystem::exists(mediaPath)) + if (Utils::FileSystem::exists(mediaPath)) return mediaPath; } - // No media found in the media directory, so look for local art as well (if configured to do so) - if(Settings::getInstance()->getBool("LocalArt")) + // No media found in the media directory, so look + // for local art as well (if configured to do so). + if (Settings::getInstance()->getBool("LocalArt")) { - for(int i = 0; i < 2; i++) - { - std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-marquee" + extList[i]; - if(Utils::FileSystem::exists(localMediaPath)) + for (int i = 0; i < 2; i++) { + std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + + "-marquee" + extList[i]; + if (Utils::FileSystem::exists(localMediaPath)) return localMediaPath; } } @@ -186,32 +196,30 @@ const std::string FileData::getImagePath() const { const char* extList[2] = { ".png", ".jpg" }; - // Look for mix image (a combination of screenshot, 3D box and marquee) in the media directory + // Look for mix image (a combination of screenshot, 3D box and marquee) in the media directory. std::string tempPath = getMediaDirectory() + mSystemName + "/miximages/" + getDisplayName(); - for(int i = 0; i < 2; i++) - { + for (int i = 0; i < 2; i++) { std::string mediaPath = tempPath + extList[i]; - if(Utils::FileSystem::exists(mediaPath)) + if (Utils::FileSystem::exists(mediaPath)) return mediaPath; } - // If no mix image exists, try normal screenshot + // If no mix image exists, try normal screenshot. tempPath = getMediaDirectory() + mSystemName + "/screenshots/" + getDisplayName(); - for(int i = 0; i < 2; i++) - { + for (int i = 0; i < 2; i++) { std::string mediaPath = tempPath + extList[i]; - if(Utils::FileSystem::exists(mediaPath)) + if (Utils::FileSystem::exists(mediaPath)) return mediaPath; } - // No media found in the media directory, so look for local art as well (if configured to do so) - if(Settings::getInstance()->getBool("LocalArt")) - { - for(int i = 0; i < 2; i++) - { - std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-image" + extList[i]; - if(Utils::FileSystem::exists(localMediaPath)) + // No media found in the media directory, so look + // for local art as well (if configured to do so). + if (Settings::getInstance()->getBool("LocalArt")) { + for (int i = 0; i < 2; i++) { + std::string localMediaPath = mEnvData->mStartPath + "/images/" + + getDisplayName() + "-image" + extList[i]; + if (Utils::FileSystem::exists(localMediaPath)) return localMediaPath; } } @@ -225,17 +233,14 @@ const std::vector& FileData::getChildrenListToDisplay() FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex(); if (idx->isFiltered()) { mFilteredChildren.clear(); - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) - { + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { if (idx->showFile((*it))) { mFilteredChildren.push_back(*it); } } - return mFilteredChildren; } - else - { + else { return mChildren; } } @@ -245,16 +250,12 @@ std::vector FileData::getFilesRecursive(unsigned int typeMask, bool d std::vector out; FileFilterIndex* idx = mSystem->getIndex(); - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) - { - if((*it)->getType() & typeMask) - { + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { + if ((*it)->getType() & typeMask) { if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it)) out.push_back(*it); } - - if((*it)->getChildren().size() > 0) - { + if ((*it)->getChildren().size() > 0) { std::vector subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly); out.insert(out.cend(), subchildren.cbegin(), subchildren.cend()); } @@ -271,9 +272,10 @@ const bool FileData::isArcadeAsset() { const std::string stem = Utils::FileSystem::getStem(mPath); return ( - (mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO))) - && - (MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem)) + (mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || + mSystem->hasPlatformId(PlatformIds::NEOGEO))) && + (MameNames::getInstance()->isBios(stem) || + MameNames::getInstance()->isDevice(stem)) ); } @@ -288,8 +290,7 @@ void FileData::addChild(FileData* file) assert(file->getParent() == NULL); const std::string key = file->getKey(); - if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) - { + if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) { mChildrenByFilename[key] = file; mChildren.push_back(file); file->mParent = this; @@ -301,10 +302,8 @@ void FileData::removeChild(FileData* file) assert(mType == FOLDER); assert(file->getParent() == this); mChildrenByFilename.erase(file->getKey()); - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) - { - if(*it == file) - { + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { + if (*it == file) { file->mParent = NULL; mChildren.erase(it); return; @@ -320,19 +319,59 @@ void FileData::sort(ComparisonFunction& comparator, bool ascending) { std::stable_sort(mChildren.begin(), mChildren.end(), comparator); - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) - { - if((*it)->getChildren().size() > 0) + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { + if ((*it)->getChildren().size() > 0) (*it)->sort(comparator, ascending); } - if(!ascending) + if (!ascending) std::reverse(mChildren.begin(), mChildren.end()); } -void FileData::sort(const SortType& type) +void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending) { - sort(*type.comparisonFunction, type.ascending); + std::vector mChildrenFavorites; + std::vector mChildrenOthers; + + for (unsigned int i = 0; i < mChildren.size(); i++) { + if (mChildren[i]->getFavorite()) + mChildrenFavorites.push_back(mChildren[i]); + else + mChildrenOthers.push_back(mChildren[i]); + } + + // Sort favorite games and the other games separately. + std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator); + std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator); + + for (auto it = mChildrenFavorites.cbegin(); it != mChildrenFavorites.cend(); it++) { + if ((*it)->getChildren().size() > 0) + (*it)->sortFavoritesOnTop(comparator, ascending); + } + + for (auto it = mChildrenOthers.cbegin(); it != mChildrenOthers.cend(); it++) { + if ((*it)->getChildren().size() > 0) + (*it)->sortFavoritesOnTop(comparator, ascending); + } + + if (!ascending) { + std::reverse(mChildrenFavorites.begin(), mChildrenFavorites.end()); + std::reverse(mChildrenOthers.begin(), mChildrenOthers.end()); + } + + // Combine the individually sorted favorite games and other games vectors. + mChildren.erase(mChildren.begin(), mChildren.end()); + mChildren.reserve(mChildrenFavorites.size() + mChildrenOthers.size()); + mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end()); + mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end()); +} + +void FileData::sort(const SortType& type, bool mFavoritesOnTop) +{ + if (mFavoritesOnTop) + sortFavoritesOnTop(*type.comparisonFunction, type.ascending); + else + sort(*type.comparisonFunction, type.ascending); } void FileData::launchGame(Window* window) @@ -346,15 +385,13 @@ void FileData::launchGame(Window* window) std::string command = ""; - // Check if there is a launch string override for the game and the corresponding option has been set - if(Settings::getInstance()->getBool("LaunchstringOverride") && !metadata.get("launchstring").empty()) - { + // Check if there is a launch string override for the game + // and the corresponding option to use it has been set. + if (Settings::getInstance()->getBool("LaunchstringOverride") && + !metadata.get("launchstring").empty()) command = metadata.get("launchstring"); - } else - { command = mEnvData->mLaunchCommand; - } const std::string rom = Utils::FileSystem::getEscapedPath(getPath()); const std::string basename = Utils::FileSystem::getStem(getPath()); @@ -369,25 +406,23 @@ void FileData::launchGame(Window* window) LOG(LogInfo) << " " << command; int exitCode = runSystemCommand(command); - if(exitCode != 0) - { + if (exitCode != 0) LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!"; - } Scripting::fireEvent("game-end"); // window->init(); + VolumeControl::getInstance()->init(); window->normalizeNextUpdate(); - //update number of times the game has been launched - + // Update number of times the game has been launched. FileData* gameToUpdate = getSourceFileData(); int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1; gameToUpdate->metadata.set("playcount", std::to_string(static_cast(timesPlayed))); - //update last played time + // Update last played time. gameToUpdate->metadata.set("lastplayed", Utils::Time::DateTime(Utils::Time::now())); CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate); @@ -395,9 +430,10 @@ void FileData::launchGame(Window* window) } CollectionFileData::CollectionFileData(FileData* file, SystemData* system) - : FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), file->getSourceFileData()->getSystemEnvData(), system) + : FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), + file->getSourceFileData()->getSystemEnvData(), system) { - // we use this constructor to create a clone of the filedata, and change its system + // We use this constructor to create a clone of the filedata, and change its system. mSourceFileData = file->getSourceFileData(); refreshMetadata(); mParent = NULL; @@ -407,8 +443,8 @@ CollectionFileData::CollectionFileData(FileData* file, SystemData* system) CollectionFileData::~CollectionFileData() { - // need to remove collection file data at the collection object destructor - if(mParent) + // Need to remove collection file data at the collection object destructor. + if (mParent) mParent->removeChild(this); mParent = NULL; } @@ -431,28 +467,28 @@ void CollectionFileData::refreshMetadata() const std::string& CollectionFileData::getName() { if (mDirty) { - mCollectionFileName = Utils::String::removeParenthesis(mSourceFileData->metadata.get("name")); - mCollectionFileName += " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]"; + mCollectionFileName = + Utils::String::removeParenthesis(mSourceFileData->metadata.get("name")); + mCollectionFileName += + " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]"; mDirty = false; } if (Settings::getInstance()->getBool("CollectionShowSystemInfo")) return mCollectionFileName; + return mSourceFileData->metadata.get("name"); } -// returns Sort Type based on a string description +// Return sort type based on a string description. FileData::SortType getSortTypeFromString(std::string desc) { std::vector SortTypes = FileSorts::SortTypes; - // find it - for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) - { + // Find it + for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { const FileData::SortType& sort = FileSorts::SortTypes.at(i); - if(sort.description == desc) - { + if (sort.description == desc) return sort; - } } - // if not found default to name, ascending + // If no type found then default to "filename, ascending". return FileSorts::SortTypes.at(0); } diff --git a/es-app/src/FileData.h b/es-app/src/FileData.h index a96053165..eb5321311 100644 --- a/es-app/src/FileData.h +++ b/es-app/src/FileData.h @@ -1,3 +1,11 @@ +// +// FileData.h +// +// Provides game file data structures and functions to access and sort this information. +// Also provides functions to look up paths to media files and for launching games +// (launching initiated by the ViewController). +// + #pragma once #ifndef ES_APP_FILE_DATA_H #define ES_APP_FILE_DATA_H @@ -10,15 +18,13 @@ class SystemData; class Window; struct SystemEnvironmentData; -enum FileType -{ +enum FileType { GAME = 1, // Cannot have children. FOLDER = 2, PLACEHOLDER = 3 }; -enum FileChangeType -{ +enum FileChangeType { FILE_ADDED, FILE_METADATA_CHANGED, FILE_REMOVED, @@ -33,7 +39,11 @@ FileType stringToFileType(const char* str); class FileData { public: - FileData(FileType type, const std::string& path, SystemEnvironmentData* envData, SystemData* system); + FileData(FileType type, + const std::string& path, + SystemEnvironmentData* envData, + SystemData* system); + virtual ~FileData(); virtual const std::string& getName(); @@ -42,7 +52,8 @@ public: inline FileType getType() const { return mType; } inline const std::string& getPath() const { return mPath; } inline FileData* getParent() const { return mParent; } - inline const std::unordered_map& getChildrenByFilename() const { return mChildrenByFilename; } + inline const std::unordered_map& getChildrenByFilename() const + { return mChildrenByFilename; } inline const std::vector& getChildren() const { return mChildren; } inline SystemData* getSystem() const { return mSystem; } inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } @@ -53,7 +64,8 @@ public: virtual const std::string getImagePath() const; const std::vector& getChildrenListToDisplay(); - std::vector getFilesRecursive(unsigned int typeMask, bool displayedOnly = false) const; + std::vector getFilesRecursive(unsigned int typeMask, + bool displayedOnly = false) const; void addChild(FileData* file); // Error if mType != FOLDER void removeChild(FileData* file); //Error if mType != FOLDER @@ -69,33 +81,42 @@ public: virtual FileData* getSourceFileData(); inline std::string getSystemName() const { return mSystemName; }; - // Returns our best guess at the "real" name for this file (will attempt to perform MAME name translation) + // Returns our best guess at the "real" name for this file + // (will attempt to perform MAME name translation). std::string getDisplayName() const; - // As above, but also remove parenthesis + // As above, but also remove parenthesis. std::string getCleanName() const; void launchGame(Window* window); typedef bool ComparisonFunction(const FileData* a, const FileData* b); - struct SortType - { + struct SortType { ComparisonFunction* comparisonFunction; bool ascending; std::string description; - SortType(ComparisonFunction* sortFunction, bool sortAscending, const std::string & sortDescription) - : comparisonFunction(sortFunction), ascending(sortAscending), description(sortDescription) {} + SortType(ComparisonFunction* sortFunction, + bool sortAscending, + const std::string& sortDescription) + : comparisonFunction(sortFunction), + ascending(sortAscending), + description(sortDescription) {} }; void sort(ComparisonFunction& comparator, bool ascending = true); - void sort(const SortType& type); + void sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending = true); + void sort(const SortType& type, bool mFavoritesOnTop = false); MetaDataList metadata; + inline void setSortTypeString(std::string typestring) { mSortTypeString = typestring; } + inline std::string getSortTypeString() { return mSortTypeString; } + protected: FileData* mSourceFileData; FileData* mParent; std::string mSystemName; + std::string mSortTypeString = ""; private: FileType mType; @@ -117,7 +138,7 @@ public: FileData* getSourceFileData(); std::string getKey(); private: - // needs to be updated when metadata changes + // Needs to be updated when metadata changes. std::string mCollectionFileName; bool mDirty; }; diff --git a/es-app/src/Gamelist.cpp b/es-app/src/Gamelist.cpp index 313670420..01e725103 100644 --- a/es-app/src/Gamelist.cpp +++ b/es-app/src/Gamelist.cpp @@ -1,3 +1,9 @@ +// +// Gamelist.cpp +// +// Parses and updates the gamelist.xml files. +// + #include "Gamelist.h" #include @@ -12,24 +18,24 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType type) { - // first, verify that path is within the system's root folder + // First, verify that path is within the system's root folder. FileData* root = system->getRootFolder(); bool contains = false; std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains); - if(!contains) - { - LOG(LogError) << "File path \"" << path << "\" is outside system path \"" << system->getStartPath() << "\""; - return NULL; + if (!contains) { + LOG(LogError) << "File path \"" << path << "\" is outside system path \"" << + system->getStartPath() << "\""; + return nullptr; } Utils::FileSystem::stringList pathList = Utils::FileSystem::getPathList(relative); auto path_it = pathList.begin(); FileData* treeNode = root; bool found = false; - while(path_it != pathList.end()) - { - const std::unordered_map& children = treeNode->getChildrenByFilename(); + while (path_it != pathList.end()) { + const std::unordered_map& children = + treeNode->getChildrenByFilename(); std::string key = *path_it; found = children.find(key) != children.cend(); @@ -37,40 +43,35 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType treeNode = children.at(key); } - // this is the end - if(path_it == --pathList.end()) - { - if(found) + // This is the end + if (path_it == --pathList.end()) { + if (found) return treeNode; - if(type == FOLDER) - { - LOG(LogWarning) << "gameList: folder doesn't already exist, won't create"; - return NULL; + if (type == FOLDER) { + LOG(LogWarning) << "Gamelist: folder doesn't exist, won't create"; + return nullptr; } FileData* file = new FileData(type, path, system->getSystemEnvData(), system); - // skipping arcade assets from gamelist - if(!file->isArcadeAsset()) - { + // Skipping arcade assets from gamelist. + if (!file->isArcadeAsset()) treeNode->addChild(file); - } return file; } - if(!found) - { - // don't create folders unless it's leading up to a game - // if type is a folder it's gonna be empty, so don't bother - if(type == FOLDER) - { - LOG(LogWarning) << "gameList: folder doesn't already exist, won't create"; - return NULL; + if (!found) { + // Don't create folders unless they're including any games. + // If the type is FOLDER it's going to be empty, so don't bother. + if (type == FOLDER) { + LOG(LogWarning) << "Gamelist: folder doesn't exist, won't create"; + return nullptr; } - // create missing folder - FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it, system->getSystemEnvData(), system); + // Create missing folder. + FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + + "/" + *path_it, system->getSystemEnvData(), system); treeNode->addChild(folder); treeNode = folder; } @@ -78,7 +79,7 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType path_it++; } - return NULL; + return nullptr; } void parseGamelist(SystemData* system) @@ -86,7 +87,7 @@ void parseGamelist(SystemData* system) bool trustGamelist = Settings::getInstance()->getBool("ParseGamelistOnly"); std::string xmlpath = system->getGamelistPath(false); - if(!Utils::FileSystem::exists(xmlpath)) + if (!Utils::FileSystem::exists(xmlpath)) return; LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\"..."; @@ -94,15 +95,14 @@ void parseGamelist(SystemData* system) pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); - if(!result) - { - LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description(); + if (!result) { + LOG(LogError) << "Error parsing XML file \"" << xmlpath << + "\"!\n " < node in gamelist \"" << xmlpath << "\"!"; return; } @@ -111,33 +111,32 @@ void parseGamelist(SystemData* system) const char* tagList[2] = { "game", "folder" }; FileType typeList[2] = { GAME, FOLDER }; - for(int i = 0; i < 2; i++) - { + for (int i = 0; i < 2; i++) { const char* tag = tagList[i]; FileType type = typeList[i]; - for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag)) - { - const std::string path = Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(), relativeTo, false); + for (pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = + fileNode.next_sibling(tag)) { + const std::string path = + Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(), + relativeTo, false); - if(!trustGamelist && !Utils::FileSystem::exists(path)) - { + if (!trustGamelist && !Utils::FileSystem::exists(path)) { LOG(LogWarning) << "File \"" << path << "\" does not exist! Ignoring."; continue; } FileData* file = findOrCreateFile(system, path, type); - if(!file) - { - LOG(LogError) << "Error finding/creating FileData for \"" << path << "\", skipping."; + if (!file) { + LOG(LogError) << "Error finding/creating FileData for \"" << + path << "\", skipping."; continue; } - else if(!file->isArcadeAsset()) - { + else if (!file->isArcadeAsset()) { std::string defaultName = file->metadata.get("name"); file->metadata = MetaDataList::createFromXML(GAME_METADATA, fileNode, relativeTo); - //make sure name gets set if one didn't exist - if(file->metadata.get("name").empty()) + // Make sure a name gets set if one doesn't exist. + if (file->metadata.get("name").empty()) file->metadata.set("name", defaultName); file->metadata.resetChangedFlag(); @@ -146,128 +145,137 @@ void parseGamelist(SystemData* system) } } -void addFileDataNode(pugi::xml_node& parent, const FileData* file, const char* tag, SystemData* system) +void addFileDataNode(pugi::xml_node& parent, const FileData* file, + const char* tag, SystemData* system) { - //create game and add to parent node + // Create game and add to parent node. pugi::xml_node newNode = parent.append_child(tag); - //write metadata + // Write metadata. file->metadata.appendToXML(newNode, true, system->getStartPath()); - if(newNode.children().begin() == newNode.child("name") //first element is name - && ++newNode.children().begin() == newNode.children().end() //theres only one element - && newNode.child("name").text().get() == file->getDisplayName()) //the name is the default - { - //if the only info is the default name, don't bother with this node - //delete it and ultimately do nothing - parent.remove_child(newNode); - }else{ - //there's something useful in there so we'll keep the node, add the path + // First element is "name", there's only one element and the name is the default. + if (newNode.children().begin() == newNode.child("name") && + ++newNode.children().begin() == newNode.children().end() && + newNode.child("name").text().get() == file->getDisplayName()) { - // try and make the path relative if we can so things still work if we change the rom folder location in the future - newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file->getPath(), system->getStartPath(), false).c_str()); + // If the only info is the default name, don't bother + // with this node, delete it and ultimately do nothing. + parent.remove_child(newNode); + } + else { + // There's something useful in there so we'll keep the node, add the path. + + // Try and make the path relative if we can so things still + // work if we change the ROM folder location in the future. + newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file-> + getPath(), system->getStartPath(), false).c_str()); } } void updateGamelist(SystemData* system) { - //We do this by reading the XML again, adding changes and then writing it back, - //because there might be information missing in our systemdata which would then miss in the new XML. - //We have the complete information for every game though, so we can simply remove a game - //we already have in the system from the XML, and then add it back from its GameData information... - - if(Settings::getInstance()->getBool("IgnoreGamelist")) + // We do this by reading the XML again, adding changes and then writing them back, + // because there might be information missing in our systemdata which we would otherwise + // miss in the new XML file. We have the complete information for every game though, so + // we can simply remove a game we already have in the system from the XML, and then add + // it back from its GameData information... + if (Settings::getInstance()->getBool("IgnoreGamelist")) return; pugi::xml_document doc; pugi::xml_node root; std::string xmlReadPath = system->getGamelistPath(false); - if(Utils::FileSystem::exists(xmlReadPath)) - { - //parse an existing file first + if (Utils::FileSystem::exists(xmlReadPath)) { + // Parse an existing file first. pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str()); - if(!result) - { - LOG(LogError) << "Error parsing XML file \"" << xmlReadPath << "\"!\n " << result.description(); + if (!result) { + LOG(LogError) << "Error parsing XML file \"" << xmlReadPath << "\"!\n " << + result.description(); return; } root = doc.child("gameList"); - if(!root) - { - LOG(LogError) << "Could not find node in gamelist \"" << xmlReadPath << "\"!"; + if (!root) { + LOG(LogError) << "Could not find node in gamelist \"" << + xmlReadPath << "\"!"; return; } - }else{ - //set up an empty gamelist to append to + } + else { + // Set up an empty gamelist to append to. root = doc.append_child("gameList"); } - - //now we have all the information from the XML. now iterate through all our games and add information from there + // Now we have all the information from the XML file, so iterate + // through all our games and add the information from there. FileData* rootFolder = system->getRootFolder(); - if (rootFolder != nullptr) - { + if (rootFolder != nullptr) { int numUpdated = 0; - //get only files, no folders + // Get only files, no folders. std::vector files = rootFolder->getFilesRecursive(GAME | FOLDER); - //iterate through all files, checking if they're already in the XML - for(std::vector::const_iterator fit = files.cbegin(); fit != files.cend(); ++fit) - { + // Iterate through all files, checking if they're already in the XML file. + for (std::vector::const_iterator fit = files.cbegin(); + fit != files.cend(); ++fit) { const char* tag = ((*fit)->getType() == GAME) ? "game" : "folder"; - // do not touch if it wasn't changed anyway + // Do not touch if it wasn't changed. if (!(*fit)->metadata.wasChanged()) continue; - // check if the file already exists in the XML - // if it does, remove it before adding - for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag)) - { + // Check if the file already exists in the XML file. + // If it does, remove the entry before adding it back. + for (pugi::xml_node fileNode = root.child(tag); fileNode; + fileNode = fileNode.next_sibling(tag)) { pugi::xml_node pathNode = fileNode.child("path"); - if(!pathNode) - { + if (!pathNode) { LOG(LogError) << "<" << tag << "> node contains no child!"; continue; } - std::string nodePath = Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath(pathNode.text().get(), system->getStartPath(), true)); + std::string nodePath =Utils::FileSystem::getCanonicalPath( + Utils::FileSystem::resolveRelativePath(pathNode.text().get(), + system->getStartPath(), true)); std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath()); - if(nodePath == gamePath) - { - // found it + if (nodePath == gamePath) { + // Found it root.remove_child(fileNode); break; } } - // it was either removed or never existed to begin with; either way, we can add it now + // It was either removed or never existed to begin with. + // Either way, we can add it now. addFileDataNode(root, *fit, tag, system); ++numUpdated; } - //now write the file - + // Now write the file. if (numUpdated > 0) { const auto startTs = std::chrono::system_clock::now(); - //make sure the folders leading up to this path exist (or the write will fail) + // Make sure the folders leading up to this path exist (or the write will fail). std::string xmlWritePath(system->getGamelistPath(true)); Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath)); - LOG(LogInfo) << "Added/Updated " << numUpdated << " entities in '" << xmlReadPath << "'"; + LOG(LogInfo) << "Added/Updated " << numUpdated << + " entities in '" << xmlReadPath << "'"; if (!doc.save_file(xmlWritePath.c_str())) { - LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath << "\" (for system " << system->getName() << ")!"; + LOG(LogError) << "Error saving gamelist.xml to \"" << + xmlWritePath << "\" (for system " << system->getName() << ")!"; } const auto endTs = std::chrono::system_clock::now(); - LOG(LogInfo) << "Saved gamelist.xml for system \"" << system->getName() << "\" in " << std::chrono::duration_cast(endTs - startTs).count() << " ms"; + LOG(LogInfo) << "Saved gamelist.xml for system \"" << system->getName() << "\" in " << + std::chrono::duration_cast + (endTs - startTs).count() << " ms"; } - }else{ + } + else { LOG(LogError) << "Found no root folder for system \"" << system->getName() << "\"!"; } } diff --git a/es-app/src/Gamelist.h b/es-app/src/Gamelist.h index d9502a196..0be4aea41 100644 --- a/es-app/src/Gamelist.h +++ b/es-app/src/Gamelist.h @@ -1,3 +1,9 @@ +// +// Gamelist.h +// +// Parses and updates the gamelist.xml files. +// + #pragma once #ifndef ES_APP_GAME_LIST_H #define ES_APP_GAME_LIST_H diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 5279ad15c..3e7ec983e 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -1,3 +1,10 @@ +// +// MetaData.cpp +// +// Static data for default metadata values as well as functions +// to read and write metadata from the gamelist files. +// + #include "MetaData.h" #include "utils/FileSystemUtil.h" @@ -24,7 +31,8 @@ MetaDataDecl gameDecls[] = { {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date"} }; -const std::vector gameMDD(gameDecls, gameDecls + sizeof(gameDecls) / sizeof(gameDecls[0])); +const std::vector gameMDD(gameDecls, gameDecls + + sizeof(gameDecls) / sizeof(gameDecls[0])); MetaDataDecl folderDecls[] = { {"name", MD_STRING, "", false, "name", "enter game name"}, @@ -37,12 +45,12 @@ MetaDataDecl folderDecls[] = { {"genre", MD_STRING, "unknown", false, "genre", "enter game genre"}, {"players", MD_INT, "1", false, "players", "enter number of players"} }; -const std::vector folderMDD(folderDecls, folderDecls + sizeof(folderDecls) / sizeof(folderDecls[0])); +const std::vector folderMDD(folderDecls, folderDecls + + sizeof(folderDecls) / sizeof(folderDecls[0])); const std::vector& getMDDByType(MetaDataListType type) { - switch(type) - { + switch(type) { case GAME_METADATA: return gameMDD; case FOLDER_METADATA: @@ -53,36 +61,31 @@ const std::vector& getMDDByType(MetaDataListType type) return gameMDD; } - - MetaDataList::MetaDataList(MetaDataListType type) - : mType(type), mWasChanged(false) + : mType(type), mWasChanged(false) { const std::vector& mdd = getMDD(); - for(auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) + for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) set(iter->key, iter->defaultValue); } - -MetaDataList MetaDataList::createFromXML(MetaDataListType type, pugi::xml_node& node, const std::string& relativeTo) +MetaDataList MetaDataList::createFromXML(MetaDataListType type, + pugi::xml_node& node, const std::string& relativeTo) { MetaDataList mdl(type); const std::vector& mdd = mdl.getMDD(); - for(auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) - { + for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) { pugi::xml_node md = node.child(iter->key.c_str()); - if(md) - { - // if it's a path, resolve relative paths + if (md) { + // If it's a path, resolve relative paths. std::string value = md.text().get(); if (iter->type == MD_PATH) - { value = Utils::FileSystem::resolveRelativePath(value, relativeTo, true); - } mdl.set(iter->key, value); - }else{ + } + else { mdl.set(iter->key, iter->defaultValue); } } @@ -90,21 +93,20 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type, pugi::xml_node& return mdl; } -void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults, const std::string& relativeTo) const +void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults, + const std::string& relativeTo) const { const std::vector& mdd = getMDD(); - for(auto mddIter = mdd.cbegin(); mddIter != mdd.cend(); mddIter++) - { + for (auto mddIter = mdd.cbegin(); mddIter != mdd.cend(); mddIter++) { auto mapIter = mMap.find(mddIter->key); - if(mapIter != mMap.cend()) - { - // we have this value! - // if it's just the default (and we ignore defaults), don't write it - if(ignoreDefaults && mapIter->second == mddIter->defaultValue) + if (mapIter != mMap.cend()) { + // We have this value! + // If it's just the default (and we ignore defaults), don't write it. + if (ignoreDefaults && mapIter->second == mddIter->defaultValue) continue; - // try and make paths relative if we can + // Try and make paths relative if we can. std::string value = mapIter->second; if (mddIter->type == MD_PATH) value = Utils::FileSystem::createRelativePath(value, relativeTo, true); @@ -122,7 +124,12 @@ void MetaDataList::set(const std::string& key, const std::string& value) const std::string& MetaDataList::get(const std::string& key) const { - return mMap.at(key); + // Check that the key actually exists, otherwise return empty string. + if (mMap.count(key) > 0) + return mMap.at(key); + else + + return mNoResult; } int MetaDataList::getInt(const std::string& key) const diff --git a/es-app/src/MetaData.h b/es-app/src/MetaData.h index 489970705..19bf97cf9 100644 --- a/es-app/src/MetaData.h +++ b/es-app/src/MetaData.h @@ -1,3 +1,10 @@ +// +// MetaData.h +// +// Static data for default metadata values as well as functions +// to read and write metadata from the gamelist files. +// + #pragma once #ifndef ES_APP_META_DATA_H #define ES_APP_META_DATA_H @@ -7,35 +14,35 @@ namespace pugi { class xml_node; } -enum MetaDataType -{ - //generic types +enum MetaDataType { + // Generic types MD_STRING, MD_INT, MD_FLOAT, MD_BOOL, - //specialized types + // Specialized types MD_MULTILINE_STRING, MD_LAUNCHSTRING, MD_PATH, MD_RATING, MD_DATE, - MD_TIME //used for lastplayed + MD_TIME // Used for lastplayed }; -struct MetaDataDecl -{ +struct MetaDataDecl { std::string key; MetaDataType type; std::string defaultValue; - bool isStatistic; //if true, ignore values for this metadata - std::string displayName; // displayed as this in editors - std::string displayPrompt; // phrase displayed in editors when prompted to enter value (currently only for strings) + // If true, ignore values for this metadata. + bool isStatistic; + // Displayed as this in editors. + std::string displayName; + // Phrase displayed in editors when prompted to enter value (currently only for strings). + std::string displayPrompt; }; -enum MetaDataListType -{ +enum MetaDataListType { GAME_METADATA, FOLDER_METADATA }; @@ -45,8 +52,10 @@ const std::vector& getMDDByType(MetaDataListType type); class MetaDataList { public: - static MetaDataList createFromXML(MetaDataListType type, pugi::xml_node& node, const std::string& relativeTo); - void appendToXML(pugi::xml_node& parent, bool ignoreDefaults, const std::string& relativeTo) const; + static MetaDataList createFromXML(MetaDataListType type, + pugi::xml_node& node, const std::string& relativeTo); + void appendToXML(pugi::xml_node& parent, bool ignoreDefaults, + const std::string& relativeTo) const; MetaDataList(MetaDataListType type); @@ -65,6 +74,7 @@ public: private: MetaDataListType mType; std::map mMap; + std::string mNoResult = ""; bool mWasChanged; }; diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 11ff0b830..417d541bd 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -1,3 +1,12 @@ +// +// SystemData.cpp +// +// Provides data structures for the game systems and populates and indexes them based +// on the configuration in es_systems.cfg as well as the presence of game ROM files. +// Also provides functions to read and write to the gamelist files and to handle theme +// loading. +// + #include "SystemData.h" #include "utils/FileSystemUtil.h" @@ -18,31 +27,44 @@ std::vector SystemData::sSystemVector; -SystemData::SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem) : - mName(name), mFullName(fullName), mEnvData(envData), mThemeFolder(themeFolder), mIsCollectionSystem(CollectionSystem), mIsGameSystem(true) +SystemData::SystemData( + const std::string& name, + const std::string& fullName, + SystemEnvironmentData* envData, + const std::string& themeFolder, + bool CollectionSystem) + : mName(name), + mFullName(fullName), + mEnvData(envData), + mThemeFolder(themeFolder), + mIsCollectionSystem(CollectionSystem), + mIsGameSystem(true) { mFilterIndex = new FileFilterIndex(); - // if it's an actual system, initialize it, if not, just create the data structure - if(!CollectionSystem) - { + // If it's an actual system, initialize it, if not, just create the data structure. + if (!CollectionSystem) { mRootFolder = new FileData(FOLDER, mEnvData->mStartPath, mEnvData, this); mRootFolder->metadata.set("name", mFullName); - if(!Settings::getInstance()->getBool("ParseGamelistOnly")) + if (!Settings::getInstance()->getBool("ParseGamelistOnly")) populateFolder(mRootFolder); - if(!Settings::getInstance()->getBool("IgnoreGamelist")) + if (!Settings::getInstance()->getBool("IgnoreGamelist")) parseGamelist(this); - mRootFolder->sort(FileSorts::SortTypes.at(0)); + setupSystemSortType(mRootFolder); + + mRootFolder->sort(getSortTypeFromString(mRootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavoritesFirst")); indexAllGameFilters(mRootFolder); } - else - { - // virtual systems are updated afterwards, we're just creating the data structure + else { + // Virtual systems are updated afterwards by CollectionSystemManager. + // We're just creating the data structure here. mRootFolder = new FileData(FOLDER, "" + name, mEnvData, this); + setupSystemSortType(mRootFolder); } setIsGameSystemStatus(); loadTheme(); @@ -50,7 +72,7 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, Sys SystemData::~SystemData() { - if(Settings::getInstance()->getString("SaveGamelistsMode") == "on exit") + if (Settings::getInstance()->getString("SaveGamelistsMode") == "on exit") writeMetaData(); delete mRootFolder; @@ -59,27 +81,25 @@ SystemData::~SystemData() void SystemData::setIsGameSystemStatus() { - // we exclude non-game systems from specific operations (i.e. the "RetroPie" system, at least) - // if/when there are more in the future, maybe this can be a more complex method, with a proper list - // but for now a simple string comparison is more performant + // We exclude non-game systems from specific operations (i.e. the "RetroPie" system, at least). + // If/when there are more in the future, maybe this can be a more complex method, with a proper + // list but for now a simple string comparison is more performant. mIsGameSystem = (mName != "retropie"); } void SystemData::populateFolder(FileData* folder) { const std::string& folderPath = folder->getPath(); - if(!Utils::FileSystem::isDirectory(folderPath)) - { + if (!Utils::FileSystem::isDirectory(folderPath)) { LOG(LogWarning) << "Error - folder with path \"" << folderPath << "\" is not a directory!"; return; } - //make sure that this isn't a symlink to a thing we already have - if(Utils::FileSystem::isSymlink(folderPath)) - { - //if this symlink resolves to somewhere that's at the beginning of our path, it's gonna recurse - if(folderPath.find(Utils::FileSystem::getCanonicalPath(folderPath)) == 0) - { + // Make sure that this isn't a symlink to an object we already have. + if (Utils::FileSystem::isSymlink(folderPath)) { + // If this symlink resolves to somewhere that's at the beginning of our + // path, it's going to create a recursive loop. Make sure to avoid this. + if (folderPath.find(Utils::FileSystem::getCanonicalPath(folderPath)) == 0) { LOG(LogWarning) << "Skipping infinitely recursive symlink \"" << folderPath << "\""; return; } @@ -90,42 +110,42 @@ void SystemData::populateFolder(FileData* folder) bool isGame; bool showHidden = Settings::getInstance()->getBool("ShowHiddenFiles"); Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(folderPath); - for(Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); it != dirContent.cend(); ++it) - { + for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); + it != dirContent.cend(); ++it) { filePath = *it; - // skip hidden files and folders - if(!showHidden && Utils::FileSystem::isHidden(filePath)) + // Skip hidden files and folders. + if (!showHidden && Utils::FileSystem::isHidden(filePath)) continue; - //this is a little complicated because we allow a list of extensions to be defined (delimited with a space) - //we first get the extension of the file itself: + // This is a little complicated because we allow a list + // of extensions to be defined (delimited with a space). + // We first get the extension of the file itself: extension = Utils::FileSystem::getExtension(filePath); - //fyi, folders *can* also match the extension and be added as games - this is mostly just to support higan - //see issue #75: https://github.com/Aloshi/EmulationStation/issues/75 + // FYI, folders *can* also match the extension and be added as games. + // This is mostly just to support higan. + // See issue #75: https://github.com/Aloshi/EmulationStation/issues/75 isGame = false; - if(std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(), extension) != mEnvData->mSearchExtensions.cend()) - { + if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(), + extension) != mEnvData->mSearchExtensions.cend()) { FileData* newGame = new FileData(GAME, filePath, mEnvData, this); - // preventing new arcade assets to be added - if(!newGame->isArcadeAsset()) - { + // Prevent new arcade assets from being added. + if (!newGame->isArcadeAsset()) { folder->addChild(newGame); isGame = true; } } - //add directories that also do not match an extension as folders - if(!isGame && Utils::FileSystem::isDirectory(filePath)) - { + // Add directories that also do not match an extension as folders. + if (!isGame && Utils::FileSystem::isDirectory(filePath)) { FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this); populateFolder(newFolder); - //ignore folders that do not contain games - if(newFolder->getChildrenByFilename().size() == 0) + // Ignore folders that do not contain games. + if (newFolder->getChildrenByFilename().size() == 0) delete newFolder; else folder->addChild(newFolder); @@ -137,12 +157,17 @@ void SystemData::indexAllGameFilters(const FileData* folder) { const std::vector& children = folder->getChildren(); - for(std::vector::const_iterator it = children.cbegin(); it != children.cend(); ++it) - { - switch((*it)->getType()) - { - case GAME: { mFilterIndex->addToIndex(*it); } break; - case FOLDER: { indexAllGameFilters(*it); } break; + for (std::vector::const_iterator it = children.cbegin(); + it != children.cend(); ++it) { + switch ((*it)->getType()) { + case GAME: { + mFilterIndex->addToIndex(*it); + } + break; + case FOLDER: { + indexAllGameFilters(*it); + } + break; } } } @@ -153,8 +178,7 @@ std::vector readList(const std::string& str, const char* delims = " size_t prevOff = str.find_first_not_of(delims, 0); size_t off = str.find_first_of(delims, prevOff); - while(off != std::string::npos || prevOff != std::string::npos) - { + while (off != std::string::npos || prevOff != std::string::npos) { ret.push_back(str.substr(prevOff, off - prevOff)); prevOff = str.find_first_not_of(delims, off); @@ -164,7 +188,7 @@ std::vector readList(const std::string& str, const char* delims = " return ret; } -//creates systems from information located in a config file +// Creates systems from information located in a config file. bool SystemData::loadConfig() { deleteSystems(); @@ -173,8 +197,7 @@ bool SystemData::loadConfig() LOG(LogInfo) << "Loading system config file " << path << "..."; - if(!Utils::FileSystem::exists(path)) - { + if (!Utils::FileSystem::exists(path)) { LOG(LogError) << "es_systems.cfg file does not exist!"; writeExampleConfig(getConfigPath(true)); return false; @@ -183,80 +206,78 @@ bool SystemData::loadConfig() pugi::xml_document doc; pugi::xml_parse_result res = doc.load_file(path.c_str()); - if(!res) - { + if (!res) { LOG(LogError) << "Could not parse es_systems.cfg file!"; LOG(LogError) << res.description(); return false; } - //actually read the file + // Actually read the file. pugi::xml_node systemList = doc.child("systemList"); - if(!systemList) - { + if (!systemList) { LOG(LogError) << "es_systems.cfg is missing the tag!"; return false; } - for(pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system")) - { + for (pugi::xml_node system = systemList.child("system"); system; + system = system.next_sibling("system")) { std::string name, fullname, path, cmd, themeFolder; name = system.child("name").text().get(); fullname = system.child("fullname").text().get(); path = system.child("path").text().get(); - // convert extensions list from a string into a vector of strings + // Convert extensions list from a string into a vector of strings. std::vector extensions = readList(system.child("extension").text().get()); cmd = system.child("command").text().get(); - // platform id list + // Platform ID list const char* platformList = system.child("platform").text().get(); std::vector platformStrs = readList(platformList); std::vector platformIds; - for(auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) - { + for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) { const char* str = it->c_str(); PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str); - if(platformId == PlatformIds::PLATFORM_IGNORE) - { - // when platform is ignore, do not allow other platforms + if (platformId == PlatformIds::PLATFORM_IGNORE) { + // When platform is PLATFORM_IGNORE, do not allow other platforms. platformIds.clear(); platformIds.push_back(platformId); break; } - // if there appears to be an actual platform ID supplied but it didn't match the list, warn - if(str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN) - LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" << str << "\" from list \"" << platformList << "\")"; - else if(platformId != PlatformIds::PLATFORM_UNKNOWN) + // If there appears to be an actual platform ID supplied + // but it didn't match the list, generate a warning. + if (str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN) + LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" + << str << "\" from list \"" << platformList << "\")"; + else if (platformId != PlatformIds::PLATFORM_UNKNOWN) platformIds.push_back(platformId); } - // theme folder + // Theme folder. themeFolder = system.child("theme").text().as_string(name.c_str()); - //validate - if(name.empty() || path.empty() || extensions.empty() || cmd.empty()) - { - LOG(LogError) << "System \"" << name << "\" is missing name, path, extension, or command!"; + // Validate. + if (name.empty() || path.empty() || extensions.empty() || cmd.empty()) { + LOG(LogError) << "System \"" << name << + "\" is missing name, path, extension, or command!"; continue; } - //convert path to generic directory seperators + // Convert path to generic directory seperators. path = Utils::FileSystem::getGenericPath(path); - //expand home symbol if the startpath contains ~ - if(path[0] == '~') + // Expand home symbol if the startpath contains ~ + if (path[0] == '~') { path.erase(0, 1); path.insert(0, Utils::FileSystem::getHomePath()); } - //create the system runtime environment data + // Create the system runtime environment data. SystemEnvironmentData* envData = new SystemEnvironmentData; envData->mStartPath = path; envData->mSearchExtensions = extensions; @@ -264,11 +285,11 @@ bool SystemData::loadConfig() envData->mPlatformIds = platformIds; SystemData* newSys = new SystemData(name, fullname, envData, themeFolder); - if(newSys->getRootFolder()->getChildrenByFilename().size() == 0) - { + if (newSys->getRootFolder()->getChildrenByFilename().size() == 0) { LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it."; delete newSys; - }else{ + } + else { sSystemVector.push_back(newSys); } } @@ -325,17 +346,16 @@ void SystemData::writeExampleConfig(const std::string& path) void SystemData::deleteSystems() { - for(unsigned int i = 0; i < sSystemVector.size(); i++) - { + for (unsigned int i = 0; i < sSystemVector.size(); i++) delete sSystemVector.at(i); - } + sSystemVector.clear(); } std::string SystemData::getConfigPath(bool forWrite) { std::string path = Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg"; - if(forWrite || Utils::FileSystem::exists(path)) + if (forWrite || Utils::FileSystem::exists(path)) return path; return "/etc/emulationstation/es_systems.cfg"; @@ -352,12 +372,13 @@ SystemData* SystemData::getNext() const { std::vector::const_iterator it = getIterator(); + // As we are starting in a valid gamelistview, this will + // always succeed, even if we have to come full circle. do { it++; if (it == sSystemVector.cend()) it = sSystemVector.cbegin(); } while (!(*it)->isVisible()); - // as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle. return *it; } @@ -366,12 +387,13 @@ SystemData* SystemData::getPrev() const { std::vector::const_reverse_iterator it = getRevIterator(); + // As we are starting in a valid gamelistview, this will + // always succeed, even if we have to come full circle. do { it++; if (it == sSystemVector.crend()) it = sSystemVector.crbegin(); } while (!(*it)->isVisible()); - // as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle. return *it; } @@ -381,13 +403,15 @@ std::string SystemData::getGamelistPath(bool forWrite) const std::string filePath; filePath = mRootFolder->getPath() + "/gamelist.xml"; - if(Utils::FileSystem::exists(filePath)) + if (Utils::FileSystem::exists(filePath)) return filePath; - filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName + "/gamelist.xml"; - if(forWrite) // make sure the directory exists if we're going to write to it, or crashes will happen + filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + + mName + "/gamelist.xml"; + // Make sure the directory exists if we're going to write to it, or crashes will happen. + if (forWrite) Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(filePath)); - if(forWrite || Utils::FileSystem::exists(filePath)) + if (forWrite || Utils::FileSystem::exists(filePath)) return filePath; return "/etc/emulationstation/gamelists/" + mName + "/gamelist.xml"; @@ -395,24 +419,25 @@ std::string SystemData::getGamelistPath(bool forWrite) const std::string SystemData::getThemePath() const { - // where we check for themes, in order: + // Locations where we check for themes, in the following order: // 1. [SYSTEM_PATH]/theme.xml - // 2. system theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/theme.xml - // 3. default system theme from currently selected theme set [CURRENT_THEME_PATH]/theme.xml + // 2. System theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/theme.xml + // 3. Default system theme from currently selected theme set [CURRENT_THEME_PATH]/theme.xml - // first, check game folder + // First, check game folder. std::string localThemePath = mRootFolder->getPath() + "/theme.xml"; - if(Utils::FileSystem::exists(localThemePath)) + if (Utils::FileSystem::exists(localThemePath)) return localThemePath; - // not in game folder, try system theme in theme sets + // Not in game folder, try system theme in theme sets. localThemePath = ThemeData::getThemeFromCurrentSet(mThemeFolder); if (Utils::FileSystem::exists(localThemePath)) return localThemePath; - // not system theme, try default system theme in theme set - localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml"; + // Not system theme, try default system theme in theme set. + localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + + "/theme.xml"; return localThemePath; } @@ -429,32 +454,27 @@ unsigned int SystemData::getGameCount() const SystemData* SystemData::getRandomSystem() { - // this is a bit brute force. It might be more efficient to just to a while (!gameSystem) do random again... + // This is a bit brute force. + // It might be more efficient to just do a while (!gameSystem) do random again... unsigned int total = 0; - for(auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++) - { + for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++) { if ((*it)->isGameSystem()) - total ++; + total++; } - // get random number in range + // Get a random number in range. int target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1)); for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++) { - if ((*it)->isGameSystem()) - { + if ((*it)->isGameSystem()) { if (target > 0) - { target--; - } else - { return (*it); - } } } - // if we end up here, there is no valid system + // If we end up here, there is no valid system. return NULL; } @@ -463,10 +483,12 @@ FileData* SystemData::getRandomGame() std::vector list = mRootFolder->getFilesRecursive(GAME, true); unsigned int total = (int)list.size(); int target = 0; - // get random number in range + + // Get a random number in range. if (total == 0) return NULL; target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1)); + return list.at(target); } @@ -481,36 +503,54 @@ void SystemData::loadTheme() std::string path = getThemePath(); - if(!Utils::FileSystem::exists(path)) // no theme available for this platform + if (!Utils::FileSystem::exists(path)) // No theme available for this platform. return; - try - { - // build map with system variables for theme to use, + try { + // Build map with system variables for theme to use. std::map sysData; sysData.insert(std::pair("system.name", getName())); sysData.insert(std::pair("system.theme", getThemeFolder())); sysData.insert(std::pair("system.fullName", getFullName())); mTheme->loadFile(sysData, path); - } catch(ThemeException& e) - { + } + catch (ThemeException& e) { LOG(LogError) << e.what(); - mTheme = std::make_shared(); // reset to empty + mTheme = std::make_shared(); // Reset to empty. } } void SystemData::writeMetaData() { - if(Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem) + if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem) return; - //save changed game data back to xml + // Save changed game data back to xml. updateGamelist(this); } void SystemData::onMetaDataSavePoint() { - if(Settings::getInstance()->getString("SaveGamelistsMode") != "always") + if (Settings::getInstance()->getString("SaveGamelistsMode") != "always") return; writeMetaData(); } + +void SystemData::setupSystemSortType(FileData* mRootFolder) +{ + // If DefaultSortOrder is set to something, check that it is actually a valid value. + if (Settings::getInstance()->getString("DefaultSortOrder") != "") { + for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { + if (FileSorts::SortTypes.at(i).description == + Settings::getInstance()->getString("DefaultSortOrder")) { + mRootFolder->setSortTypeString(Settings::getInstance()-> + getString("DefaultSortOrder")); + break; + } + } + } + // If no valid sort type was defined in the configuration + // file, set sorting to "filename, ascending". + if (mRootFolder->getSortTypeString() == "") + mRootFolder->setSortTypeString(FileSorts::SortTypes.at(0).description); +} diff --git a/es-app/src/SystemData.h b/es-app/src/SystemData.h index b72e48e38..e98485c55 100644 --- a/es-app/src/SystemData.h +++ b/es-app/src/SystemData.h @@ -1,3 +1,12 @@ +// +// SystemData.h +// +// Provides data structures for the game systems and populates and indexes them based +// on the configuration in es_systems.cfg as well as the presence of game ROM files. +// Also provides functions to read and write to the gamelist files and to handle theme +// loading. +// + #pragma once #ifndef ES_APP_SYSTEM_DATA_H #define ES_APP_SYSTEM_DATA_H @@ -23,18 +32,27 @@ struct SystemEnvironmentData class SystemData { public: - SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem = false); + SystemData(const std::string& name, + const std::string& fullName, + SystemEnvironmentData* envData, + const std::string& themeFolder, + bool CollectionSystem = false); + ~SystemData(); inline FileData* getRootFolder() const { return mRootFolder; }; inline const std::string& getName() const { return mName; } inline const std::string& getFullName() const { return mFullName; } inline const std::string& getStartPath() const { return mEnvData->mStartPath; } - inline const std::vector& getExtensions() const { return mEnvData->mSearchExtensions; } + inline const std::vector& getExtensions() const + { return mEnvData->mSearchExtensions; } inline const std::string& getThemeFolder() const { return mThemeFolder; } inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } - inline const std::vector& getPlatformIds() const { return mEnvData->mPlatformIds; } - inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) != mEnvData->mPlatformIds.cend(); } + inline const std::vector& getPlatformIds() const + { return mEnvData->mPlatformIds; } + inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; + return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) + != mEnvData->mPlatformIds.cend(); } inline const std::shared_ptr& getTheme() const { return mTheme; } @@ -46,14 +64,21 @@ public: unsigned int getDisplayedGameCount() const; static void deleteSystems(); - static bool loadConfig(); //Load the system config file at getConfigPath(). Returns true if no errors were encountered. An example will be written if the file doesn't exist. + // Load the system config file at getConfigPath(). + // Returns true if no errors were encountered. + // An example will be written if the file doesn't exist. + static bool loadConfig(); static void writeExampleConfig(const std::string& path); - static std::string getConfigPath(bool forWrite); // if forWrite, will only return ~/.emulationstation/es_systems.cfg, never /etc/emulationstation/es_systems.cfg + // If forWrite, will only return ~/.emulationstation/es_systems.cfg, + // never /etc/emulationstation/es_systems.cfg. + static std::string getConfigPath(bool forWrite); static std::vector sSystemVector; - inline std::vector::const_iterator getIterator() const { return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); }; - inline std::vector::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); }; + inline std::vector::const_iterator getIterator() const + { return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); }; + inline std::vector::const_reverse_iterator getRevIterator() const + { return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); }; inline bool isCollection() { return mIsCollectionSystem; }; inline bool isGameSystem() { return mIsGameSystem; }; @@ -70,6 +95,8 @@ public: FileFilterIndex* getIndex() { return mFilterIndex; }; void onMetaDataSavePoint(); + void setupSystemSortType(FileData* mRootFolder); + private: bool mIsCollectionSystem; bool mIsGameSystem; diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.cpp b/es-app/src/guis/GuiCollectionSystemsOptions.cpp index ede6f3eb6..8f9b09ba0 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.cpp +++ b/es-app/src/guis/GuiCollectionSystemsOptions.cpp @@ -71,9 +71,9 @@ void GuiCollectionSystemsOptions::initializeMenu() bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem")); mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections); - sortAllSystemsSwitch = std::make_shared(mWindow); - sortAllSystemsSwitch->setState(Settings::getInstance()->getBool("SortAllSystems")); - mMenu.addWithLabel("SORT CUSTOM COLLECTIONS AND SYSTEMS", sortAllSystemsSwitch); + sortFavFirstCustomSwitch = std::make_shared(mWindow); + sortFavFirstCustomSwitch->setState(Settings::getInstance()->getBool("FavFirstCustom")); + mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch); toggleSystemNameInCollections = std::make_shared(mWindow); toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo")); @@ -170,8 +170,8 @@ void GuiCollectionSystemsOptions::applySettings() std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto"); std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects()); std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom"); - bool outSort = sortAllSystemsSwitch->getState(); - bool prevSort = Settings::getInstance()->getBool("SortAllSystems"); + bool outSort = sortFavFirstCustomSwitch->getState(); + bool prevSort = Settings::getInstance()->getBool("FavFirstCustom"); bool outBundle = bundleCustomCollections->getState(); bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem"); bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo"); @@ -189,7 +189,7 @@ void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, st { Settings::getInstance()->setString("CollectionSystemsAuto", newAutoSettings); Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings); - Settings::getInstance()->setBool("SortAllSystems", sortAllSystemsSwitch->getState()); + Settings::getInstance()->setBool("FavFirstCustom", sortFavFirstCustomSwitch->getState()); Settings::getInstance()->setBool("UseCustomCollectionsSystem", bundleCustomCollections->getState()); Settings::getInstance()->setBool("CollectionShowSystemInfo", toggleSystemNameInCollections->getState()); Settings::getInstance()->saveFile(); diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.h b/es-app/src/guis/GuiCollectionSystemsOptions.h index dd19bf962..c3f4508c8 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.h +++ b/es-app/src/guis/GuiCollectionSystemsOptions.h @@ -28,8 +28,8 @@ private: void exitEditMode(); std::shared_ptr< OptionListComponent > autoOptionList; std::shared_ptr< OptionListComponent > customOptionList; - std::shared_ptr sortAllSystemsSwitch; std::shared_ptr bundleCustomCollections; + std::shared_ptr sortFavFirstCustomSwitch; std::shared_ptr toggleSystemNameInCollections; MenuComponent mMenu; SystemData* mSystem; diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 44d767186..3cfa17ff2 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -1,3 +1,10 @@ +// +// GuiGamelistOptions.cpp +// +// Gamelist options menu for the 'Jump to...' quick selector, +// game sorting, game filters, and metadata edit. +// + #include "GuiGamelistOptions.h" #include "guis/GuiGamelistFilter.h" @@ -12,43 +19,50 @@ #include "SystemData.h" #include "Sound.h" -GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : GuiComponent(window), - mSystem(system), mMenu(window, "OPTIONS"), fromPlaceholder(false), mFiltersChanged(false) +GuiGamelistOptions::GuiGamelistOptions( + Window* window, + SystemData* system) + : GuiComponent(window), + mSystem(system), + mMenu(window, "OPTIONS"), + fromPlaceholder(false), + mFiltersChanged(false) { addChild(&mMenu); - // check it's not a placeholder folder - if it is, only show "Filter Options" + // Check that it's not a placeholder folder - if it is, only show "Filter Options". FileData* file = getGamelist()->getCursor(); fromPlaceholder = file->isPlaceHolder(); ComponentListRow row; + // Read the applicable favorite sorting setting depending on whether the + // system is a custom collection or not. + if (CollectionSystemManager::get()->getIsCustomCollection(file->getSystem())) + mFavoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); + else + mFavoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); + if (!fromPlaceholder) { - // jump to letter + // Jump to letter. row.elements.clear(); - // define supported character range - // this range includes all numbers, capital letters, and most reasonable symbols + // Define supported character range. + // This range includes all numbers, capital letters, and most reasonable symbols. char startChar = '!'; char endChar = '_'; char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]); - if(curChar < startChar || curChar > endChar) + if (curChar < startChar || curChar > endChar) curChar = startChar; mJumpToLetterList = std::make_shared(mWindow, "JUMP TO...", false); - if(Settings::getInstance()->getBool("FavoritesFirst") && system->getName() != "favorites" && system->getName() != "recent") - { - // set firstFavorite to the numerical entry of the first favorite game in the list - // if no favorites are found set it to -1 - findFirstFavorite(); - - // if the currently selected game is a favorite, set curChar to 0 so we don't get two current positions - if(getGamelist()->getCursor()->getFavorite()) - curChar = 0; - - if (firstFavorite != -1) - { + if (mFavoritesSorting && system->getName() != "favorites" && + system->getName() != "recent") { + // Check whether the first game in the list is a favorite, if it's + // not, then there are no favorites currently visible in this gamelist. + if (getGamelist()->getCursor()->getParent()->getChildrenListToDisplay()[0]-> + getFavorite()) { if (getGamelist()->getCursor()->getFavorite()) mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 1); else @@ -56,117 +70,147 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui } } - for (char c = startChar; c <= endChar; c++) - { - // check if c is a valid first letter in current list - const std::vector& files = getGamelist()->getCursor()->getParent()->getChildrenListToDisplay(); - for (auto file : files) - { + for (char c = startChar; c <= endChar; c++) { + // Check if c is a valid first letter in the current list. + const std::vector& files = + getGamelist()->getCursor()->getParent()->getChildrenListToDisplay(); + for (auto file : files) { char candidate = (char)toupper(file->getSortName()[0]); - if (c == candidate) - { - // if the game is a favorite, continue to the next entry in the list - if (firstFavorite != -1 && file->getFavorite()) + if (c == candidate) { + // If the game is a favorite, continue to the next entry in the list. + if (mFavoritesSorting && system->getName() != "favorites" && + system->getName() != "recent" && file->getFavorite()) continue; - mJumpToLetterList->add(std::string(1, c), std::string(1, c), c == curChar); + // If the currently selected game is a favorite, set the character + // as not selected so we don't get two current positions. + if (mFavoritesSorting && system->getName() != "favorites" && + system->getName() != "recent" && + getGamelist()->getCursor()->getFavorite()) + mJumpToLetterList->add(std::string(1, c), std::string(1, c), 0); + else + mJumpToLetterList->add(std::string(1, c), std::string(1, c), c == curChar); break; } } } - row.addElement(std::make_shared(mWindow, "JUMP TO...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, "JUMP TO...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(mJumpToLetterList, false); row.input_handler = [&](InputConfig* config, Input input) { - if(config->isMappedTo("a", input) && input.value) - { - if(mJumpToLetterList->getSelected() == FAVORITE_CHAR) - { - navigationsounds.playThemeNavigationSound(SCROLLSOUND); - jumpToFirstFavorite(); - return true; - } - navigationsounds.playThemeNavigationSound(SCROLLSOUND); - jumpToLetter(); + if (config->isMappedTo("a", input) && input.value) { + navigationsounds.playThemeNavigationSound(SCROLLSOUND); + if (mJumpToLetterList->getSelected() == FAVORITE_CHAR) + jumpToFirstRow(); + else + jumpToLetter(); + return true; } - else if(mJumpToLetterList->input(config, input)) - { + else if (mJumpToLetterList->input(config, input)) { return true; } return false; }; - mMenu.addRow(row); + if (system->getName() != "recent") + mMenu.addRow(row); - // sort list by + // Sort list by selected sort type (persistent throughout the program session). mListSort = std::make_shared(mWindow, "SORT GAMES BY", false); - for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) - { - const FileData::SortType& sort = FileSorts::SortTypes.at(i); - mListSort->add(sort.description, &sort, i == 0); // TODO - actually make the sort type persistent - } + FileData* root = mSystem->getRootFolder(); + std::string sortType = root->getSortTypeString(); - mMenu.addWithLabel("SORT GAMES BY", mListSort); + for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { + const FileData::SortType& sort = FileSorts::SortTypes.at(i); + if (sort.description == sortType) + mListSort->add(sort.description, &sort, 1); + else + mListSort->add(sort.description, &sort, 0); + } + // Don't show the sort type option if the gamelist type is recent/last played. + if (system->getName() != "recent") + mMenu.addWithLabel("SORT GAMES BY", mListSort); } - // show filtered menu - if(!Settings::getInstance()->getBool("ForceDisableFilters")) - { + + // Show filtered menu. + if (system->getName() != "recent" && !Settings::getInstance()->getBool("ForceDisableFilters")) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this)); mMenu.addRow(row); } - std::map customCollections = CollectionSystemManager::get()->getCustomCollectionSystems(); + std::map customCollections = + CollectionSystemManager::get()->getCustomCollectionSystems(); - if(UIModeController::getInstance()->isUIModeFull() && - ((customCollections.find(system->getName()) != customCollections.cend() && CollectionSystemManager::get()->getEditingCollection() != system->getName()) || - CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == system->getName())) - { + if (UIModeController::getInstance()->isUIModeFull() && + ((customCollections.find(system->getName()) != customCollections.cend() && + CollectionSystemManager::get()->getEditingCollection() != system->getName()) || + CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == + system->getName())) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this)); mMenu.addRow(row); } - if(UIModeController::getInstance()->isUIModeFull() && CollectionSystemManager::get()->isEditing()) - { + if (UIModeController::getInstance()->isUIModeFull() && + CollectionSystemManager::get()->isEditing()) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "FINISH EDITING '" + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, "FINISH EDITING '" + Utils::String::toUpper( + CollectionSystemManager::get()->getEditingCollection()) + + "' COLLECTION",Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this)); mMenu.addRow(row); } - if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder && !(mSystem->isCollection() && file->getType() == FOLDER)) - { + if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder && + !(mSystem->isCollection() && file->getType() == FOLDER)) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, + "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); mMenu.addRow(row); } - // center the menu + // Center the menu. setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2); + mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - + mMenu.getSize().y()) / 2); } GuiGamelistOptions::~GuiGamelistOptions() { - // apply sort if (!fromPlaceholder) { FileData* root = mSystem->getRootFolder(); - root->sort(*mListSort->getSelected()); // will also recursively sort children - // notify that the root folder was sorted - getGamelist()->onFileChanged(root, FILE_SORTED); + // If a new sorting type was selected, then sort and update mSortTypeString for the system. + if ((*mListSort->getSelected()).description != root->getSortTypeString()) { + navigationsounds.playThemeNavigationSound(SCROLLSOUND); + + // This will also recursively sort children. + root->sort(*mListSort->getSelected(), mFavoritesSorting); + root->setSortTypeString((*mListSort->getSelected()).description); + + // Select the first row of the gamelist. + FileData* firstRow = getGamelist()->getCursor()->getParent()-> + getChildrenListToDisplay()[0]; + getGamelist()->setCursor(firstRow); + + // Notify that the root folder was sorted. + getGamelist()->onFileChanged(root, FILE_SORTED); + } } - if (mFiltersChanged) - { - // only reload full view if we came from a placeholder - // as we need to re-display the remaining elements for whatever new - // game is selected + if (mFiltersChanged) { + // Only reload full view if we came from a placeholder as we need to + // re-display the remaining elements for whatever new game is selected. ViewController::get()->reloadGameListView(mSystem); } } @@ -181,20 +225,16 @@ void GuiGamelistOptions::openGamelistFilter() void GuiGamelistOptions::startEditMode() { std::string editingSystem = mSystem->getName(); - // need to check if we're editing the collections bundle, as we will want to edit the selected collection within - if(editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) - { + // Need to check if we're editing the collections bundle, + // as we will want to edit the selected collection within. + if (editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) { FileData* file = getGamelist()->getCursor(); - // do we have the cursor on a specific collection? + // Do we have the cursor on a specific collection?. if (file->getType() == FOLDER) - { editingSystem = file->getName(); - } else - { - // we are inside a specific collection. We want to edit that one. + // We are inside a specific collection. We want to edit that one. editingSystem = file->getSystem()->getName(); - } } CollectionSystemManager::get()->setEditMode(editingSystem); delete this; @@ -208,8 +248,8 @@ void GuiGamelistOptions::exitEditMode() void GuiGamelistOptions::openMetaDataEd() { - // open metadata editor - // get the FileData that hosts the original metadata + // Open metadata editor. + // Get the FileData that holds the original metadata. FileData* file = getGamelist()->getCursor()->getSourceFileData(); ScraperSearchParams p; p.game = file; @@ -217,106 +257,63 @@ void GuiGamelistOptions::openMetaDataEd() std::function deleteBtnFunc; - if (file->getType() == FOLDER) - { + if (file->getType() == FOLDER) { deleteBtnFunc = NULL; } - else - { + else { deleteBtnFunc = [this, file] { CollectionSystemManager::get()->deleteCollectionFiles(file); ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true); }; } - mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p, Utils::FileSystem::getFileName(file->getPath()), - std::bind(&IGameListView::onFileChanged, ViewController::get()->getGameListView(file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc)); + mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p, + Utils::FileSystem::getFileName(file->getPath()), std::bind( + &IGameListView::onFileChanged, ViewController::get()->getGameListView( + file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc)); } void GuiGamelistOptions::jumpToLetter() { char letter = mJumpToLetterList->getSelected()[0]; - IGameListView* gamelist = getGamelist(); - // this is a really shitty way to get a list of files - const std::vector& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay(); + // Get first row of the gamelist. + const std::vector& files = getGamelist()->getCursor()-> + getParent()->getChildrenListToDisplay(); - long min = 0; - long max = (long)files.size() - 1; - long mid = 0; - - while(max >= min) - { - mid = ((max - min) / 2) + min; - - // game somehow has no first character to check - if(files.at(mid)->getName().empty()) - continue; - - char checkLetter = (char)toupper(files.at(mid)->getSortName()[0]); - - if(checkLetter < letter) - min = mid + 1; - else if(checkLetter > letter || (mid > 0 && (letter == toupper(files.at(mid - 1)->getSortName()[0])))) - max = mid - 1; - else - { - // exact match found - // step through games to exclude favorites - if (firstFavorite != -1) - { - while(files[mid]->getFavorite()) - mid++; + for (unsigned int i = 0; i < files.size(); i++) { + if (mFavoritesSorting && mSystem->getName() != "favorites") { + if ((char)toupper(files.at(i)->getSortName()[0]) == + letter && !files.at(i)->getFavorite()) { + getGamelist()->setCursor(files.at(i)); + break; + } + } + else { + if ((char)toupper(files.at(i)->getSortName()[0]) == letter) { + getGamelist()->setCursor(files.at(i)); + break; } - break; } } - gamelist->setCursor(files.at(mid)); - delete this; } -void GuiGamelistOptions::findFirstFavorite() +void GuiGamelistOptions::jumpToFirstRow() { - IGameListView* gamelist = getGamelist(); - - // this is a really shitty way to get a list of files - const std::vector& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay(); - - long loop = 0; - long max = (long)files.size() - 1; - - // Loop through the game list looking for the first game marked as a favorite - while (!files[loop]->getFavorite() and loop < max) - loop++; - - // if the last entry in the game list was not a favorite then there were none for this system - if (!files[loop]->getFavorite()) - { - firstFavorite = -1; - return; - } - - firstFavorite = loop; -} - -void GuiGamelistOptions::jumpToFirstFavorite() -{ - IGameListView* gamelist = getGamelist(); - - // this is a really shitty way to get a list of files - const std::vector& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay(); - - gamelist->setCursor(files.at(firstFavorite)); + // Get first row of the gamelist. + const std::vector& files = getGamelist()->getCursor()-> + getParent()->getChildrenListToDisplay(); + getGamelist()->setCursor(files.at(0)); delete this; } bool GuiGamelistOptions::input(InputConfig* config, Input input) { - if((config->isMappedTo("b", input) || config->isMappedTo("select", input)) && input.value) - { + if ((config->isMappedTo("b", input) || + config->isMappedTo("select", input)) && input.value) { delete this; return true; } diff --git a/es-app/src/guis/GuiGamelistOptions.h b/es-app/src/guis/GuiGamelistOptions.h index 79b95f8ef..8e47c8b63 100644 --- a/es-app/src/guis/GuiGamelistOptions.h +++ b/es-app/src/guis/GuiGamelistOptions.h @@ -1,3 +1,10 @@ +// +// GuiGamelistOptions.h +// +// Gamelist options menu for the 'Jump to...' quick selector, +// game sorting, game filters, and metadata edit. +// + #pragma once #ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H #define ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H @@ -26,12 +33,11 @@ private: void openMetaDataEd(); void startEditMode(); void exitEditMode(); + void jumpToLetter(); - void findFirstFavorite(); - void jumpToFirstFavorite(); + void jumpToFirstRow(); const std::string FAVORITE_CHAR = "\uF005"; - long firstFavorite = -1; MenuComponent mMenu; @@ -43,6 +49,7 @@ private: SystemData* mSystem; IGameListView* getGamelist(); + bool mFavoritesSorting; bool fromPlaceholder; bool mFiltersChanged; }; diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 0528ec287..e9f752c93 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -1,3 +1,9 @@ +// +// GuiMenu.cpp +// +// Main menu. +// + #include "guis/GuiMenu.h" #include "components/OptionListComponent.h" @@ -11,6 +17,7 @@ #include "guis/GuiSettings.h" #include "views/UIModeController.h" #include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" #include "CollectionSystemManager.h" #include "EmulationStation.h" #include "Scripting.h" @@ -20,7 +27,11 @@ #include #include "platform.h" -GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MENU"), mVersion(window) +GuiMenu::GuiMenu( + Window* window) + : GuiComponent(window), + mMenu(window, "MAIN MENU"), + mVersion(window) { bool isFullUI = UIModeController::getInstance()->isUIModeFull(); @@ -29,12 +40,12 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { openSoundSettings(); }); - if (isFullUI) addEntry("UI SETTINGS", 0x777777FF, true, [this] { openUISettings(); }); if (isFullUI) - addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { openCollectionSystemSettings(); }); + addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { + openCollectionSystemSettings(); }); if (isFullUI) addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherSettings(); }); @@ -47,38 +58,44 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN addChild(&mMenu); addVersionInfo(); setSize(mMenu.getSize()); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, Renderer::getScreenHeight() * 0.15f); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, + Renderer::getScreenHeight() * 0.15f); } void GuiMenu::openScraperSettings() { auto s = new GuiSettings(mWindow, "SCRAPER"); - // scrape from - auto scraper_list = std::make_shared< OptionListComponent< std::string > >(mWindow, "SCRAPE FROM", false); + // Scrape from. + auto scraper_list = std::make_shared< OptionListComponent< std::string > + >(mWindow, "SCRAPE FROM", false); std::vector scrapers = getScraperList(); - // Select either the first entry of the one read from the settings, just in case the scraper from settings has vanished. - for(auto it = scrapers.cbegin(); it != scrapers.cend(); it++) + // Select either the first entry of the one read from the settings, + // just in case the scraper from settings has vanished. + for (auto it = scrapers.cbegin(); it != scrapers.cend(); it++) scraper_list->add(*it, *it, *it == Settings::getInstance()->getString("Scraper")); s->addWithLabel("SCRAPE FROM", scraper_list); - s->addSaveFunc([scraper_list] { Settings::getInstance()->setString("Scraper", scraper_list->getSelected()); }); + s->addSaveFunc([scraper_list] { Settings::getInstance()->setString("Scraper", + scraper_list->getSelected()); }); - // scrape ratings + // Scrape ratings. auto scrape_ratings = std::make_shared(mWindow); scrape_ratings->setState(Settings::getInstance()->getBool("ScrapeRatings")); s->addWithLabel("SCRAPE RATINGS", scrape_ratings); - s->addSaveFunc([scrape_ratings] { Settings::getInstance()->setBool("ScrapeRatings", scrape_ratings->getState()); }); + s->addSaveFunc([scrape_ratings] { Settings::getInstance()->setBool("ScrapeRatings", + scrape_ratings->getState()); }); - // scrape now + // Scrape now. ComponentListRow row; auto openScrapeNow = [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }; std::function openAndSave = openScrapeNow; openAndSave = [s, openAndSave] { s->save(); openAndSave(); }; row.makeAcceptInputHandler(openAndSave); - auto scrape_now = std::make_shared(mWindow, "SCRAPE NOW", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto scrape_now = std::make_shared + (mWindow, "SCRAPE NOW", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); auto bracket = makeArrow(mWindow); row.addElement(scrape_now, true); row.addElement(bracket, false); @@ -91,24 +108,25 @@ void GuiMenu::openSoundSettings() { auto s = new GuiSettings(mWindow, "SOUND SETTINGS"); - // volume + // System volume. auto volume = std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); volume->setValue((float)VolumeControl::getInstance()->getVolume()); s->addWithLabel("SYSTEM VOLUME", volume); - s->addSaveFunc([volume] { VolumeControl::getInstance()->setVolume((int)Math::round(volume->getValue())); }); + s->addSaveFunc([volume] { VolumeControl::getInstance()-> + setVolume((int)Math::round(volume->getValue())); }); - if (UIModeController::getInstance()->isUIModeFull()) - { + if (UIModeController::getInstance()->isUIModeFull()) { #if defined(__linux__) // audio card - auto audio_card = std::make_shared< OptionListComponent >(mWindow, "AUDIO CARD", false); + auto audio_card = std::make_shared< OptionListComponent + >(mWindow, "AUDIO CARD", false); std::vector audio_cards; - #ifdef _RPI_ +#ifdef _RPI_ // RPi Specific Audio Cards audio_cards.push_back("local"); audio_cards.push_back("hdmi"); audio_cards.push_back("both"); - #endif +#endif audio_cards.push_back("default"); audio_cards.push_back("sysdefault"); audio_cards.push_back("dmix"); @@ -116,11 +134,12 @@ void GuiMenu::openSoundSettings() audio_cards.push_back("plughw"); audio_cards.push_back("null"); if (Settings::getInstance()->getString("AudioCard") != "") { - if(std::find(audio_cards.begin(), audio_cards.end(), Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) { + if (std::find(audio_cards.begin(), audio_cards.end(), + Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) { audio_cards.push_back(Settings::getInstance()->getString("AudioCard")); } } - for(auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++) + for (auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++) audio_card->add(*ac, *ac, Settings::getInstance()->getString("AudioCard") == *ac); s->addWithLabel("AUDIO CARD", audio_card); s->addSaveFunc([audio_card] { @@ -129,8 +148,9 @@ void GuiMenu::openSoundSettings() VolumeControl::getInstance()->init(); }); - // volume control device - auto vol_dev = std::make_shared< OptionListComponent >(mWindow, "AUDIO DEVICE", false); + // Volume control device. + auto vol_dev = std::make_shared< OptionListComponent + >(mWindow, "AUDIO DEVICE", false); std::vector transitions; transitions.push_back("PCM"); transitions.push_back("Speaker"); @@ -138,11 +158,12 @@ void GuiMenu::openSoundSettings() transitions.push_back("Digital"); transitions.push_back("Analogue"); if (Settings::getInstance()->getString("AudioDevice") != "") { - if(std::find(transitions.begin(), transitions.end(), Settings::getInstance()->getString("AudioDevice")) == transitions.end()) { + if (std::find(transitions.begin(), transitions.end(), + Settings::getInstance()->getString("AudioDevice")) == transitions.end()) { transitions.push_back(Settings::getInstance()->getString("AudioDevice")); } } - for(auto it = transitions.cbegin(); it != transitions.cend(); it++) + for (auto it = transitions.cbegin(); it != transitions.cend(); it++) vol_dev->add(*it, *it, Settings::getInstance()->getString("AudioDevice") == *it); s->addWithLabel("AUDIO DEVICE", vol_dev); s->addSaveFunc([vol_dev] { @@ -152,7 +173,7 @@ void GuiMenu::openSoundSettings() }); #endif - // disable sounds + // Disable sounds. auto sounds_enabled = std::make_shared(mWindow); sounds_enabled->setState(Settings::getInstance()->getBool("EnableSounds")); s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled); @@ -170,11 +191,13 @@ void GuiMenu::openSoundSettings() auto video_audio = std::make_shared(mWindow); video_audio->setState(Settings::getInstance()->getBool("VideoAudio")); s->addWithLabel("ENABLE VIDEO AUDIO", video_audio); - s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); }); + s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", + video_audio->getState()); }); #ifdef _RPI_ // OMX player Audio Device - auto omx_audio_dev = std::make_shared< OptionListComponent >(mWindow, "OMX PLAYER AUDIO DEVICE", false); + auto omx_audio_dev = std::make_shared< OptionListComponent + >(mWindow, "OMX PLAYER AUDIO DEVICE", false); std::vector omx_cards; // RPi Specific Audio Cards omx_cards.push_back("local"); @@ -183,7 +206,8 @@ void GuiMenu::openSoundSettings() omx_cards.push_back("alsa:hw:0,0"); omx_cards.push_back("alsa:hw:1,0"); if (Settings::getInstance()->getString("OMXAudioDev") != "") { - if (std::find(omx_cards.begin(), omx_cards.end(), Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) { + if (std::find(omx_cards.begin(), omx_cards.end(), + Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) { omx_cards.push_back(Settings::getInstance()->getString("OMXAudioDev")); } } @@ -198,26 +222,25 @@ void GuiMenu::openSoundSettings() } mWindow->pushGui(s); - } void GuiMenu::openUISettings() { auto s = new GuiSettings(mWindow, "UI SETTINGS"); - //UI mode - auto UImodeSelection = std::make_shared< OptionListComponent >(mWindow, "UI MODE", false); + // UI mode. + auto UImodeSelection = std::make_shared< OptionListComponent + >(mWindow, "UI MODE", false); std::vector UImodes = UIModeController::getInstance()->getUIModes(); for (auto it = UImodes.cbegin(); it != UImodes.cend(); it++) UImodeSelection->add(*it, *it, Settings::getInstance()->getString("UIMode") == *it); s->addWithLabel("UI MODE", UImodeSelection); Window* window = mWindow; - s->addSaveFunc([ UImodeSelection, window] - { + s->addSaveFunc([ UImodeSelection, window] { std::string selectedMode = UImodeSelection->getSelected(); - if (selectedMode != "Full") - { - std::string msg = "You are changing the UI to a restricted mode:\n" + selectedMode + "\n"; + if (selectedMode != "Full") { + std::string msg = "You are changing the UI to a restricted mode:\n" + + selectedMode + "\n"; msg += "This will hide most menu-options to prevent changes to the system.\n"; msg += "To unlock and return to the full UI, enter this code: \n"; msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\n"; @@ -231,109 +254,114 @@ void GuiMenu::openUISettings() } }); - // fullscreen mode - auto fullscreen_mode = std::make_shared< OptionListComponent >(mWindow, "FULLSCREEN MODE", false); + // Fullscreen mode. + auto fullscreen_mode = std::make_shared< OptionListComponent + >(mWindow, "FULLSCREEN MODE", false); std::vector screenmode; screenmode.push_back("normal"); screenmode.push_back("borderless"); - for(auto it = screenmode.cbegin(); it != screenmode.cend(); it++) + for (auto it = screenmode.cbegin(); it != screenmode.cend(); it++) fullscreen_mode->add(*it, *it, Settings::getInstance()->getString("FullscreenMode") == *it); s->addWithLabel("FULLSCREEN MODE (REQUIRES RESTART)", fullscreen_mode); s->addSaveFunc([fullscreen_mode] { if (Settings::getInstance()->getString("FullscreenMode") == "normal" - && fullscreen_mode->getSelected() != "normal") - { + && fullscreen_mode->getSelected() != "normal") { Settings::getInstance()->setString("PowerSaverMode", "default"); PowerSaver::init(); } Settings::getInstance()->setString("FullscreenMode", fullscreen_mode->getSelected()); }); - // screensaver + // Screensaver. ComponentListRow screensaver_row; screensaver_row.elements.clear(); - screensaver_row.addElement(std::make_shared(mWindow, "SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + screensaver_row.addElement(std::make_shared + (mWindow, "SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); screensaver_row.addElement(makeArrow(mWindow), false); screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this)); s->addRow(screensaver_row); - // quick system select (left/right in game list view) + // Quick system select (left/right in game list view). auto quick_sys_select = std::make_shared(mWindow); quick_sys_select->setState(Settings::getInstance()->getBool("QuickSystemSelect")); s->addWithLabel("QUICK SYSTEM SELECT", quick_sys_select); - s->addSaveFunc([quick_sys_select] { Settings::getInstance()->setBool("QuickSystemSelect", quick_sys_select->getState()); }); + s->addSaveFunc([quick_sys_select] { Settings::getInstance()->setBool("QuickSystemSelect", + quick_sys_select->getState()); }); - // carousel transition option + // Carousel transition option. auto move_carousel = std::make_shared(mWindow); move_carousel->setState(Settings::getInstance()->getBool("MoveCarousel")); s->addWithLabel("CAROUSEL TRANSITIONS", move_carousel); s->addSaveFunc([move_carousel] { if (move_carousel->getState() && !Settings::getInstance()->getBool("MoveCarousel") - && PowerSaver::getMode() == PowerSaver::INSTANT) - { + && PowerSaver::getMode() == PowerSaver::INSTANT) { Settings::getInstance()->setString("PowerSaverMode", "default"); PowerSaver::init(); } Settings::getInstance()->setBool("MoveCarousel", move_carousel->getState()); }); - // transition style - auto transition_style = std::make_shared< OptionListComponent >(mWindow, "TRANSITION STYLE", false); + // Transition style. + auto transition_style = std::make_shared< OptionListComponent + >(mWindow, "TRANSITION STYLE", false); std::vector transitions; transitions.push_back("fade"); transitions.push_back("slide"); transitions.push_back("instant"); - for(auto it = transitions.cbegin(); it != transitions.cend(); it++) - transition_style->add(*it, *it, Settings::getInstance()->getString("TransitionStyle") == *it); + for (auto it = transitions.cbegin(); it != transitions.cend(); it++) + transition_style->add(*it, *it, Settings::getInstance()-> + getString("TransitionStyle") == *it); s->addWithLabel("TRANSITION STYLE", transition_style); s->addSaveFunc([transition_style] { if (Settings::getInstance()->getString("TransitionStyle") == "instant" && transition_style->getSelected() != "instant" - && PowerSaver::getMode() == PowerSaver::INSTANT) - { + && PowerSaver::getMode() == PowerSaver::INSTANT) { Settings::getInstance()->setString("PowerSaverMode", "default"); PowerSaver::init(); } Settings::getInstance()->setString("TransitionStyle", transition_style->getSelected()); }); - // theme set + // Theme selection. auto themeSets = ThemeData::getThemeSets(); - if(!themeSets.empty()) + if (!themeSets.empty()) { - std::map::const_iterator selectedSet = themeSets.find(Settings::getInstance()->getString("ThemeSet")); - if(selectedSet == themeSets.cend()) + std::map::const_iterator selectedSet = + themeSets.find(Settings::getInstance()->getString("ThemeSet")); + if (selectedSet == themeSets.cend()) selectedSet = themeSets.cbegin(); - auto theme_set = std::make_shared< OptionListComponent >(mWindow, "THEME SET", false); - for(auto it = themeSets.cbegin(); it != themeSets.cend(); it++) + auto theme_set = std::make_shared< OptionListComponent + >(mWindow, "THEME SET", false); + for (auto it = themeSets.cbegin(); it != themeSets.cend(); it++) theme_set->add(it->first, it->first, it == selectedSet); s->addWithLabel("THEME SET", theme_set); Window* window = mWindow; - s->addSaveFunc([window, theme_set] - { + s->addSaveFunc([window, theme_set] { bool needReload = false; std::string oldTheme = Settings::getInstance()->getString("ThemeSet"); - if(oldTheme != theme_set->getSelected()) + if (oldTheme != theme_set->getSelected()) needReload = true; Settings::getInstance()->setString("ThemeSet", theme_set->getSelected()); - if(needReload) + if (needReload) { Scripting::fireEvent("theme-changed", theme_set->getSelected(), oldTheme); CollectionSystemManager::get()->updateSystemsList(); ViewController::get()->goToStart(); - ViewController::get()->reloadAll(); // TODO - replace this with some sort of signal-based implementation + // TODO - replace this with some sort of signal-based implementation. + ViewController::get()->reloadAll(); } }); } - // GameList view style - auto gamelist_style = std::make_shared< OptionListComponent >(mWindow, "GAMELIST VIEW STYLE", false); + // GameList view style. + auto gamelist_style = std::make_shared< OptionListComponent + >(mWindow, "GAMELIST VIEW STYLE", false); std::vector styles; styles.push_back("automatic"); styles.push_back("basic"); @@ -342,92 +370,121 @@ void GuiMenu::openUISettings() styles.push_back("grid"); for (auto it = styles.cbegin(); it != styles.cend(); it++) - gamelist_style->add(*it, *it, Settings::getInstance()->getString("GamelistViewStyle") == *it); + gamelist_style->add(*it, *it, Settings::getInstance()-> + getString("GamelistViewStyle") == *it); s->addWithLabel("GAMELIST VIEW STYLE", gamelist_style); s->addSaveFunc([gamelist_style] { bool needReload = false; - if (Settings::getInstance()->getString("GamelistViewStyle") != gamelist_style->getSelected()) + if (Settings::getInstance()->getString("GamelistViewStyle") != + gamelist_style->getSelected()) needReload = true; Settings::getInstance()->setString("GamelistViewStyle", gamelist_style->getSelected()); if (needReload) ViewController::get()->reloadAll(); }); - // Optionally start in selected system - auto systemfocus_list = std::make_shared< OptionListComponent >(mWindow, "START ON SYSTEM", false); + // Optionally start in selected system. + auto systemfocus_list = std::make_shared< OptionListComponent + >(mWindow, "START ON SYSTEM", false); systemfocus_list->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == ""); - for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) - { + for (auto it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); it++) { if ("retropie" != (*it)->getName()) - { - systemfocus_list->add((*it)->getName(), (*it)->getName(), Settings::getInstance()->getString("StartupSystem") == (*it)->getName()); - } + systemfocus_list->add((*it)->getName(), (*it)->getName(), + Settings::getInstance()->getString("StartupSystem") == (*it)->getName()); } s->addWithLabel("START ON SYSTEM", systemfocus_list); s->addSaveFunc([systemfocus_list] { Settings::getInstance()->setString("StartupSystem", systemfocus_list->getSelected()); }); - // show help + // Show help. auto show_help = std::make_shared(mWindow); show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts")); s->addWithLabel("ON-SCREEN HELP", show_help); - s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts", show_help->getState()); }); + s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts", + show_help->getState()); }); - // enable filters (ForceDisableFilters) + // Enable filters (ForceDisableFilters). auto enable_filter = std::make_shared(mWindow); enable_filter->setState(!Settings::getInstance()->getBool("ForceDisableFilters")); - s->addWithLabel("ENABLE FILTERS", enable_filter); + s->addWithLabel("ENABLE GAMELIST FILTERS", enable_filter); s->addSaveFunc([enable_filter] { bool filter_is_enabled = !Settings::getInstance()->getBool("ForceDisableFilters"); Settings::getInstance()->setBool("ForceDisableFilters", !enable_filter->getState()); - if (enable_filter->getState() != filter_is_enabled) ViewController::get()->ReloadAndGoToStart(); + if (enable_filter->getState() != filter_is_enabled) + ViewController::get()->ReloadAndGoToStart(); }); - // hide start menu in Kid Mode + // Hide start menu in Kid Mode. auto disable_start = std::make_shared(mWindow); disable_start->setState(Settings::getInstance()->getBool("DisableKidStartMenu")); s->addWithLabel("DISABLE START MENU IN KID MODE", disable_start); - s->addSaveFunc([disable_start] { Settings::getInstance()->setBool("DisableKidStartMenu", disable_start->getState()); }); + s->addSaveFunc([disable_start] { Settings::getInstance()->setBool("DisableKidStartMenu", + disable_start->getState()); }); - // hide Reboot System option in the Quit menu + // Hide Reboot System option in the quit menu. auto show_rebootsystem = std::make_shared(mWindow); show_rebootsystem->setState(Settings::getInstance()->getBool("ShowRebootSystem")); s->addWithLabel("SHOW \"REBOOT SYSTEM\" MENU ENTRY", show_rebootsystem); - s->addSaveFunc([show_rebootsystem] { Settings::getInstance()->setBool("ShowRebootSystem", show_rebootsystem->getState()); }); + s->addSaveFunc([show_rebootsystem] { Settings::getInstance()->setBool("ShowRebootSystem", + show_rebootsystem->getState()); }); - // hide Power Off System option in the Quit menu + // Hide Power Off System option in the quit menu. auto show_poweroffsystem = std::make_shared(mWindow); show_poweroffsystem->setState(Settings::getInstance()->getBool("ShowPoweroffSystem")); s->addWithLabel("SHOW \"POWER OFF SYSTEM\" MENU ENTRY", show_poweroffsystem); - s->addSaveFunc([show_poweroffsystem] { Settings::getInstance()->setBool("ShowPoweroffSystem", show_poweroffsystem->getState()); }); + s->addSaveFunc([show_poweroffsystem] { Settings::getInstance()->setBool("ShowPoweroffSystem", + show_poweroffsystem->getState()); }); - // Show favorites first in gamelists + // Sort favorites on top of the gamelists. auto favoritesFirstSwitch = std::make_shared(mWindow); favoritesFirstSwitch->setState(Settings::getInstance()->getBool("FavoritesFirst")); - s->addWithLabel("SHOW FAVORITES ON TOP OF GAMELIST", favoritesFirstSwitch); - s->addSaveFunc([favoritesFirstSwitch] - { + s->addWithLabel("SORT FAVORITES ON TOP OF GAMELISTS", favoritesFirstSwitch); + s->addSaveFunc([favoritesFirstSwitch] { if (Settings::getInstance()->setBool("FavoritesFirst", favoritesFirstSwitch->getState())) - ViewController::get()->reloadAll(); + for (auto it = SystemData::sSystemVector.cbegin(); it != + SystemData::sSystemVector.cend(); it++) { + + // The favorites and recent gamelists never sort favorites on top. + if ((*it)->getName() == "favorites" || (*it)->getName() == "recent") + continue; + + // Don't re-sort custom collections as they have their own option + // for whether to sort favorites on top or not (FavFirstCustom). + if (CollectionSystemManager::get()->getIsCustomCollection((*it))) + continue; + + FileData* rootFolder = (*it)->getRootFolder(); + + rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavoritesFirst")); + ViewController::get()->reloadGameListView(*it); + + // Jump to the first row of the game list. + IGameListView* gameList = ViewController::get()->getGameListView((*it)).get(); + FileData* firstRow = gameList->getCursor()->getParent()->getChildrenListToDisplay()[0]; + gameList->setCursor(firstRow); + } }); mWindow->pushGui(s); - } void GuiMenu::openOtherSettings() { auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); - // maximum vram + // Maximum VRAM. auto max_vram = std::make_shared(mWindow, 0.f, 1000.f, 10.f, "Mb"); max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM"))); s->addWithLabel("VRAM LIMIT", max_vram); - s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM", (int)Math::round(max_vram->getValue())); }); + s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM", + (int)Math::round(max_vram->getValue())); }); - // power saver - auto power_saver = std::make_shared< OptionListComponent >(mWindow, "POWER SAVER MODES", false); + // Power saver. + auto power_saver = std::make_shared< OptionListComponent + >(mWindow, "POWER SAVER MODES", false); std::vector modes; modes.push_back("disabled"); modes.push_back("default"); @@ -437,7 +494,8 @@ void GuiMenu::openOtherSettings() power_saver->add(*it, *it, Settings::getInstance()->getString("PowerSaverMode") == *it); s->addWithLabel("POWER SAVER MODES", power_saver); s->addSaveFunc([this, power_saver] { - if (Settings::getInstance()->getString("PowerSaverMode") != "instant" && power_saver->getSelected() == "instant") { + if (Settings::getInstance()->getString("PowerSaverMode") != + "instant" && power_saver->getSelected() == "instant") { Settings::getInstance()->setString("TransitionStyle", "instant"); Settings::getInstance()->setBool("MoveCarousel", false); Settings::getInstance()->setBool("EnableSounds", false); @@ -446,15 +504,17 @@ void GuiMenu::openOtherSettings() PowerSaver::init(); }); - // gamelists - auto gamelistsSaveMode = std::make_shared< OptionListComponent >(mWindow, "SAVE METADATA", false); + // Gamelists. + auto gamelistsSaveMode = std::make_shared< OptionListComponent + >(mWindow, "SAVE METADATA", false); std::vector saveModes; saveModes.push_back("on exit"); saveModes.push_back("always"); saveModes.push_back("never"); - for(auto it = saveModes.cbegin(); it != saveModes.cend(); it++) - gamelistsSaveMode->add(*it, *it, Settings::getInstance()->getString("SaveGamelistsMode") == *it); + for (auto it = saveModes.cbegin(); it != saveModes.cend(); it++) + gamelistsSaveMode->add(*it, *it, Settings::getInstance()-> + getString("SaveGamelistsMode") == *it); s->addWithLabel("SAVE METADATA", gamelistsSaveMode); s->addSaveFunc([gamelistsSaveMode] { Settings::getInstance()->setString("SaveGamelistsMode", gamelistsSaveMode->getSelected()); @@ -463,51 +523,56 @@ void GuiMenu::openOtherSettings() auto parse_gamelists = std::make_shared(mWindow); parse_gamelists->setState(Settings::getInstance()->getBool("ParseGamelistOnly")); s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists); - s->addSaveFunc([parse_gamelists] { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); }); + s->addSaveFunc([parse_gamelists] { Settings::getInstance()-> + setBool("ParseGamelistOnly", parse_gamelists->getState()); }); auto local_art = std::make_shared(mWindow); local_art->setState(Settings::getInstance()->getBool("LocalArt")); s->addWithLabel("SEARCH FOR LOCAL ART", local_art); - s->addSaveFunc([local_art] { Settings::getInstance()->setBool("LocalArt", local_art->getState()); }); + s->addSaveFunc([local_art] { Settings::getInstance()-> + setBool("LocalArt", local_art->getState()); }); - // Allow overriding of the launch string per game (the option to disable this is intended primarily for testing purposes) + // Allow overriding of the launch string per game (the option + // to disable this is intended primarily for testing purposes). auto launchstring_override = std::make_shared(mWindow); launchstring_override->setState(Settings::getInstance()->getBool("LaunchstringOverride")); - s->addWithLabel("PER GAME OVERRIDE OF LAUNCH STRING", launchstring_override); - s->addSaveFunc([launchstring_override] { Settings::getInstance()->setBool("LaunchstringOverride", launchstring_override->getState()); }); + s->addWithLabel("PER GAME LAUNCH STRING OVERRIDE", launchstring_override); + s->addSaveFunc([launchstring_override] { Settings::getInstance()-> + setBool("LaunchstringOverride", launchstring_override->getState()); }); - // hidden files + // Hidden files. auto hidden_files = std::make_shared(mWindow); hidden_files->setState(Settings::getInstance()->getBool("ShowHiddenFiles")); s->addWithLabel("SHOW HIDDEN FILES", hidden_files); - s->addSaveFunc([hidden_files] { Settings::getInstance()->setBool("ShowHiddenFiles", hidden_files->getState()); }); + s->addSaveFunc([hidden_files] { Settings::getInstance()->setBool("ShowHiddenFiles", + hidden_files->getState()); }); #ifdef _RPI_ - // Video Player - VideoOmxPlayer + // Video Player - VideoOmxPlayer. auto omx_player = std::make_shared(mWindow); omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", omx_player); s->addSaveFunc([omx_player] { - // need to reload all views to re-create the right video components + // Need to reload all views to re-create the right video components. bool needReload = false; - if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) + if (Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) needReload = true; Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState()); - if(needReload) + if (needReload) ViewController::get()->reloadAll(); }); #endif - // framerate + // Framerate. auto framerate = std::make_shared(mWindow); framerate->setState(Settings::getInstance()->getBool("DrawFramerate")); s->addWithLabel("SHOW FRAMERATE", framerate); - s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate", framerate->getState()); }); - + s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate", + framerate->getState()); }); mWindow->pushGui(s); @@ -521,7 +586,6 @@ void GuiMenu::openConfigInput() window->pushGui(new GuiDetectDevice(window, false, nullptr)); }, "NO", nullptr) ); - } void GuiMenu::openQuitMenu() @@ -531,10 +595,8 @@ void GuiMenu::openQuitMenu() Window* window = mWindow; ComponentListRow row; - if (UIModeController::getInstance()->isUIModeFull()) - { - if(Settings::getInstance()->getBool("ShowExit")) - { + if (UIModeController::getInstance()->isUIModeFull()) { + if (Settings::getInstance()->getBool("ShowExit")) { row.makeAcceptInputHandler([window] { window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES", [] { @@ -542,13 +604,13 @@ void GuiMenu::openQuitMenu() quitES(); }, "NO", nullptr)); }); - row.addElement(std::make_shared(window, "QUIT EMULATIONSTATION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, "QUIT EMULATIONSTATION", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); s->addRow(row); } } - if(Settings::getInstance()->getBool("ShowRebootSystem")) - { + if (Settings::getInstance()->getBool("ShowRebootSystem")) { row.elements.clear(); row.makeAcceptInputHandler([window] { window->pushGui(new GuiMsgBox(window, "REALLY REBOOT?", "YES", @@ -559,12 +621,12 @@ void GuiMenu::openQuitMenu() LOG(LogWarning) << "Reboot terminated with non-zero result!"; }, "NO", nullptr)); }); - row.addElement(std::make_shared(window, "REBOOT SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, "REBOOT SYSTEM", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); s->addRow(row); } - if(Settings::getInstance()->getBool("ShowPoweroffSystem")) - { + if (Settings::getInstance()->getBool("ShowPoweroffSystem")) { row.elements.clear(); row.makeAcceptInputHandler([window] { window->pushGui(new GuiMsgBox(window, "REALLY POWER OFF?", "YES", @@ -575,7 +637,8 @@ void GuiMenu::openQuitMenu() LOG(LogWarning) << "Power off terminated with non-zero result!"; }, "NO", nullptr)); }); - row.addElement(std::make_shared(window, "POWER OFF SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, "POWER OFF SYSTEM", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); s->addRow(row); } @@ -584,8 +647,6 @@ void GuiMenu::openQuitMenu() void GuiMenu::addVersionInfo() { -// std::string buildDate = (Settings::getInstance()->getBool("Debug") ? std::string( " (" + Utils::String::toUpper(PROGRAM_BUILT_STRING) + ")") : ("")); - mVersion.setFont(Font::get(FONT_SIZE_SMALL)); mVersion.setColor(0x5E5E5EFF); mVersion.setText("EMULATIONSTATION-DE V" + Utils::String::toUpper(PROGRAM_VERSION_STRING)); @@ -607,16 +668,19 @@ void GuiMenu::onSizeChanged() mVersion.setPosition(0, mSize.y() - mVersion.getSize().y()); } -void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func) +void GuiMenu::addEntry( + const char* name, + unsigned int color, + bool add_arrow, + const std::function& func) { std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); - // populate the list + // Populate the list. ComponentListRow row; row.addElement(std::make_shared(mWindow, name, font, color), true); - if(add_arrow) - { + if (add_arrow) { std::shared_ptr bracket = makeArrow(mWindow); row.addElement(bracket, false); } @@ -628,11 +692,11 @@ void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, con bool GuiMenu::input(InputConfig* config, Input input) { - if(GuiComponent::input(config, input)) + if (GuiComponent::input(config, input)) return true; - if((config->isMappedTo("b", input) || config->isMappedTo("start", input)) && input.value != 0) - { + if ((config->isMappedTo("b", input) || config->isMappedTo("start", input)) && + input.value != 0) { delete this; return true; } diff --git a/es-app/src/guis/GuiMenu.h b/es-app/src/guis/GuiMenu.h index da8406bb7..8d2dc3a88 100644 --- a/es-app/src/guis/GuiMenu.h +++ b/es-app/src/guis/GuiMenu.h @@ -1,3 +1,9 @@ +// +// GuiMenu.h +// +// Main menu. +// + #pragma once #ifndef ES_APP_GUIS_GUI_MENU_H #define ES_APP_GUIS_GUI_MENU_H @@ -16,7 +22,8 @@ public: HelpStyle getHelpStyle() override; private: - void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func); + void addEntry(const char* name, unsigned int color, + bool add_arrow, const std::function& func); void addVersionInfo(); void openCollectionSystemSettings(); void openConfigInput(); diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 6d45c9a5a..17b77abe7 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -1,3 +1,12 @@ +// +// ViewController.cpp +// +// Handles overall system navigation including animations and transitions. +// Also creates the gamelist views and handles refresh and reloads of these when needed +// (for example when metadata has been changed or when a list sorting has taken place). +// Initiates the launching of games, calling FileData to do the actual launch. +// + #include "views/ViewController.h" #include "animations/Animation.h" @@ -18,7 +27,7 @@ #include "Window.h" #include "Sound.h" -ViewController* ViewController::sInstance = NULL; +ViewController* ViewController::sInstance = nullptr; NavigationSounds navigationsounds; ViewController* ViewController::get() @@ -33,8 +42,13 @@ void ViewController::init(Window* window) sInstance = new ViewController(window); } -ViewController::ViewController(Window* window) - : GuiComponent(window), mCurrentView(nullptr), mCamera(Transform4x4f::Identity()), mFadeOpacity(0), mLockInput(false) +ViewController::ViewController( + Window* window) + : GuiComponent(window), + mCurrentView(nullptr), + mCamera(Transform4x4f::Identity()), + mFadeOpacity(0), + mLockInput(false) { mState.viewing = NOTHING; } @@ -42,24 +56,23 @@ ViewController::ViewController(Window* window) ViewController::~ViewController() { assert(sInstance == this); - sInstance = NULL; + sInstance = nullptr; } void ViewController::goToStart() { - // If specific system is requested, go directly to the game list + // If a specific system is requested, go directly to its game list. auto requestedSystem = Settings::getInstance()->getString("StartupSystem"); - if("" != requestedSystem && "retropie" != requestedSystem) - { - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++){ - if ((*it)->getName() == requestedSystem) - { + if ("" != requestedSystem && "retropie" != requestedSystem) { + for (auto it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); it++) { + if ((*it)->getName() == requestedSystem) { goToGameList(*it); return; } } - // Requested system doesn't exist + // Requested system doesn't exist. Settings::getInstance()->setString("StartupSystem", ""); } goToSystemView(SystemData::sSystemVector.at(0)); @@ -80,17 +93,16 @@ int ViewController::getSystemId(SystemData* system) void ViewController::goToSystemView(SystemData* system) { - // Tell any current view it's about to be hidden + // Tell any current view it's about to be hidden. if (mCurrentView) - { mCurrentView->onHide(); - } mState.viewing = SYSTEM_SELECT; mState.system = system; auto systemList = getSystemListView(); - systemList->setPosition(getSystemId(system) * (float)Renderer::getScreenWidth(), systemList->getPosition().y()); + systemList->setPosition(getSystemId(system) * (float)Renderer::getScreenWidth(), + systemList->getPosition().y()); systemList->goToSystem(system, false); mCurrentView = systemList; @@ -120,13 +132,14 @@ void ViewController::goToPrevGameList() void ViewController::goToGameList(SystemData* system) { - if(mState.viewing == SYSTEM_SELECT) - { - // move system list + if (mState.viewing == SYSTEM_SELECT) { + // Move system list. auto sysList = getSystemListView(); float offX = sysList->getPosition().x(); int sysId = getSystemId(system); - sysList->setPosition(sysId * (float)Renderer::getScreenWidth(), sysList->getPosition().y()); + + sysList->setPosition(sysId * (float)Renderer::getScreenWidth(), + sysList->getPosition().y()); offX = sysList->getPosition().x() - offX; mCamera.translation().x() -= offX; } @@ -135,67 +148,65 @@ void ViewController::goToGameList(SystemData* system) mState.system = system; if (mCurrentView) - { mCurrentView->onHide(); - } + mCurrentView = getGameListView(system); + if (mCurrentView) - { mCurrentView->onShow(); - } playViewTransition(); } void ViewController::playViewTransition() { Vector3f target(Vector3f::Zero()); - if(mCurrentView) + if (mCurrentView) target = mCurrentView->getPosition(); - // no need to animate, we're not going anywhere (probably goToNextGamelist() or goToPrevGamelist() when there's only 1 system) - if(target == -mCamera.translation() && !isAnimationPlaying(0)) + // No need to animate, we're not going anywhere (probably due to goToNextGamelist() + // or goToPrevGamelist() being called when there's only 1 system). + if (target == -mCamera.translation() && !isAnimationPlaying(0)) return; std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); - if(transition_style == "fade") - { - // fade - // stop whatever's currently playing, leaving mFadeOpacity wherever it is + + if (transition_style == "fade") { + // Fade. + // Stop whatever's currently playing, leaving mFadeOpacity wherever it is. cancelAnimation(0); auto fadeFunc = [this](float t) { mFadeOpacity = Math::lerp(0, 1, t); }; - const static int FADE_DURATION = 240; // fade in/out time - const static int FADE_WAIT = 320; // time to wait between in/out + const static int FADE_DURATION = 240; // Fade in/out time. + const static int FADE_WAIT = 320; // Time to wait between in/out. setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0, [this, fadeFunc, target] { this->mCamera.translation() = -target; updateHelpPrompts(); setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT, nullptr, true); }); - // fast-forward animation if we're partway faded - if(target == -mCamera.translation()) - { - // not changing screens, so cancel the first half entirely + // Fast-forward animation if we're partway faded. + if (target == -mCamera.translation()) { + // Not changing screens, so cancel the first half entirely. advanceAnimation(0, FADE_DURATION); advanceAnimation(0, FADE_WAIT); advanceAnimation(0, FADE_DURATION - (int)(mFadeOpacity * FADE_DURATION)); - }else{ + } + else { advanceAnimation(0, (int)(mFadeOpacity * FADE_DURATION)); } - } else if (transition_style == "slide"){ - // slide or simple slide + } + else if (transition_style == "slide") { + // Slide or simple slide. setAnimation(new MoveCameraAnimation(mCamera, target)); - updateHelpPrompts(); // update help prompts immediately - } else { - // instant - setAnimation(new LambdaAnimation( - [this, target](float /*t*/) - { - this->mCamera.translation() = -target; - }, 1)); + updateHelpPrompts(); // Update help prompts immediately. + } + else { + // Instant. + setAnimation(new LambdaAnimation([this, target](float /*t*/) { + this->mCamera.translation() = -target; }, 1)); updateHelpPrompts(); } } @@ -203,19 +214,18 @@ void ViewController::playViewTransition() void ViewController::onFileChanged(FileData* file, FileChangeType change) { auto it = mGameListViews.find(file->getSystem()); - if(it != mGameListViews.cend()) + if (it != mGameListViews.cend()) it->second->onFileChanged(file, change); } void ViewController::launch(FileData* game, Vector3f center) { - if(game->getType() != GAME) - { + if (game->getType() != GAME) { LOG(LogError) << "tried to launch something that isn't a game"; return; } - // Hide the current view + // Hide the current view. if (mCurrentView) mCurrentView->onHide(); @@ -223,47 +233,51 @@ void ViewController::launch(FileData* game, Vector3f center) origCamera.translation() = -mCurrentView->getPosition(); center += mCurrentView->getPosition(); - stopAnimation(1); // make sure the fade in isn't still playing - mWindow->stopInfoPopup(); // make sure we disable any existing info popup + stopAnimation(1); // Make sure the fade in isn't still playing. + mWindow->stopInfoPopup(); // Make sure we disable any existing info popup. mLockInput = true; std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); navigationsounds.playThemeNavigationSound(LAUNCHSOUND); - // let launch sound play to the end before launching game + // Let launch sound play to the end before launching game. while(navigationsounds.isPlayingThemeNavigationSound(LAUNCHSOUND)); - if(transition_style == "fade") - { - // fade out, launch game, fade back in + if (transition_style == "fade") { + // Fade out, launch game, fade back in. auto fadeFunc = [this](float t) { mFadeOpacity = Math::lerp(0.0f, 1.0f, t); }; - setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this, game, fadeFunc] - { + setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this, game, fadeFunc] { game->launchGame(mWindow); - setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this] { mLockInput = false; }, true); + setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this] { + mLockInput = false; }, true); this->onFileChanged(game, FILE_METADATA_CHANGED); if (mCurrentView) mCurrentView->onShow(); }); - } else if (transition_style == "slide"){ - // move camera to zoom in on center + fade out, launch game, come back in - setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 1500), 0, [this, origCamera, center, game] - { + } + else if (transition_style == "slide") { + // Move camera to zoom in on center + fade out, launch game, come back in. + setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 1500), 0, + [this, origCamera, center, game] { game->launchGame(mWindow); mCamera = origCamera; - setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this] { mLockInput = false; }, true); + setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this] { + mLockInput = false; }, true); this->onFileChanged(game, FILE_METADATA_CHANGED); if (mCurrentView) mCurrentView->onShow(); }); - } else { // instant - setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this, origCamera, center, game] - { + } + // Instant + else { + setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, + [this, origCamera, center, game] { game->launchGame(mWindow); mCamera = origCamera; - setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this] { mLockInput = false; }, true); + setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, + [this] { mLockInput = false; }, true); this->onFileChanged(game, FILE_METADATA_CHANGED); if (mCurrentView) mCurrentView->onShow(); @@ -273,10 +287,8 @@ void ViewController::launch(FileData* game, Vector3f center) void ViewController::removeGameListView(SystemData* system) { - //if we already made one, return that one auto exists = mGameListViews.find(system); - if(exists != mGameListViews.cend()) - { + if (exists != mGameListViews.cend()) { exists->second.reset(); mGameListViews.erase(system); } @@ -284,18 +296,18 @@ void ViewController::removeGameListView(SystemData* system) std::shared_ptr ViewController::getGameListView(SystemData* system) { - //if we already made one, return that one + // If we already made one, return that one. auto exists = mGameListViews.find(system); - if(exists != mGameListViews.cend()) + if (exists != mGameListViews.cend()) return exists->second; system->getIndex()->setUIModeFilters(); - //if we didn't, make it, remember it, and return it + // If we didn't, make it, remember it, and return it. std::shared_ptr view; bool themeHasVideoView = system->getTheme()->hasView("video"); - //decide type + // Decide type. GameListViewType selectedViewType = AUTOMATIC; std::string viewPreference = Settings::getInstance()->getString("GamelistViewStyle"); @@ -308,39 +320,39 @@ std::shared_ptr ViewController::getGameListView(SystemData* syste if (viewPreference.compare("video") == 0) selectedViewType = VIDEO; - if (selectedViewType == AUTOMATIC) - { + if (selectedViewType == AUTOMATIC) { std::vector files = system->getRootFolder()->getFilesRecursive(GAME | FOLDER); - for (auto it = files.cbegin(); it != files.cend(); it++) - { - if (themeHasVideoView && !(*it)->getVideoPath().empty()) - { + for (auto it = files.cbegin(); it != files.cend(); it++) { + if (themeHasVideoView && !(*it)->getVideoPath().empty()) { selectedViewType = VIDEO; break; } - else if (!(*it)->getThumbnailPath().empty()) - { + else if (!(*it)->getThumbnailPath().empty()) { selectedViewType = DETAILED; - // Don't break out in case any subsequent files have video + // Don't break out in case any subsequent files have videos. } } } - // Create the view + // Create the view. switch (selectedViewType) { case VIDEO: - view = std::shared_ptr(new VideoGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr( + new VideoGameListView(mWindow, system->getRootFolder())); break; case DETAILED: - view = std::shared_ptr(new DetailedGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr( + new DetailedGameListView(mWindow, system->getRootFolder())); break; case GRID: - view = std::shared_ptr(new GridGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr( + new GridGameListView(mWindow, system->getRootFolder())); break; case BASIC: default: - view = std::shared_ptr(new BasicGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr( + new BasicGameListView(mWindow, system->getRootFolder())); break; } @@ -348,7 +360,8 @@ std::shared_ptr ViewController::getGameListView(SystemData* syste std::vector& sysVec = SystemData::sSystemVector; int id = (int)(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin()); - view->setPosition(id * (float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight() * 2); + view->setPosition(id * (float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight() * 2); addChild(view.get()); @@ -358,8 +371,8 @@ std::shared_ptr ViewController::getGameListView(SystemData* syste std::shared_ptr ViewController::getSystemListView() { - //if we already made one, return that one - if(mSystemListView) + // If we already made one, return that one. + if (mSystemListView) return mSystemListView; mSystemListView = std::shared_ptr(new SystemView(mWindow)); @@ -371,23 +384,22 @@ std::shared_ptr ViewController::getSystemListView() bool ViewController::input(InputConfig* config, Input input) { - if(mLockInput) + if (mLockInput) return true; - // open menu - if(!(UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("DisableKidStartMenu")) && config->isMappedTo("start", input) && input.value != 0) - { - // open menu + // Open menu. + if (!(UIModeController::getInstance()->isUIModeKid() && + Settings::getInstance()->getBool("DisableKidStartMenu")) && + config->isMappedTo("start", input) && input.value != 0) { mWindow->pushGui(new GuiMenu(mWindow)); return true; } - if(UIModeController::getInstance()->listen(config, input)) // check if UI mode has changed due to passphrase completion - { + // Check if UI mode has changed due to passphrase completion. + if (UIModeController::getInstance()->listen(config, input)) return true; - } - if(mCurrentView) + if (mCurrentView) return mCurrentView->input(config, input); return false; @@ -395,10 +407,8 @@ bool ViewController::input(InputConfig* config, Input input) void ViewController::update(int deltaTime) { - if(mCurrentView) - { + if (mCurrentView) mCurrentView->update(deltaTime); - } updateSelf(deltaTime); } @@ -409,48 +419,48 @@ void ViewController::render(const Transform4x4f& parentTrans) Transform4x4f transInverse; transInverse.invert(trans); - // camera position, position + size + // Camera position, position + size. Vector3f viewStart = transInverse.translation(); - Vector3f viewEnd = transInverse * Vector3f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight(), 0); + Vector3f viewEnd = transInverse * Vector3f((float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight(), 0); // Keep track of UI mode changes. UIModeController::getInstance()->monitorUIMode(); - // draw systemview + // Draw system view. getSystemListView()->render(trans); - // draw gamelists - for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) - { - // clipping + // Draw gamelists. + for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) { + // Clipping. Vector3f guiStart = it->second->getPosition(); - Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(), it->second->getSize().y(), 0); + Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(), + it->second->getSize().y(), 0); - if(guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() && - guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) + if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() && + guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) it->second->render(trans); } - if(mWindow->peekGui() == this) + if (mWindow->peekGui() == this) mWindow->renderHelpPromptsEarly(); - // fade out - if(mFadeOpacity) - { + // Fade out. + if (mFadeOpacity) { unsigned int fadeColor = 0x00000000 | (unsigned char)(mFadeOpacity * 255); Renderer::setMatrix(parentTrans); - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), fadeColor, fadeColor); + Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), + Renderer::getScreenHeight(), fadeColor, fadeColor); } } void ViewController::preload() { uint32_t i = 0; - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) - { - if(Settings::getInstance()->getBool("SplashScreen") && - Settings::getInstance()->getBool("SplashScreenProgress")) - { + for (auto it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); it++) { + if (Settings::getInstance()->getBool("SplashScreen") && + Settings::getInstance()->getBool("SplashScreenProgress")) { i++; char buffer[100]; sprintf (buffer, "Loading '%s' (%d/%d)", @@ -461,80 +471,74 @@ void ViewController::preload() (*it)->getIndex()->resetFilters(); getGameListView(*it); } - // load navigation sounds + // Load navigation sounds. navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme()); } void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme) { - for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) - { - if(it->second.get() == view) - { + for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) { + if (it->second.get() == view) { bool isCurrent = (mCurrentView == it->second); SystemData* system = it->first; FileData* cursor = view->getCursor(); mGameListViews.erase(it); - if(reloadTheme) + if (reloadTheme) system->loadTheme(); system->getIndex()->setUIModeFilters(); std::shared_ptr newView = getGameListView(system); - // to counter having come from a placeholder + // To counter having come from a placeholder. if (!cursor->isPlaceHolder()) { newView->setCursor(cursor); } - if(isCurrent) + if (isCurrent) mCurrentView = newView; break; } } - // Redisplay the current view + // Redisplay the current view. if (mCurrentView) mCurrentView->onShow(); - } void ViewController::reloadAll() { - // clear all gamelistviews + // Clear all GameListViews. std::map cursorMap; - for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) - { + for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) cursorMap[it->first] = it->second->getCursor(); - } + mGameListViews.clear(); - - // load themes, create gamelistviews and reset filters - for(auto it = cursorMap.cbegin(); it != cursorMap.cend(); it++) - { + // Load themes, create GameListViews and reset filters. + for (auto it = cursorMap.cbegin(); it != cursorMap.cend(); it++) { it->first->loadTheme(); it->first->getIndex()->resetFilters(); getGameListView(it->first)->setCursor(it->second); } - // Rebuild SystemListView + // Rebuild SystemListView. mSystemListView.reset(); getSystemListView(); - // update mCurrentView since the pointers changed - if(mState.viewing == GAME_LIST) - { + // Update mCurrentView since the pointers changed. + if (mState.viewing == GAME_LIST) { mCurrentView = getGameListView(mState.getSystem()); - }else if(mState.viewing == SYSTEM_SELECT) - { + } + else if (mState.viewing == SYSTEM_SELECT) { SystemData* system = mState.getSystem(); goToSystemView(SystemData::sSystemVector.front()); mSystemListView->goToSystem(system, false); mCurrentView = mSystemListView; - }else{ + } + else { goToSystemView(SystemData::sSystemVector.front()); } - // load navigation sounds + // Load navigation sounds. navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme()); updateHelpPrompts(); @@ -543,19 +547,19 @@ void ViewController::reloadAll() std::vector ViewController::getHelpPrompts() { std::vector prompts; - if(!mCurrentView) + if (!mCurrentView) return prompts; prompts = mCurrentView->getHelpPrompts(); - if(!(UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("DisableKidStartMenu"))) + if (!(UIModeController::getInstance()->isUIModeKid() && + Settings::getInstance()->getBool("DisableKidStartMenu"))) prompts.push_back(HelpPrompt("start", "menu")); - return prompts; } HelpStyle ViewController::getHelpStyle() { - if(!mCurrentView) + if (!mCurrentView) return GuiComponent::getHelpStyle(); return mCurrentView->getHelpStyle(); diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index c5a4d3aca..63b3c192d 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -1,3 +1,12 @@ +// +// ViewController.h +// +// Handles overall system navigation including animations and transitions. +// Also creates the gamelist views and handles refresh and reloads of these when needed +// (for example when metadata has been changed or when a list sorting has taken place). +// Initiates the launching of games, calling FileData to do the actual launch. +// + #pragma once #ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H #define ES_APP_VIEWS_VIEW_CONTROLLER_H @@ -11,7 +20,8 @@ class IGameListView; class SystemData; class SystemView; -// Used to smoothly transition the camera between multiple views (e.g. from system to system, from gamelist to gamelist). +// Handles transitions between views, e.g. from system to system and from gamelist to gamelist. +// Also sets up the initial gamelists and refreshes and reloads them as required. class ViewController : public GuiComponent { public: @@ -27,8 +37,11 @@ public: // If a basic view detected a metadata change, it can request to recreate // the current gamelist view (as it may change to be detailed). void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false); - inline void reloadGameListView(SystemData* system, bool reloadTheme = false) { reloadGameListView(getGameListView(system).get(), reloadTheme); } - void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes. + inline void reloadGameListView(SystemData* system, bool reloadTheme = false) + { reloadGameListView(getGameListView(system).get(), reloadTheme); } + // Reload everything with a theme. + // Used when the "ThemeSet" setting changes. + void reloadAll(); // Navigation. void goToNextGameList(); @@ -42,22 +55,21 @@ public: // Plays a nice launch effect and launches the game at the end of it. // Once the game terminates, plays a return effect. - void launch(FileData* game, Vector3f centerCameraOn = Vector3f(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0)); + void launch(FileData* game, Vector3f centerCameraOn = + Vector3f(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0)); bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Transform4x4f& parentTrans) override; - enum ViewMode - { + enum ViewMode { NOTHING, START_SCREEN, SYSTEM_SELECT, GAME_LIST }; - enum GameListViewType - { + enum GameListViewType { AUTOMATIC, BASIC, DETAILED, @@ -65,11 +77,11 @@ public: VIDEO }; - struct State - { + struct State { ViewMode viewing; - inline SystemData* getSystem() const { assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); return system; } + inline SystemData* getSystem() const + { assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); return system; } private: friend ViewController; diff --git a/es-app/src/views/gamelist/BasicGameListView.cpp b/es-app/src/views/gamelist/BasicGameListView.cpp index ac0eb6137..0c88238d9 100644 --- a/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/es-app/src/views/gamelist/BasicGameListView.cpp @@ -1,3 +1,9 @@ +// +// BasicGameListView.cpp +// +// Interface that defines a GameListView of the type 'Basic'. +// + #include "views/gamelist/BasicGameListView.h" #include "utils/FileSystemUtil.h" @@ -7,8 +13,11 @@ #include "Settings.h" #include "SystemData.h" -BasicGameListView::BasicGameListView(Window* window, FileData* root) - : ISimpleGameListView(window, root), mList(window) +BasicGameListView::BasicGameListView( + Window* window, + FileData* root) + : ISimpleGameListView(window, root), + mList(window) { mList.setSize(mSize.x(), mSize.y() * 0.8f); mList.setPosition(0, mSize.y() * 0.2f); @@ -29,9 +38,9 @@ void BasicGameListView::onThemeChanged(const std::shared_ptr& theme) void BasicGameListView::onFileChanged(FileData* file, FileChangeType change) { - if(change == FILE_METADATA_CHANGED) + if (change == FILE_METADATA_CHANGED) { - // might switch to a detailed view + // Might switch to a detailed view. ViewController::get()->reloadGameListView(this); return; } @@ -43,55 +52,23 @@ void BasicGameListView::populateList(const std::vector& files) { mList.clear(); mHeaderText.setText(mRoot->getSystem()->getFullName()); - if (files.size() > 0) - { - - std::string systemName = mRoot->getSystem()->getName(); - - bool favoritesFirst = Settings::getInstance()->getBool("FavoritesFirst"); - - bool showFavoriteIcon = (systemName != "favorites" && systemName != "recent"); - if (!showFavoriteIcon) - favoritesFirst = false; - - if (favoritesFirst) - { - for (auto file : files) - { - if (!file->getFavorite()) - continue; - - if (showFavoriteIcon) - mList.add(FAVORITE_GAME_CHAR + " " + file->getName(), file, file->getType() == FOLDER); - else if (file->getType() == FOLDER) - mList.add(FAVORITE_FOLDER_CHAR + " " + file->getName(), file, true); - else - mList.add(file->getName(), file, false); + if (files.size() > 0) { + for (auto it = files.cbegin(); it != files.cend(); it++) { + if ((*it)->getFavorite() && + mRoot->getSystem()->getName() != "favorites") { + mList.add(FAVORITE_GAME_CHAR + " " + (*it)->getName(), + *it, ((*it)->getType() == FOLDER)); } - } - - for (auto file : files) - { - if (file->getFavorite()) - { - if (favoritesFirst) - continue; - - if (showFavoriteIcon) - { - mList.add(FAVORITE_GAME_CHAR + " " + file->getName(), file, file->getType() == FOLDER); - continue; - } + else if ((*it)->getType() == FOLDER && + mRoot->getSystem()->getName() != "collections") { + mList.add(FAVORITE_FOLDER_CHAR + " " + (*it)->getName(), *it, true); + } + else { + mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER)); } - - if (file->getType() == FOLDER) - mList.add(FAVORITE_FOLDER_CHAR + " " + file->getName(), file, true); - else - mList.add(file->getName(), file, false); } } - else - { + else { addPlaceholder(); } } @@ -103,25 +80,25 @@ FileData* BasicGameListView::getCursor() void BasicGameListView::setCursor(FileData* cursor) { - if(!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) - { + if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) { populateList(cursor->getParent()->getChildrenListToDisplay()); 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()) + // 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) + while (ptr && ptr != mRoot) { tmp.push(ptr); ptr = ptr->getParent(); } - // flip the stack and put it in mCursorStack + // Flip the stack and put it in mCursorStack. mCursorStack = std::stack(); - while(!tmp.empty()) + while (!tmp.empty()) { mCursorStack.push(tmp.top()); tmp.pop(); @@ -132,8 +109,9 @@ void BasicGameListView::setCursor(FileData* cursor) void BasicGameListView::addPlaceholder() { - // empty list - add a placeholder - FileData* placeholder = new FileData(PLACEHOLDER, "", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); + // Empty list - add a placeholder. + FileData* placeholder = new FileData(PLACEHOLDER, "", + this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER)); } @@ -154,47 +132,47 @@ void BasicGameListView::launch(FileData* game) void BasicGameListView::remove(FileData *game, bool deleteFile) { + // Actually delete the file on the filesystem. if (deleteFile) - Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem + Utils::FileSystem::removeFile(game->getPath()); + FileData* parent = game->getParent(); - if (getCursor() == game) // Select next element in list, or prev if none - { + // 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 = (int)std::distance(siblings.cbegin(), gameIter); - if (gameIter != siblings.cend()) - { + if (gameIter != siblings.cend()) { if ((gamePos + 1) < siblings.size()) - { setCursor(siblings.at(gamePos + 1)); - } else if (gamePos > 1) { + else if (gamePos > 1) setCursor(siblings.at(gamePos - 1)); - } } } mList.remove(game); - if(mList.size() == 0) - { + + if (mList.size() == 0) addPlaceholder(); - } - delete game; // remove before repopulating (removes from parent) - onFileChanged(parent, FILE_REMOVED); // update the view, with game removed + + // Remove before repopulating (removes from parent), then update the view. + delete game; + onFileChanged(parent, FILE_REMOVED); } std::vector BasicGameListView::getHelpPrompts() { std::vector prompts; - if(Settings::getInstance()->getBool("QuickSystemSelect")) + if (Settings::getInstance()->getBool("QuickSystemSelect")) prompts.push_back(HelpPrompt("left/right", "system")); prompts.push_back(HelpPrompt("up/down", "choose")); prompts.push_back(HelpPrompt("a", "launch")); prompts.push_back(HelpPrompt("b", "back")); - if(!UIModeController::getInstance()->isUIModeKid()) + if (!UIModeController::getInstance()->isUIModeKid()) prompts.push_back(HelpPrompt("select", "options")); - if(mRoot->getSystem()->isGameSystem()) + if (mRoot->getSystem()->isGameSystem()) prompts.push_back(HelpPrompt("x", "random")); - if(mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) + if (mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) { std::string prompt = CollectionSystemManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); diff --git a/es-app/src/views/gamelist/BasicGameListView.h b/es-app/src/views/gamelist/BasicGameListView.h index 473916678..1eea37886 100644 --- a/es-app/src/views/gamelist/BasicGameListView.h +++ b/es-app/src/views/gamelist/BasicGameListView.h @@ -1,3 +1,9 @@ +// +// BasicGameListView.h +// +// Interface that defines a GameListView of the type 'basic'. +// + #pragma once #ifndef ES_APP_VIEWS_GAME_LIST_BASIC_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_BASIC_GAME_LIST_VIEW_H @@ -10,7 +16,7 @@ class BasicGameListView : public ISimpleGameListView public: BasicGameListView(Window* window, FileData* root); - // Called when a FileData* is added, has its metadata changed, or is removed + // Called when a FileData* is added, has its metadata changed, or is removed. virtual void onFileChanged(FileData* file, FileChangeType change); virtual void onThemeChanged(const std::shared_ptr& theme); diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index 6646e7244..b642377a3 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -1,24 +1,44 @@ +// +// DetailedGameListView.cpp +// +// Interface that defines a GameListView of the type 'detailed'. +// + #include "views/gamelist/DetailedGameListView.h" #include "animations/LambdaAnimation.h" #include "views/ViewController.h" -DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : - BasicGameListView(window, root), - mDescContainer(window), mDescription(window), - mThumbnail(window), - mMarquee(window), - mImage(window), +DetailedGameListView::DetailedGameListView( + Window* window, + FileData* root) + : BasicGameListView(window, root), + mDescContainer(window), + mDescription(window), - mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), - mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window), + mThumbnail(window), + mMarquee(window), + mImage(window), - mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window), - mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), - mName(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) { - //mHeaderImage.setPosition(mSize.x() * 0.25f, 0); - const float padding = 0.01f; mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); @@ -26,7 +46,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mList.setAlignment(TextListComponent::ALIGN_LEFT); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - // Thumbnail + // Thumbnail. mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setVisible(false); @@ -34,23 +54,23 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mThumbnail.setDefaultZIndex(25); addChild(&mThumbnail); - // Marquee + // Marquee. mMarquee.setOrigin(0.5f, 0.5f); - // Default to off the screen + // Default to off the screen. mMarquee.setPosition(2.0f, 2.0f); mMarquee.setVisible(false); mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f); mMarquee.setDefaultZIndex(35); addChild(&mMarquee); - // image + // 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*padding), mSize.y() * 0.4f); mImage.setDefaultZIndex(30); addChild(&mImage); - // metadata labels + values + // Metadata labels + values. mLblRating.setText("Rating: "); addChild(&mLblRating); addChild(&mRating); @@ -85,7 +105,8 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - + mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -94,7 +115,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mDescription.setSize(mDescContainer.getSize().x(), 0); mDescContainer.addChild(&mDescription); - initMDLabels(); initMDValues(); updateInfoPanel(); @@ -105,9 +125,12 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them 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); + 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); initMDLabels(); @@ -118,11 +141,8 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" }; - for(unsigned int i = 0; i < labels.size(); i++) - { + for (unsigned int i = 0; i < labels.size(); i++) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); - } - initMDValues(); std::vector values = getMDValues(); @@ -132,14 +152,14 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them "md_genre", "md_players", "md_lastplayed", "md_playcount" }; - for(unsigned int i = 0; i < values.size(); i++) - { + 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); + mDescContainer.applyTheme(theme, getName(), "md_description", + POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); mDescription.setSize(mDescContainer.getSize().x(), 0); - mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mDescription.applyTheme(theme, getName(), "md_description", + ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); sortChildren(); } @@ -156,15 +176,14 @@ void DetailedGameListView::initMDLabels() const float colSize = (mSize.x() * 0.48f) / colCount; const float rowPadding = 0.01f * mSize.y(); - for(unsigned int i = 0; i < components.size(); i++) - { + for (unsigned int i = 0; i < components.size(); i++) { const unsigned int row = i % rowCount; Vector3f pos(0.0f, 0.0f, 0.0f); - if(row == 0) - { + if (row == 0) { pos = start + Vector3f(colSize * (i / rowCount), 0, 0); - }else{ - // work from the last component + } + else { + // Work from the last component. GuiComponent* lc = components[i-1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -193,20 +212,22 @@ void DetailedGameListView::initMDValues() float bottom = 0.0f; const float colSize = (mSize.x() * 0.48f) / 2; - for(unsigned int i = 0; i < labels.size(); i++) - { + for (unsigned int i = 0; i < labels.size(); i++) { const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; - values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); + values[i]->setPosition(labels[i]->getPosition() + + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setDefaultZIndex(40); float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); - if(testBot > bottom) + + 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()); + mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - + mDescContainer.getPosition().y()); } void DetailedGameListView::updateInfoPanel() @@ -214,12 +235,12 @@ void DetailedGameListView::updateInfoPanel() FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); bool fadingOut; - if(file == NULL) - { + if (file == nullptr) { //mImage.setImage(""); //mDescription.setText(""); fadingOut = true; - }else{ + } + else { mThumbnail.setImage(file->getThumbnailPath()); mMarquee.setImage(file->getMarqueePath()); mImage.setImage(file->getImagePath()); @@ -234,8 +255,7 @@ void DetailedGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); mName.setValue(file->metadata.get("name")); - if(file->getType() == GAME) - { + if (file->getType() == GAME) { mLastPlayed.setValue(file->metadata.get("lastplayed")); mPlayCount.setValue(file->metadata.get("playcount")); } @@ -252,18 +272,13 @@ void DetailedGameListView::updateInfoPanel() std::vector labels = getMDLabels(); comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - for(auto it = comps.cbegin(); it != comps.cend(); it++) - { + 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) - { + // 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((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255)); }; comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); @@ -274,7 +289,7 @@ void DetailedGameListView::updateInfoPanel() void DetailedGameListView::launch(FileData* game) { Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); - if(mImage.hasImage()) + if (mImage.hasImage()) target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); ViewController::get()->launch(game, target); diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index 681ffce5f..7e3f8e529 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -1,3 +1,9 @@ +// +// DetailedGameListView.h +// +// Interface that defines a GameListView of the type 'detailed'. +// + #pragma once #ifndef ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H @@ -28,7 +34,14 @@ private: ImageComponent mMarquee; ImageComponent mImage; - TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount; + TextComponent mLblRating; + TextComponent mLblReleaseDate; + TextComponent mLblDeveloper; + TextComponent mLblPublisher; + TextComponent mLblGenre; + TextComponent mLblPlayers; + TextComponent mLblLastPlayed; + TextComponent mLblPlayCount; RatingComponent mRating; DateTimeComponent mReleaseDate; diff --git a/es-app/src/views/gamelist/GridGameListView.cpp b/es-app/src/views/gamelist/GridGameListView.cpp index 2e07b72a6..85aa7424f 100644 --- a/es-app/src/views/gamelist/GridGameListView.cpp +++ b/es-app/src/views/gamelist/GridGameListView.cpp @@ -1,3 +1,9 @@ +// +// GridGameListView.cpp +// +// Interface that defines a GameListView of the type 'grid'. +// + #include "views/gamelist/GridGameListView.h" #include "animations/LambdaAnimation.h" @@ -12,24 +18,42 @@ #endif #include "components/VideoVlcComponent.h" -GridGameListView::GridGameListView(Window* window, FileData* root) : - ISimpleGameListView(window, root), - mGrid(window), mMarquee(window), - mImage(window), - mVideo(nullptr), - mVideoPlaying(false), - mDescContainer(window), mDescription(window), +GridGameListView::GridGameListView( + Window* window, + FileData* root) + : ISimpleGameListView(window, root), - mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), - mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window), + mGrid(window), + mMarquee(window), + mImage(window), + mVideo(nullptr), + mVideoPlaying(false), - mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window), - mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), - mName(window) + mDescContainer(window), + mDescription(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) { const float padding = 0.01f; -// Create the correct type of video window +// Create the correct type of video window. #ifdef _RPI_ if (Settings::getInstance()->getBool("VideoOmxPlayer")) mVideo = new VideoPlayerComponent(window, ""); @@ -46,7 +70,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : populateList(root->getChildrenListToDisplay()); - // metadata labels + values + // Metadata labels + values. mLblRating.setText("Rating: "); addChild(&mLblRating); addChild(&mRating); @@ -81,7 +105,8 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - + mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -89,7 +114,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : mDescription.setFont(Font::get(FONT_SIZE_SMALL)); mDescription.setSize(mDescContainer.getSize().x(), 0); 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*padding), mSize.y() * 0.18f); @@ -128,7 +153,7 @@ FileData* GridGameListView::getCursor() void GridGameListView::setCursor(FileData* file) { - if(!mGrid.setCursor(file)) + if (!mGrid.setCursor(file)) { populateList(file->getParent()->getChildrenListToDisplay()); mGrid.setCursor(file); @@ -147,11 +172,13 @@ std::string GridGameListView::getQuickSystemSelectLeftButton() bool GridGameListView::input(InputConfig* config, Input input) { - if (input.value == 0 and (config->isMappedLike("left", input) || config->isMappedLike("right", input) - || (config->isMappedLike("up", input)) || (config->isMappedLike("down", input)) )) + if (input.value == 0 and (config->isMappedLike("left", input) || + config->isMappedLike("right", input) || + (config->isMappedLike("up", input)) || + (config->isMappedLike("down", input)) )) navigationsounds.playThemeNavigationSound(SCROLLSOUND); - if(config->isMappedLike("left", input) || config->isMappedLike("right", input)) + if (config->isMappedLike("left", input) || config->isMappedLike("right", input)) return GuiComponent::input(config, input); return ISimpleGameListView::input(config, input); @@ -167,8 +194,8 @@ const std::string GridGameListView::getImagePath(FileData* file) else if (src == ImageSource::MARQUEE) return file->getMarqueePath(); - // If no thumbnail was found, then use the image media type - if(file->getThumbnailPath() == ""); + // If no thumbnail was found, then use the image media type. + if (file->getThumbnailPath() == ""); return file->getImagePath(); return file->getThumbnailPath(); @@ -178,17 +205,12 @@ void GridGameListView::populateList(const std::vector& files) { mGrid.clear(); mHeaderText.setText(mRoot->getSystem()->getFullName()); - if (files.size() > 0) - { + if (files.size() > 0) { for (auto it = files.cbegin(); it != files.cend(); it++) - { mGrid.add((*it)->getName(), getImagePath(*it), *it); - } } else - { addPlaceholder(); - } } void GridGameListView::onThemeChanged(const std::shared_ptr& theme) @@ -199,9 +221,12 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) 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); - mVideo->applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | 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); initMDLabels(); std::vector labels = getMDLabels(); @@ -211,11 +236,8 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" }; - for(unsigned int i = 0; i < labels.size(); i++) - { + for (unsigned int i = 0; i < labels.size(); i++) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); - } - initMDValues(); std::vector values = getMDValues(); @@ -225,16 +247,17 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) "md_genre", "md_players", "md_lastplayed", "md_playcount" }; - for(unsigned int i = 0; i < values.size(); i++) - { + 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); + mDescContainer.applyTheme(theme, getName(), "md_description", + POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); mDescription.setSize(mDescContainer.getSize().x(), 0); - mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mDescription.applyTheme(theme, getName(), "md_description", + ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); - // Repopulate list in case new theme is displaying a different image. Preserve selection. + // Repopulate list in case a new theme is displaying a different image. + // Preserve selection. FileData* file = mGrid.getSelected(); populateList(mRoot->getChildrenListToDisplay()); mGrid.setCursor(file); @@ -254,15 +277,14 @@ void GridGameListView::initMDLabels() const float colSize = (mSize.x() * 0.48f) / colCount; const float rowPadding = 0.01f * mSize.y(); - for(unsigned int i = 0; i < components.size(); i++) - { + for (unsigned int i = 0; i < components.size(); i++) { const unsigned int row = i % rowCount; Vector3f pos(0.0f, 0.0f, 0.0f); - if(row == 0) - { + if (row == 0) { pos = start + Vector3f(colSize * (i / rowCount), 0, 0); - }else{ - // work from the last component + } + else { + // Work from the last component. GuiComponent* lc = components[i-1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -291,36 +313,37 @@ void GridGameListView::initMDValues() float bottom = 0.0f; const float colSize = (mSize.x() * 0.48f) / 2; - for(unsigned int i = 0; i < labels.size(); i++) - { + for (unsigned int i = 0; i < labels.size(); i++) { const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; - values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); + values[i]->setPosition(labels[i]->getPosition() + + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setDefaultZIndex(40); float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); - if(testBot > bottom) + 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()); + mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - + mDescContainer.getPosition().y()); } void GridGameListView::updateInfoPanel() { - FileData* file = (mGrid.size() == 0 || mGrid.isScrolling()) ? NULL : mGrid.getSelected(); + FileData* file = (mGrid.size() == 0 || mGrid.isScrolling()) ? nullptr : mGrid.getSelected(); bool fadingOut; - if(file == NULL) + if (file == nullptr) { mVideo->setVideo(""); mVideo->setImage(""); mVideoPlaying = false; - //mDescription.setText(""); fadingOut = true; }else{ +// Temporary fix to disable only audio from playing // if (!mVideo->setVideo(file->getVideoPath())) // { // mVideo->setDefaultVideo(); @@ -330,7 +353,7 @@ void GridGameListView::updateInfoPanel() // mVideo->setImage(file->getThumbnailPath()); mMarquee.setImage(file->getMarqueePath()); // mImage.setImage(file->getImagePath()); - + mDescription.setText(file->metadata.get("desc")); mDescContainer.reset(); @@ -342,8 +365,7 @@ void GridGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); mName.setValue(file->metadata.get("name")); - if(file->getType() == GAME) - { + if (file->getType() == GAME) { mLastPlayed.setValue(file->metadata.get("lastplayed")); mPlayCount.setValue(file->metadata.get("playcount")); } @@ -360,18 +382,13 @@ void GridGameListView::updateInfoPanel() std::vector labels = getMDLabels(); comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - for(auto it = comps.cbegin(); it != comps.cend(); it++) - { + 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) - { + // 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((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255)); }; comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); @@ -381,67 +398,62 @@ void GridGameListView::updateInfoPanel() void GridGameListView::addPlaceholder() { - // empty grid - add a placeholder - FileData* placeholder = new FileData(PLACEHOLDER, "", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); + // Empty grid - add a placeholder. + FileData* placeholder = new FileData(PLACEHOLDER, "", + this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); mGrid.add(placeholder->getName(), "", placeholder); } void GridGameListView::launch(FileData* game) -{ +{ float screenWidth = (float) Renderer::getScreenWidth(); float screenHeight = (float) Renderer::getScreenHeight(); Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0); - if(mMarquee.hasImage() && - (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && - mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) - { + if (mMarquee.hasImage() && + (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && + mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0); - } - else if(mImage.hasImage() && - (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && - mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) - { + else if (mImage.hasImage() && + (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && + mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); - } - else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && - mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) - { + else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && + mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0); - } ViewController::get()->launch(game, target); - } void GridGameListView::remove(FileData *game, bool deleteFile) { + // Actually delete the file on the filesystem. if (deleteFile) - Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem + Utils::FileSystem::removeFile(game->getPath()); + FileData* parent = game->getParent(); - if (getCursor() == game) // Select next element in list, or prev if none - { + // 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 = (int)std::distance(siblings.cbegin(), gameIter); - if (gameIter != siblings.cend()) - { + if (gameIter != siblings.cend()) { if ((gamePos + 1) < (int)siblings.size()) - { setCursor(siblings.at(gamePos + 1)); - } else if ((gamePos - 1) > 0) { + else if ((gamePos - 1) > 0) setCursor(siblings.at(gamePos - 1)); - } } } mGrid.remove(game); - if(mGrid.size() == 0) - { + + if (mGrid.size() == 0) addPlaceholder(); - } - delete game; // remove before repopulating (removes from parent) - onFileChanged(parent, FILE_REMOVED); // update the view, with game removed + + // Remove before repopulating (removes from parent). + // Update the view, with game removed. + delete game; + onFileChanged(parent, FILE_REMOVED); } std::vector GridGameListView::getMDLabels() @@ -476,17 +488,16 @@ std::vector GridGameListView::getHelpPrompts() { std::vector prompts; - if(Settings::getInstance()->getBool("QuickSystemSelect")) + if (Settings::getInstance()->getBool("QuickSystemSelect")) prompts.push_back(HelpPrompt("lr", "system")); prompts.push_back(HelpPrompt("up/down/left/right", "choose")); prompts.push_back(HelpPrompt("a", "launch")); prompts.push_back(HelpPrompt("b", "back")); - if(!UIModeController::getInstance()->isUIModeKid()) + if (!UIModeController::getInstance()->isUIModeKid()) prompts.push_back(HelpPrompt("select", "options")); - if(mRoot->getSystem()->isGameSystem()) + if (mRoot->getSystem()->isGameSystem()) prompts.push_back(HelpPrompt("x", "random")); - if(mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) - { + if (mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) { std::string prompt = CollectionSystemManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); } diff --git a/es-app/src/views/gamelist/GridGameListView.h b/es-app/src/views/gamelist/GridGameListView.h index 6f5798fef..c8a19b806 100644 --- a/es-app/src/views/gamelist/GridGameListView.h +++ b/es-app/src/views/gamelist/GridGameListView.h @@ -1,3 +1,9 @@ +// +// GridGameListView.h +// +// Interface that defines a GameListView of the type 'grid'. +// + #pragma once #ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H @@ -46,7 +52,14 @@ private: void initMDLabels(); void initMDValues(); - TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount; + TextComponent mLblRating; + TextComponent mLblReleaseDate; + TextComponent mLblDeveloper; + TextComponent mLblPublisher; + TextComponent mLblGenre; + TextComponent mLblPlayers; + TextComponent mLblLastPlayed; + TextComponent mLblPlayCount; ImageComponent mMarquee; VideoComponent* mVideo; diff --git a/es-app/src/views/gamelist/IGameListView.cpp b/es-app/src/views/gamelist/IGameListView.cpp index 39ebf52b0..d37581af8 100644 --- a/es-app/src/views/gamelist/IGameListView.cpp +++ b/es-app/src/views/gamelist/IGameListView.cpp @@ -1,3 +1,9 @@ +// +// IGameListView.cpp +// +// Interface that defines the minimum for a GameListView. +// + #include "views/gamelist/IGameListView.h" #include "guis/GuiGamelistOptions.h" @@ -8,16 +14,17 @@ bool IGameListView::input(InputConfig* config, Input input) { - // select to open GuiGamelistOptions - if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && input.value) - { + // Select button opens GuiGamelistOptions. + if (!UIModeController::getInstance()->isUIModeKid() && + config->isMappedTo("select", input) && input.value) { mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem())); return true; - - // Ctrl-R to reload a 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) - { + } + // 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) << "reloading view"; ViewController::get()->reloadGameListView(this, true); return true; @@ -46,8 +53,10 @@ void IGameListView::render(const Transform4x4f& parentTrans) float scaleX = trans.r0().x(); float scaleY = trans.r1().y(); - Vector2i pos((int)Math::round(trans.translation()[0]), (int)Math::round(trans.translation()[1])); - Vector2i size((int)Math::round(mSize.x() * scaleX), (int)Math::round(mSize.y() * scaleY)); + Vector2i pos((int)Math::round(trans.translation()[0]), + (int)Math::round(trans.translation()[1])); + Vector2i size((int)Math::round(mSize.x() * scaleX), + (int)Math::round(mSize.y() * scaleY)); Renderer::pushClipRect(pos, size); renderChildren(trans); diff --git a/es-app/src/views/gamelist/IGameListView.h b/es-app/src/views/gamelist/IGameListView.h index da6299d27..3ab837fae 100644 --- a/es-app/src/views/gamelist/IGameListView.h +++ b/es-app/src/views/gamelist/IGameListView.h @@ -1,3 +1,9 @@ +// +// IGameListView.h +// +// Interface that defines the minimum for a GameListView. +// + #pragma once #ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H @@ -13,12 +19,18 @@ class Window; class IGameListView : public GuiComponent { public: - IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root) - { setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); } + IGameListView( + Window* window, + FileData* root) + : GuiComponent(window), + mRoot(root) + { setSize((float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight()); } virtual ~IGameListView() {} - // Called when a new file is added, a file is removed, a file's metadata changes, or a file's children are sorted. + // Called when a new file is added, a file is removed, a file's metadata changes, + // or a file's children are sorted. // NOTE: FILE_SORTED is only reported for the topmost FileData, where the sort started. // Since sorts are recursive, that FileData's children probably changed too. virtual void onFileChanged(FileData* file, FileChangeType change) = 0; diff --git a/es-app/src/views/gamelist/ISimpleGameListView.cpp b/es-app/src/views/gamelist/ISimpleGameListView.cpp index 747600112..260b647c4 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -1,3 +1,9 @@ +// +// ISimpleGameListView.cpp +// +// Interface that defines a simple GameListView. +// + #include "views/gamelist/ISimpleGameListView.h" #include "views/UIModeController.h" @@ -7,8 +13,13 @@ #include "Sound.h" #include "SystemData.h" -ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGameListView(window, root), - mHeaderText(window), mHeaderImage(window), mBackground(window) +ISimpleGameListView::ISimpleGameListView( + Window* window, + FileData* root) + : IGameListView(window, root), + mHeaderText(window), + mHeaderImage(window), + mBackground(window) { mHeaderText.setText("Logo Text"); mHeaderText.setSize(mSize.x(), 0); @@ -35,7 +46,7 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr& theme mHeaderImage.applyTheme(theme, getName(), "logo", ALL); mHeaderText.applyTheme(theme, getName(), "logoText", ALL); - // Remove old theme extras + // Remove old theme extras. for (auto extra : mThemeExtras) { removeChild(extra); @@ -43,18 +54,16 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr& theme } mThemeExtras.clear(); - // Add new theme extras + // Add new theme extras. mThemeExtras = ThemeData::makeExtras(theme, getName(), mWindow); for (auto extra : mThemeExtras) - { addChild(extra); - } - if(mHeaderImage.hasImage()) - { + if (mHeaderImage.hasImage()) { removeChild(&mHeaderText); addChild(&mHeaderImage); - }else{ + } + else { addChild(&mHeaderText); removeChild(&mHeaderImage); } @@ -62,8 +71,8 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr& theme void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*change*/) { - // we could be tricky here to be efficient; - // but this shouldn't happen very often so we'll just always repopulate + // 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()); @@ -78,20 +87,17 @@ void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*cha bool ISimpleGameListView::input(InputConfig* config, Input input) { - std::shared_ptr soundfile; + std::shared_ptr soundfile; - if(input.value != 0) - { - if(config->isMappedTo("a", input)) - { + if (input.value != 0) { + if (config->isMappedTo("a", input)) { FileData* cursor = getCursor(); - if(cursor->getType() == GAME) - { + if (cursor->getType() == GAME) { launch(cursor); - }else{ - // it's a folder - if(cursor->getChildren().size() > 0) - { + } + else { + // It's a folder. + if (cursor->getChildren().size() > 0) { mCursorStack.push(cursor); populateList(cursor->getChildrenListToDisplay()); FileData* cursor = getCursor(); @@ -100,14 +106,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } return true; - }else if(config->isMappedTo("b", input)) - { - if(mCursorStack.size()) - { + } + else if (config->isMappedTo("b", input)) { + if (mCursorStack.size()) { populateList(mCursorStack.top()->getParent()->getChildren()); setCursor(mCursorStack.top()); mCursorStack.pop(); - }else{ + } + else { navigationsounds.playThemeNavigationSound(BACKSOUND); onFocusLost(); SystemData* systemToView = getCursor()->getSystem(); @@ -119,57 +125,40 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } return true; - }else if(config->isMappedLike(getQuickSystemSelectRightButton(), input)) - { - if(Settings::getInstance()->getBool("QuickSystemSelect")) - { + } + else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) { + if (Settings::getInstance()->getBool("QuickSystemSelect")) { onFocusLost(); ViewController::get()->goToNextGameList(); return true; } - }else if(config->isMappedLike(getQuickSystemSelectLeftButton(), input)) - { - if(Settings::getInstance()->getBool("QuickSystemSelect")) - { + } + else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) { + if (Settings::getInstance()->getBool("QuickSystemSelect")) { onFocusLost(); ViewController::get()->goToPrevGameList(); return true; } - }else if (config->isMappedTo("x", input)) - { - if (mRoot->getSystem()->isGameSystem()) - { - // go to random system game + } + else if (config->isMappedTo("x", input)) { + if (mRoot->getSystem()->isGameSystem()) { + // Go to random system game. navigationsounds.playThemeNavigationSound(SCROLLSOUND); FileData* randomGame = getCursor()->getSystem()->getRandomGame(); if (randomGame) - { setCursor(randomGame); - } return true; } - }else if (config->isMappedTo("y", input) && !UIModeController::getInstance()->isUIModeKid()) - { - if(mRoot->getSystem()->isGameSystem()) - { + } + else if (config->isMappedTo("y", input) && + !UIModeController::getInstance()->isUIModeKid()) { + if (mRoot->getSystem()->isGameSystem()) { navigationsounds.playThemeNavigationSound(FAVORITESOUND); - if(CollectionSystemManager::get()->toggleGameInCollection(getCursor())) - { + if (CollectionSystemManager::get()->toggleGameInCollection(getCursor())) return true; - } } } } return IGameListView::input(config, input); } - - - - - - - - - - diff --git a/es-app/src/views/gamelist/ISimpleGameListView.h b/es-app/src/views/gamelist/ISimpleGameListView.h index 785f3992c..5dc8e187d 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/es-app/src/views/gamelist/ISimpleGameListView.h @@ -1,3 +1,9 @@ +// +// ISimpleGameListView.h +// +// Interface that defines a simple GameListView. +// + #pragma once #ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H @@ -11,9 +17,11 @@ class ISimpleGameListView : public IGameListView { public: ISimpleGameListView(Window* window, FileData* root); + virtual ~ISimpleGameListView() {} - // Called when a new file is added, a file is removed, a file's metadata changes, or a file's children are sorted. + // Called when a new file is added, a file is removed, a file's metadata changes, + // or a file's children are sorted. // NOTE: FILE_SORTED is only reported for the topmost FileData, where the sort started. // Since sorts are recursive, that FileData's children probably changed too. virtual void onFileChanged(FileData* file, FileChangeType change); diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 420212f64..e6734234b 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -1,3 +1,9 @@ +// +// VideoGameListView.cpp +// +// Interface that defines a GameListView of the type 'video'. +// + #include "views/gamelist/VideoGameListView.h" #include "animations/LambdaAnimation.h" @@ -11,25 +17,41 @@ #include "Settings.h" #endif -VideoGameListView::VideoGameListView(Window* window, FileData* root) : - BasicGameListView(window, root), - mDescContainer(window), mDescription(window), - mThumbnail(window), - mMarquee(window), - mImage(window), - mVideo(nullptr), - mVideoPlaying(false), +VideoGameListView::VideoGameListView( + Window* window, + FileData* root) + : BasicGameListView(window, root), + mDescContainer(window), + mDescription(window), - mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), - mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window), + mThumbnail(window), + mMarquee(window), + mImage(window), + mVideo(nullptr), + mVideoPlaying(false), - mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window), - mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), - mName(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) { const float padding = 0.01f; - // Create the correct type of video window + // Create the correct type of video window. #ifdef _RPI_ if (Settings::getInstance()->getBool("VideoOmxPlayer")) mVideo = new VideoPlayerComponent(window, ""); @@ -44,7 +66,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : mList.setAlignment(TextListComponent::ALIGN_LEFT); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - // Thumbnail + // Thumbnail. mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setVisible(false); @@ -52,30 +74,30 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : mThumbnail.setDefaultZIndex(35); addChild(&mThumbnail); - // Marquee + // Marquee. mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f); mMarquee.setDefaultZIndex(35); addChild(&mMarquee); - // Image + // Image. mImage.setOrigin(0.5f, 0.5f); - // Default to off the screen + // Default to off the screen. mImage.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f); mImage.setVisible(false); mImage.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f); mImage.setDefaultZIndex(30); addChild(&mImage); - // video + // Video. mVideo->setOrigin(0.5f, 0.5f); mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f); mVideo->setSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.4f); mVideo->setDefaultZIndex(30); addChild(mVideo); - // metadata labels + values + // Metadata labels + values. mLblRating.setText("Rating: "); addChild(&mLblRating); addChild(&mRating); @@ -110,7 +132,8 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - + mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -133,10 +156,14 @@ 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); + 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); initMDLabels(); @@ -147,11 +174,8 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" }; - for(unsigned int i = 0; i < labels.size(); i++) - { + for (unsigned int i = 0; i < labels.size(); i++) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); - } - initMDValues(); std::vector values = getMDValues(); @@ -161,14 +185,14 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) "md_genre", "md_players", "md_lastplayed", "md_playcount" }; - for(unsigned int i = 0; i < values.size(); i++) - { + 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); + mDescContainer.applyTheme(theme, getName(), "md_description", + POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); mDescription.setSize(mDescContainer.getSize().x(), 0); - mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mDescription.applyTheme(theme, getName(), "md_description", + ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); sortChildren(); } @@ -185,15 +209,14 @@ void VideoGameListView::initMDLabels() const float colSize = (mSize.x() * 0.48f) / colCount; const float rowPadding = 0.01f * mSize.y(); - for(unsigned int i = 0; i < components.size(); i++) - { + for (unsigned int i = 0; i < components.size(); i++) { const unsigned int row = i % rowCount; Vector3f pos(0.0f, 0.0f, 0.0f); - if(row == 0) - { + if (row == 0) { pos = start + Vector3f(colSize * (i / rowCount), 0, 0); - }else{ - // work from the last component + } + else { + // Work from the last component. GuiComponent* lc = components[i-1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -222,24 +245,23 @@ void VideoGameListView::initMDValues() float bottom = 0.0f; const float colSize = (mSize.x() * 0.48f) / 2; - for(unsigned int i = 0; i < labels.size(); i++) - { + for (unsigned int i = 0; i < labels.size(); i++) { const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; - values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); + values[i]->setPosition(labels[i]->getPosition() + + Vector3f(labels[i]->getSize().x(),heightDiff, 0)); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setDefaultZIndex(40); float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); - if(testBot > bottom) + 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()); + mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - + mDescContainer.getPosition().y()); } - - void VideoGameListView::updateInfoPanel() { FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); @@ -247,8 +269,7 @@ void VideoGameListView::updateInfoPanel() Utils::FileSystem::removeFile(getTitlePath()); bool fadingOut; - if(file == NULL) - { + if (file == nullptr) { mVideo->setVideo(""); mVideo->setImage(""); mVideoPlaying = false; @@ -256,14 +277,13 @@ void VideoGameListView::updateInfoPanel() //mDescription.setText(""); fadingOut = true; - }else{ + } + else { if (!mVideo->setVideo(file->getVideoPath())) - { mVideo->setDefaultVideo(); - } + mVideoPlaying = true; -// mVideo->setImage(file->getThumbnailPath()); mVideo->setImage(file->getImagePath()); mThumbnail.setImage(file->getThumbnailPath()); mMarquee.setImage(file->getMarqueePath()); @@ -280,8 +300,7 @@ void VideoGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); mName.setValue(file->metadata.get("name")); - if(file->getType() == GAME) - { + if (file->getType() == GAME) { mLastPlayed.setValue(file->metadata.get("lastplayed")); mPlayCount.setValue(file->metadata.get("playcount")); } @@ -299,18 +318,13 @@ void VideoGameListView::updateInfoPanel() std::vector labels = getMDLabels(); comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - for(auto it = comps.cbegin(); it != comps.cend(); it++) - { + 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) - { + // 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((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255)); }; comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); @@ -325,35 +339,31 @@ void VideoGameListView::launch(FileData* game) Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0); - if(mMarquee.hasImage() && - (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && - mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) - { + if (mMarquee.hasImage() && + (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && + mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0); - } - else if(mThumbnail.hasImage() && - (mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f && - mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f)) - { + + else if (mThumbnail.hasImage() && + (mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f && + mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f)) target = Vector3f(mThumbnail.getCenter().x(), mThumbnail.getCenter().y(), 0); - } - else if(mImage.hasImage() && - (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && - mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) - { + + else if (mImage.hasImage() && + (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && + mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); - } - else if(mHeaderImage.hasImage() && - (mHeaderImage.getPosition().x() < screenWidth && mHeaderImage.getPosition().x() > 0.0f && - mHeaderImage.getPosition().y() < screenHeight && mHeaderImage.getPosition().y() > 0.0f)) - { + + else if (mHeaderImage.hasImage() && + (mHeaderImage.getPosition().x() < screenWidth && + mHeaderImage.getPosition().x() > 0.0f && + mHeaderImage.getPosition().y() < screenHeight && + mHeaderImage.getPosition().y() > 0.0f)) target = Vector3f(mHeaderImage.getCenter().x(), mHeaderImage.getCenter().y(), 0); - } - else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && - mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) - { + + else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && + mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0); - } ViewController::get()->launch(game, target); } diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index edbc2c21f..dfc168471 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -1,3 +1,9 @@ +// +// VideoGameListView.h +// +// Interface that defines a GameListView of the type 'video'. +// + #pragma once #ifndef ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H @@ -36,7 +42,14 @@ private: VideoComponent* mVideo; ImageComponent mImage; - TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount; + TextComponent mLblRating; + TextComponent mLblReleaseDate; + TextComponent mLblDeveloper; + TextComponent mLblPublisher; + TextComponent mLblGenre; + TextComponent mLblPlayers; + TextComponent mLblLastPlayed; + TextComponent mLblPlayCount; RatingComponent mRating; DateTimeComponent mReleaseDate; diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index c56fc6867..38f30b0ac 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -1,3 +1,9 @@ +// +// GuiComponent.cpp +// +// Basic GUI component handling such as placement, rotation, Z-order, rendering and animation. +// + #include "GuiComponent.h" #include "animations/Animation.h" @@ -8,11 +14,19 @@ #include "Window.h" #include -GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL), mOpacity(255), - mPosition(Vector3f::Zero()), mOrigin(Vector2f::Zero()), mRotationOrigin(0.5, 0.5), - mSize(Vector2f::Zero()), mTransform(Transform4x4f::Identity()), mIsProcessing(false), mVisible(true) +GuiComponent::GuiComponent(Window* window) + : mWindow(window), + mParent(NULL), + mOpacity(255), + mPosition(Vector3f::Zero()), + mOrigin(Vector2f::Zero()), + mRotationOrigin(0.5, 0.5), + mSize(Vector2f::Zero()), + mTransform(Transform4x4f::Identity()), + mIsProcessing(false), + mVisible(true) { - for(unsigned char i = 0; i < MAX_ANIMATIONS; i++) + for (unsigned char i = 0; i < MAX_ANIMATIONS; i++) mAnimationMap[i] = NULL; } @@ -22,18 +36,17 @@ GuiComponent::~GuiComponent() cancelAllAnimations(); - if(mParent) + if (mParent) mParent->removeChild(this); - for(unsigned int i = 0; i < getChildCount(); i++) + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->setParent(NULL); } bool GuiComponent::input(InputConfig* config, Input input) { - for(unsigned int i = 0; i < getChildCount(); i++) - { - if(getChild(i)->input(config, input)) + for (unsigned int i = 0; i < getChildCount(); i++) { + if (getChild(i)->input(config, input)) return true; } @@ -42,16 +55,14 @@ bool GuiComponent::input(InputConfig* config, Input input) void GuiComponent::updateSelf(int deltaTime) { - for(unsigned char i = 0; i < MAX_ANIMATIONS; i++) + for (unsigned char i = 0; i < MAX_ANIMATIONS; i++) advanceAnimation(i, deltaTime); } void GuiComponent::updateChildren(int deltaTime) { - for(unsigned int i = 0; i < getChildCount(); i++) - { + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->update(deltaTime); - } } void GuiComponent::update(int deltaTime) @@ -71,10 +82,8 @@ void GuiComponent::render(const Transform4x4f& parentTrans) void GuiComponent::renderChildren(const Transform4x4f& transform) const { - for(unsigned int i = 0; i < getChildCount(); i++) - { + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->render(transform); - } } Vector3f GuiComponent::getPosition() const @@ -175,12 +184,12 @@ Vector2f GuiComponent::getCenter() const mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2); } -//Children stuff. +// Children stuff. void GuiComponent::addChild(GuiComponent* cmp) { mChildren.push_back(cmp); - if(cmp->getParent()) + if (cmp->getParent()) cmp->getParent()->removeChild(cmp); cmp->setParent(this); @@ -188,20 +197,16 @@ void GuiComponent::addChild(GuiComponent* cmp) void GuiComponent::removeChild(GuiComponent* cmp) { - if(!cmp->getParent()) + if (!cmp->getParent()) return; - if(cmp->getParent() != this) - { + if (cmp->getParent() != this) LOG(LogError) << "Tried to remove child from incorrect parent!"; - } cmp->setParent(NULL); - for(auto i = mChildren.cbegin(); i != mChildren.cend(); i++) - { - if(*i == cmp) - { + for (auto i = mChildren.cbegin(); i != mChildren.cend(); i++) { + if (*i == cmp) { mChildren.erase(i); return; } @@ -215,7 +220,7 @@ void GuiComponent::clearChildren() void GuiComponent::sortChildren() { - std::stable_sort(mChildren.begin(), mChildren.end(), [](GuiComponent* a, GuiComponent* b) { + std::stable_sort(mChildren.begin(), mChildren.end(), [](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); }); } @@ -248,10 +253,8 @@ unsigned char GuiComponent::getOpacity() const void GuiComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) - { + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) (*it)->setOpacity(opacity); - } } const Transform4x4f& GuiComponent::getTransform() @@ -259,28 +262,26 @@ const Transform4x4f& GuiComponent::getTransform() mTransform = Transform4x4f::Identity(); mTransform.translate(mPosition); if (mScale != 1.0) - { mTransform.scale(mScale); - } - if (mRotation != 0.0) - { + if (mRotation != 0.0) { // Calculate offset as difference between origin and rotation origin Vector2f rotationSize = getRotationSize(); float xOff = (mOrigin.x() - mRotationOrigin.x()) * rotationSize.x(); float yOff = (mOrigin.y() - mRotationOrigin.y()) * rotationSize.y(); - // transform to offset point + // Transform to offset point if (xOff != 0.0 || yOff != 0.0) mTransform.translate(Vector3f(xOff * -1, yOff * -1, 0.0f)); - // apply rotation transform + // Apply rotation transform mTransform.rotateZ(mRotation); // Tranform back to original point if (xOff != 0.0 || yOff != 0.0) mTransform.translate(Vector3f(xOff, yOff, 0.0f)); } - mTransform.translate(Vector3f(mOrigin.x() * mSize.x() * -1, mOrigin.y() * mSize.y() * -1, 0.0f)); + mTransform.translate(Vector3f(mOrigin.x() * mSize.x() * -1, + mOrigin.y() * mSize.y() * -1, 0.0f)); return mTransform; } @@ -295,32 +296,31 @@ std::string GuiComponent::getValue() const void GuiComponent::textInput(const char* text) { - for(auto iter = mChildren.cbegin(); iter != mChildren.cend(); iter++) - { + for (auto iter = mChildren.cbegin(); iter != mChildren.cend(); iter++) (*iter)->textInput(text); - } } -void GuiComponent::setAnimation(Animation* anim, int delay, std::function finishedCallback, bool reverse, unsigned char slot) +void GuiComponent::setAnimation(Animation* anim, int delay, + std::function finishedCallback, bool reverse, unsigned char slot) { assert(slot < MAX_ANIMATIONS); AnimationController* oldAnim = mAnimationMap[slot]; mAnimationMap[slot] = new AnimationController(anim, delay, finishedCallback, reverse); - if(oldAnim) + if (oldAnim) delete oldAnim; } bool GuiComponent::stopAnimation(unsigned char slot) { assert(slot < MAX_ANIMATIONS); - if(mAnimationMap[slot]) - { + if (mAnimationMap[slot]) { delete mAnimationMap[slot]; mAnimationMap[slot] = NULL; return true; - }else{ + } + else { return false; } } @@ -328,13 +328,13 @@ bool GuiComponent::stopAnimation(unsigned char slot) bool GuiComponent::cancelAnimation(unsigned char slot) { assert(slot < MAX_ANIMATIONS); - if(mAnimationMap[slot]) - { + if (mAnimationMap[slot]) { mAnimationMap[slot]->removeFinishedCallback(); delete mAnimationMap[slot]; mAnimationMap[slot] = NULL; return true; - }else{ + } + else { return false; } } @@ -342,16 +342,17 @@ bool GuiComponent::cancelAnimation(unsigned char slot) bool GuiComponent::finishAnimation(unsigned char slot) { assert(slot < MAX_ANIMATIONS); - if(mAnimationMap[slot]) - { - // skip to animation's end - const bool done = mAnimationMap[slot]->update(mAnimationMap[slot]->getAnimation()->getDuration() - mAnimationMap[slot]->getTime()); + if (mAnimationMap[slot]) { + // Skip to animation's end + const bool done = mAnimationMap[slot]->update(mAnimationMap[slot]-> + getAnimation()->getDuration() - mAnimationMap[slot]->getTime()); assert(done); - delete mAnimationMap[slot]; // will also call finishedCallback + delete mAnimationMap[slot]; // Will also call finishedCallback mAnimationMap[slot] = NULL; return true; - }else{ + } + else { return false; } } @@ -360,29 +361,28 @@ bool GuiComponent::advanceAnimation(unsigned char slot, unsigned int time) { assert(slot < MAX_ANIMATIONS); AnimationController* anim = mAnimationMap[slot]; - if(anim) - { + if (anim) { bool done = anim->update(time); - if(done) - { + if (done) { mAnimationMap[slot] = NULL; delete anim; } return true; - }else{ + } + else { return false; } } void GuiComponent::stopAllAnimations() { - for(unsigned char i = 0; i < MAX_ANIMATIONS; i++) + for (unsigned char i = 0; i < MAX_ANIMATIONS; i++) stopAnimation(i); } void GuiComponent::cancelAllAnimations() { - for(unsigned char i = 0; i < MAX_ANIMATIONS; i++) + for (unsigned char i = 0; i < MAX_ANIMATIONS; i++) cancelAnimation(i); } @@ -403,41 +403,44 @@ int GuiComponent::getAnimationTime(unsigned char slot) const return mAnimationMap[slot]->getTime(); } -void GuiComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) +void GuiComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, const std::string& element, unsigned int properties) { - Vector2f scale = getParent() ? getParent()->getSize() : Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + Vector2f scale = getParent() ? getParent()->getSize() + : Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); const ThemeData::ThemeElement* elem = theme->getElement(view, element, ""); - if(!elem) + if (!elem) return; using namespace ThemeFlags; - if(properties & POSITION && elem->has("pos")) - { + if (properties & POSITION && elem->has("pos")) { Vector2f denormalized = elem->get("pos") * scale; setPosition(Vector3f(denormalized.x(), denormalized.y(), 0)); } - if(properties & ThemeFlags::SIZE && elem->has("size")) + if (properties & ThemeFlags::SIZE && elem->has("size")) setSize(elem->get("size") * scale); - // position + size also implies origin - if((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) && elem->has("origin")) + // Position + size also implies origin + if ((properties & ORIGIN || (properties & POSITION && + properties & ThemeFlags::SIZE)) && elem->has("origin")) { setOrigin(elem->get("origin")); + } - if(properties & ThemeFlags::ROTATION) { - if(elem->has("rotation")) + if (properties & ThemeFlags::ROTATION) { + if (elem->has("rotation")) setRotationDegrees(elem->get("rotation")); - if(elem->has("rotationOrigin")) + if (elem->has("rotationOrigin")) setRotationOrigin(elem->get("rotationOrigin")); } - if(properties & ThemeFlags::Z_INDEX && elem->has("zIndex")) + if (properties & ThemeFlags::Z_INDEX && elem->has("zIndex")) setZIndex(elem->get("zIndex")); else setZIndex(getDefaultZIndex()); - if(properties & ThemeFlags::VISIBLE && elem->has("visible")) + if (properties & ThemeFlags::VISIBLE && elem->has("visible")) setVisible(elem->get("visible")); else setVisible(true); @@ -445,15 +448,14 @@ void GuiComponent::applyTheme(const std::shared_ptr& theme, const std void GuiComponent::updateHelpPrompts() { - if(getParent()) - { + if (getParent()) { getParent()->updateHelpPrompts(); return; } std::vector prompts = getHelpPrompts(); - if(mWindow->peekGui() == this) + if (mWindow->peekGui() == this) mWindow->setHelpPrompts(prompts, getHelpStyle()); } @@ -469,30 +471,30 @@ bool GuiComponent::isProcessing() const void GuiComponent::onShow() { - for(unsigned int i = 0; i < getChildCount(); i++) + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->onShow(); } void GuiComponent::onHide() { - for(unsigned int i = 0; i < getChildCount(); i++) + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->onHide(); } void GuiComponent::onScreenSaverActivate() { - for(unsigned int i = 0; i < getChildCount(); i++) + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->onScreenSaverActivate(); } void GuiComponent::onScreenSaverDeactivate() { - for(unsigned int i = 0; i < getChildCount(); i++) + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->onScreenSaverDeactivate(); } void GuiComponent::topWindow(bool isTop) { - for(unsigned int i = 0; i < getChildCount(); i++) + for (unsigned int i = 0; i < getChildCount(); i++) getChild(i)->topWindow(isTop); -} \ No newline at end of file +} diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 082077389..94b192523 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -1,3 +1,9 @@ +// +// GuiComponent.h +// +// Basic GUI component handling such as placement, rotation, Z-order, rendering and animation. +// + #pragma once #ifndef ES_CORE_GUI_COMPONENT_H #define ES_CORE_GUI_COMPONENT_H @@ -25,36 +31,47 @@ public: virtual void textInput(const char* text); - //Called when input is received. - //Return true if the input is consumed, false if it should continue to be passed to other children. + // Called when input is received. + // Return true if the input is consumed, false if + // it should continue to be passed to other children. virtual bool input(InputConfig* config, Input input); - //Called when time passes. Default implementation calls updateSelf(deltaTime) and updateChildren(deltaTime) - so you should probably call GuiComponent::update(deltaTime) at some point (or at least updateSelf so animations work). + // Called when time passes. + // Default implementation calls updateSelf(deltaTime) and updateChildren(deltaTime). + // So you should probably call GuiComponent::update(deltaTime) at some point (or at + // least updateSelf so animations work). virtual void update(int deltaTime); - //Called when it's time to render. By default, just calls renderChildren(parentTrans * getTransform()). - //You probably want to override this like so: - //1. Calculate the new transform that your control will draw at with Transform4x4f t = parentTrans * getTransform(). - //2. Set the renderer to use that new transform as the model matrix - Renderer::setMatrix(t); - //3. Draw your component. - //4. Tell your children to render, based on your component's transform - renderChildren(t). + // Called when it's time to render. + // By default, just calls renderChildren(parentTrans * getTransform()). + // You probably want to override this like so: + // 1. Calculate the new transform that your control will draw at with + // Transform4x4f t = parentTrans * getTransform(). + // 2. Set the renderer to use that new transform as the model matrix + // Renderer::setMatrix(t); + // 3. Draw your component. + // 4. Tell your children to render, based on your component's transform - renderChildren(t). virtual void render(const Transform4x4f& parentTrans); Vector3f getPosition() const; - inline void setPosition(const Vector3f& offset) { setPosition(offset.x(), offset.y(), offset.z()); } + inline void setPosition(const Vector3f& offset) + { setPosition(offset.x(), offset.y(), offset.z()); } void setPosition(float x, float y, float z = 0.0f); virtual void onPositionChanged() {}; - //Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center) + // Sets the origin as a percentage of this image. + // (e.g. (0, 0) is top left, (0.5, 0.5) is the center) Vector2f getOrigin() const; void setOrigin(float originX, float originY); inline void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); } virtual void onOriginChanged() {}; - //Sets the rotation origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center) + // Sets the rotation origin as a percentage of this image. + // (e.g. (0, 0) is top left, (0.5, 0.5) is the center) Vector2f getRotationOrigin() const; void setRotationOrigin(float originX, float originY); - inline void setRotationOrigin(Vector2f origin) { setRotationOrigin(origin.x(), origin.y()); } + inline void setRotationOrigin(Vector2f origin) + { setRotationOrigin(origin.x(), origin.y()); } virtual Vector2f getSize() const; inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); } @@ -92,15 +109,22 @@ public: unsigned int getChildCount() const; GuiComponent* getChild(unsigned int i) const; - // animation will be automatically deleted when it completes or is stopped. + // Animation will be automatically deleted when it completes or is stopped. bool isAnimationPlaying(unsigned char slot) const; bool isAnimationReversed(unsigned char slot) const; int getAnimationTime(unsigned char slot) const; - void setAnimation(Animation* animation, int delay = 0, std::function finishedCallback = nullptr, bool reverse = false, unsigned char slot = 0); + void setAnimation(Animation* animation, int delay = 0, + std::function finishedCallback = nullptr, + bool reverse = false, unsigned char slot = 0); bool stopAnimation(unsigned char slot); - bool cancelAnimation(unsigned char slot); // Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving things in their current state. Returns true if successful (an animation was in this slot). - bool finishAnimation(unsigned char slot); // Calls update(1.f) and finishedCallback, then deletes the animation - basically skips to the end. Returns true if successful (an animation was in this slot). - bool advanceAnimation(unsigned char slot, unsigned int time); // Returns true if successful (an animation was in this slot). + // Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving + // things in their current state. Returns true if successful (an animation was in this slot). + bool cancelAnimation(unsigned char slot); + // Calls update(1.f) and finishedCallback, then deletes the animation - basically skips + // to the end. Returns true if successful (an animation was in this slot). + bool finishAnimation(unsigned char slot); + // Returns true if successful (an animation was in this slot). + bool advanceAnimation(unsigned char slot, unsigned int time); void stopAllAnimations(); void cancelAllAnimations(); @@ -124,7 +148,8 @@ public: // Default implementation just handles and tags as normalized float pairs. // You probably want to keep this behavior for any derived classes as well as add your own. - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties); + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, const std::string& element, unsigned int properties); // Returns a list of help prompts. virtual std::vector getHelpPrompts() { return std::vector(); }; @@ -137,10 +162,12 @@ public: // Returns true if the component is busy doing background processing (e.g. HTTP downloads) bool isProcessing() const; + const static unsigned char MAX_ANIMATIONS = 4; + protected: void renderChildren(const Transform4x4f& transform) const; - void updateSelf(int deltaTime); // updates animations - void updateChildren(int deltaTime); // updates animations + void updateSelf(int deltaTime); // Updates animations + void updateChildren(int deltaTime); // Updates animations unsigned char mOpacity; Window* mWindow; @@ -162,11 +189,9 @@ protected: bool mIsProcessing; bool mVisible; -public: - const static unsigned char MAX_ANIMATIONS = 4; - private: - Transform4x4f mTransform; //Don't access this directly! Use getTransform()! + // Don't access this directly! Use getTransform()! + Transform4x4f mTransform; AnimationController* mAnimationMap[MAX_ANIMATIONS]; }; diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 84fd283f4..ddd08068f 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -68,6 +68,7 @@ void Settings::setDefaults() mStringMap["StartupSystem"] = ""; mBoolMap["DisableKidStartMenu"] = true; mStringMap["MediaDirectory"] = ""; + mStringMap["DefaultSortOrder"] = ""; mBoolMap["VSync"] = true; @@ -139,9 +140,9 @@ void Settings::setDefaults() mStringMap["CollectionSystemsAuto"] = ""; mStringMap["CollectionSystemsCustom"] = ""; mBoolMap["CollectionShowSystemInfo"] = true; - mBoolMap["SortAllSystems"] = false; mBoolMap["UseCustomCollectionsSystem"] = true; + mBoolMap["FavFirstCustom"] = true; mBoolMap["FavoritesFirst"] = true; mBoolMap["LaunchstringOverride"] = true; diff --git a/es-core/src/guis/GuiComplexTextEditPopup.cpp b/es-core/src/guis/GuiComplexTextEditPopup.cpp index c3d666ae6..e781eca0b 100644 --- a/es-core/src/guis/GuiComplexTextEditPopup.cpp +++ b/es-core/src/guis/GuiComplexTextEditPopup.cpp @@ -1,48 +1,77 @@ +// +// GuiComplexTextEditPopup.cpp +// +// Text edit popup with a title, two text strings, a text input box and buttons +// to load the second text string and to clear the input field. +// Intended for updating settings for configuration files and similar. +// + #include "guis/GuiComplexTextEditPopup.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" #include "components/TextEditComponent.h" -GuiComplexTextEditPopup::GuiComplexTextEditPopup(Window* window, const std::string& title, const std::string& infoString1, const std::string& infoString2, - const std::string& initValue, const std::function& okCallback, bool multiLine, const char* acceptBtnText) - : GuiComponent(window), mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 5)), mMultiLine(multiLine) +GuiComplexTextEditPopup::GuiComplexTextEditPopup( + Window* window, + const std::string& title, + const std::string& infoString1, + const std::string& infoString2, + const std::string& initValue, + const std::function& okCallback, + bool multiLine, const char* acceptBtnText) + : GuiComponent(window), + mBackground(window, ":/frame.png"), + mGrid(window, Vector2i(1, 5)), + mMultiLine(multiLine) { addChild(&mBackground); addChild(&mGrid); - mTitle = std::make_shared(mWindow, Utils::String::toUpper(title), Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); - mInfoString1 = std::make_shared(mWindow, infoString1, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); - mInfoString2 = std::make_shared(mWindow, infoString2, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); + mTitle = std::make_shared(mWindow, Utils::String::toUpper(title), + Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); + mInfoString1 = std::make_shared(mWindow, infoString1, + Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); + mInfoString2 = std::make_shared(mWindow, infoString2, + Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); mText = std::make_shared(mWindow); mText->setValue(initValue); - if(!multiLine) + if (!multiLine) mText->setCursor(initValue.size()); std::vector< std::shared_ptr > buttons; - buttons.push_back(std::make_shared(mWindow, acceptBtnText, acceptBtnText, [this, okCallback] { okCallback(mText->getValue()); delete this; })); - buttons.push_back(std::make_shared(mWindow, "LOAD", "load default string", [this, infoString2] { mText->setValue(infoString2); })); - buttons.push_back(std::make_shared(mWindow, "CLEAR", "clear string", [this] { mText->setValue(""); })); - buttons.push_back(std::make_shared(mWindow, "CANCEL", "discard changes", [this] { delete this; })); + buttons.push_back(std::make_shared(mWindow, acceptBtnText, acceptBtnText, + [this, okCallback] { okCallback(mText->getValue()); delete this; })); + buttons.push_back(std::make_shared(mWindow, "LOAD", "load default string", + [this, infoString2] { mText->setValue(infoString2); })); + buttons.push_back(std::make_shared(mWindow, "CLEAR", "clear string", + [this] { mText->setValue(""); })); + buttons.push_back(std::make_shared(mWindow, "CANCEL", "discard changes", + [this] { delete this; })); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true); mGrid.setEntry(mInfoString2, Vector2i(0, 2), false, true); - mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1), + GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); mGrid.setRowHeightPerc(1, 0.15, true); float textHeight = mText->getFont()->getHeight(); - if(multiLine) + + if (multiLine) textHeight *= 6; + mText->setSize(0, textHeight); - setSize(Renderer::getScreenWidth() * 0.75f, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() + 220); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + setSize(Renderer::getScreenWidth() * 0.75f,mTitle->getFont()->getHeight() + + textHeight + mButtonGrid->getSize().y() + 220); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, + (Renderer::getScreenHeight() - mSize.y()) / 2); } void GuiComplexTextEditPopup::onSizeChanged() @@ -51,7 +80,7 @@ void GuiComplexTextEditPopup::onSizeChanged() mText->setSize(mSize.x() - 40, mText->getSize().y()); - // update grid + // Update grid mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y()); mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y() / mSize.y()); mGrid.setSize(mSize); @@ -59,12 +88,11 @@ void GuiComplexTextEditPopup::onSizeChanged() bool GuiComplexTextEditPopup::input(InputConfig* config, Input input) { - if(GuiComponent::input(config, input)) + if (GuiComponent::input(config, input)) return true; - // pressing back when not text editing closes us - if(config->isMappedTo("b", input) && input.value) - { + // Pressing back button when not text editing closes us + if (config->isMappedTo("b", input) && input.value) { delete this; return true; } diff --git a/es-core/src/guis/GuiComplexTextEditPopup.h b/es-core/src/guis/GuiComplexTextEditPopup.h index 3d1d1a75a..6586d0398 100644 --- a/es-core/src/guis/GuiComplexTextEditPopup.h +++ b/es-core/src/guis/GuiComplexTextEditPopup.h @@ -1,3 +1,11 @@ +// +// GuiComplexTextEditPopup.h +// +// Text edit popup with a title, two text strings, a text input box and buttons +// to load the second text string and to clear the input field. +// Intended for updating settings for configuration files and similar. +// + #pragma once #ifndef ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H #define ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H @@ -12,8 +20,15 @@ class TextEditComponent; class GuiComplexTextEditPopup : public GuiComponent { public: - GuiComplexTextEditPopup(Window* window, const std::string& title, const std::string& infoString1, const std::string& infoString2, - const std::string& initValue, const std::function& okCallback, bool multiLine, const char* acceptBtnText = "OK"); + GuiComplexTextEditPopup( + Window* window, + const std::string& title, + const std::string& infoString1, + const std::string& infoString2, + const std::string& initValue, + const std::function& okCallback, + bool multiLine, + const char* acceptBtnText = "OK"); bool input(InputConfig* config, Input input); void onSizeChanged();