diff --git a/projects/frontend/es-app/src/guis/menus/MenuGamelistOptions.cpp b/projects/frontend/es-app/src/guis/menus/MenuGamelistOptions.cpp index 627b4153b0cd626844a728d6c06b0ad9c8a0c3ae..9506af545243972bf2bbacb099aeacc5b5d3ea4b 100644 --- a/projects/frontend/es-app/src/guis/menus/MenuGamelistOptions.cpp +++ b/projects/frontend/es-app/src/guis/menus/MenuGamelistOptions.cpp @@ -20,7 +20,7 @@ MenuGamelistOptions::MenuGamelistOptions(WindowManager& window, SystemData& syst , mResolver(resolver) , mSystem(system) , mSystemManager(systemManager) - , mGamelist(*ViewController::Instance().GetOrCreateGamelistView(&system)) + , mGamelist(*ViewController::Instance().GetOrCreateGamelistView(&system, nullptr)) , mArcade(arcadeInterface) { } diff --git a/projects/frontend/es-app/src/guis/menus/vault/MenuArcade.cpp b/projects/frontend/es-app/src/guis/menus/vault/MenuArcade.cpp index dff9f375b1f36da6d13c6c9c8b9e1aac8681c7b7..eb4efd5da6c7780dd08442bec9fc9718dc8096a3 100644 --- a/projects/frontend/es-app/src/guis/menus/vault/MenuArcade.cpp +++ b/projects/frontend/es-app/src/guis/menus/vault/MenuArcade.cpp @@ -43,12 +43,7 @@ void MenuArcade::MenuSwitchChanged(const ItemSwitch& item, bool& status, int id) (void)item; switch((Components)id) { - case Components::EnhancedView: - { - RecalboxConf::Instance().SetArcadeViewEnhanced(status).Save(); - ViewController::Instance().InvalidateAllGamelistsExcept(nullptr); - break; - } + case Components::EnhancedView: RecalboxConf::Instance().SetArcadeViewEnhanced(status).Save(); break; case Components::FoldClones: { RecalboxConf::Instance().SetArcadeViewFoldClones(status).Save(); @@ -90,7 +85,6 @@ void MenuArcade::MenuSwitchChanged(const ItemSwitch& item, bool& status, int id) case Components::UseDatabasesNames: { RecalboxConf::Instance().SetArcadeUseDatabaseNames(status).Save(); - ViewController::Instance().InvalidateAllGamelistsExcept(nullptr); break; } case Components::ManufacturersVirtual: diff --git a/projects/frontend/es-app/src/guis/menus/vault/MenuUserInterface.cpp b/projects/frontend/es-app/src/guis/menus/vault/MenuUserInterface.cpp index f6306ab13f61061fd59426dc58a6f5de74f1c536..253e12c05657222825d72334b5b51355aaebd660 100644 --- a/projects/frontend/es-app/src/guis/menus/vault/MenuUserInterface.cpp +++ b/projects/frontend/es-app/src/guis/menus/vault/MenuUserInterface.cpp @@ -139,10 +139,10 @@ void MenuUserInterface::MenuSwitchChanged(const ItemSwitch& item, bool& status, break; } case Components::DisplayByFileName: + { RecalboxConf::Instance().SetDisplayByFileName(status).Save(); - ViewController::Instance().GetOrCreateGamelistView(systemData)->refreshList(); - ViewController::Instance().InvalidateAllGamelistsExcept(nullptr); break; + } case Components::Popups: case Components::Theme: case Components::ThemeConfig: diff --git a/projects/frontend/es-app/src/systems/arcade/ArcadeTupple.h b/projects/frontend/es-app/src/systems/arcade/ArcadeTupple.h index cd253fa86718c7a0e2c561fcaac34462bb77d56c..bd697340ddb39ad86a60eb2f40a0332ab70c87ea 100644 --- a/projects/frontend/es-app/src/systems/arcade/ArcadeTupple.h +++ b/projects/frontend/es-app/src/systems/arcade/ArcadeTupple.h @@ -11,10 +11,10 @@ class FileData; struct ArcadeTupple { const ArcadeGame* mArcade; //!< Arcade game - May be null - FileData* mGame; //!< Game - Never null + const FileData* mGame; //!< Game - Never null //! Constructor - ArcadeTupple(const ArcadeGame* arcade, FileData* game) : mArcade(arcade), mGame(game) {} + ArcadeTupple(const ArcadeGame* const arcade, const FileData* const game) : mArcade(arcade), mGame(game) {} }; typedef std::vector ArcadeTupplePointerList; diff --git a/projects/frontend/es-app/src/views/ViewController.cpp b/projects/frontend/es-app/src/views/ViewController.cpp index 956fe4b382912f802775f4bd8a371357f9d00843..fb7651de2b1d12c6af5da23fdbad1817f1f4ad24 100755 --- a/projects/frontend/es-app/src/views/ViewController.cpp +++ b/projects/frontend/es-app/src/views/ViewController.cpp @@ -46,6 +46,7 @@ ViewController::ViewController(WindowManager& window, SystemManager& systemManag , mSoftPatchingLastChoice(0) , mSender(*this) , mNextItem(nullptr) + , mUseEnhencedArcadeView(RecalboxConf::Instance().GetArcadeViewEnhanced()) { // Set interfaces systemManager.SetProgressInterface(&mSplashView); @@ -175,7 +176,7 @@ void ViewController::selectGamelistAndCursor(FileData *file) SystemData& system = file->System(); ReorderGamelistViewBeforeMoving(&system, Move::None); goToGameList(&system); - ISimpleGameListView* view = GetOrCreateGamelistView(&system); + ISimpleGameListView* view = GetOrCreateGamelistView(&system, nullptr); view->setCursorStack(file); view->setCursor(file); } @@ -224,7 +225,7 @@ float ViewController::ReorderGamelistViewBeforeMoving(SystemData* target, Move m if (move == Move::Left && currentIndex < targetIndex) offset = 1; // Move every single gamelist to its position regading visible system list - ISimpleGameListView* gamelistView = GetOrCreateGamelistView(target); + ISimpleGameListView* gamelistView = GetOrCreateGamelistView(target, nullptr); float screenWidth = Renderer::Instance().DisplayWidthAsFloat(); float screenHeight = Renderer::Instance().DisplayHeightAsFloat(); for(auto& kv : mGameListViews) @@ -261,22 +262,10 @@ void ViewController::goToGameList(SystemData* system) mCamera.translation().x() = -position; } - if (mInvalidGameList[system]) - { - mInvalidGameList[system] = false; - if (!ForceGamelistViewRecreation(system)) - { - // if listview has been reload due to last game has been deleted, - // we have to stop the previous goToGameList process because current - // system will no longer exists in the available list - return; - } - } - ChangeView(ViewType::GameList, system); playViewTransition(); - ISimpleGameListView* gamelistView = GetOrCreateGamelistView(system); + ISimpleGameListView* gamelistView = GetOrCreateGamelistView(system, nullptr); NotificationManager::Instance().Notify(*gamelistView->getCursor(), Notification::GamelistBrowsing); // for reload cursor video path if present gamelistView->DoUpdateGameInformation(false); @@ -702,22 +691,40 @@ void ViewController::LaunchAnimated(const EmulatorData& emulator) } } -ISimpleGameListView* ViewController::GetOrCreateGamelistView(SystemData* system, bool reorder) +void ViewController::ArcadeViewEnhancedConfigurationChanged(const bool& value) +{ + if (value != mUseEnhencedArcadeView) + { + mUseEnhencedArcadeView = value; + for (auto& gameListView: mGameListViews) + if (gameListView.first->IsTrueArcade()) + { + // Replace old view with new one + ISimpleGameListView* previousView = gameListView.second; + ISimpleGameListView* nextView = mGameListViews[gameListView.first] = GetOrCreateGamelistView(gameListView.first, gameListView.second); + delete previousView; + // Adjust current view + if (mCurrentView == previousView) + mCurrentView = nextView; + } + } +} + +ISimpleGameListView* ViewController::GetOrCreateGamelistView(SystemData* system, ISimpleGameListView* sourceView) { - //if we already made one, return that one - auto exists = mGameListViews.find(system); - if(exists != mGameListViews.end()) - return exists->second; + // If we already made one, return that one + // Only if sourceView is null so that we don't want to "clone" the old view + if (sourceView == nullptr) + if (ISimpleGameListView** view = mGameListViews.try_get(system); view != nullptr) return *view; //if we didn't, make it, remember it, and return it ISimpleGameListView* view = - (system->Descriptor().IsArcade() && RecalboxConf::Instance().GetArcadeViewEnhanced() && !(system->Name() == "daphne")) ? + (system->Descriptor().IsArcade() && mUseEnhencedArcadeView && !(system->Name() == "daphne")) ? new ArcadeGameListView(mWindow, mSystemManager, *system, mResolver, mFlagCaches) : new DetailedGameListView(mWindow, mSystemManager, *system, mResolver, mFlagCaches); - view->DoInitialize(); + view->DoInitialize(sourceView); mGameListViews[system] = view; - mInvalidGameList[system] = false; if (reorder) ReorderGamelistViewBeforeMoving(system, Move::None); @@ -862,21 +869,17 @@ void ViewController::Render(const Transform4x4f& parentTrans) } } -void ViewController::InvalidateGamelist(const SystemData* system) +void ViewController::InvalidateGamelist(SystemData* system) { - for (auto& mGameListView : mGameListViews) - if (system == (mGameListView.first)) - { - mInvalidGameList[mGameListView.first] = true; - break; - } + ISimpleGameListView** view = mGameListViews.try_get(system); + if (view != nullptr) (*view)->Invalidate(); } void ViewController::InvalidateAllGamelistsExcept(const SystemData* systemExclude) { - for (auto& mGameListView : mGameListViews) - if (systemExclude != (mGameListView.first)) - mInvalidGameList[mGameListView.first] = true; + for (auto& gameListView : mGameListViews) + if (gameListView.first != systemExclude) + gameListView.second->Invalidate(); } bool ViewController::CollectHelpItems(Help& help) @@ -924,7 +927,7 @@ void ViewController::ChangeView(ViewType newViewMode, SystemData* targetSystem) } case ViewType::GameList: { - mCurrentView = GetOrCreateGamelistView(targetSystem); + mCurrentView = GetOrCreateGamelistView(targetSystem, nullptr); mCurrentSystem = targetSystem; break; } @@ -964,7 +967,7 @@ void ViewController::ShowSystem(SystemData* system) mSystemListView.addSystem(system); mSystemListView.Sort(); InvalidateAllGamelistsExcept(nullptr); - GetOrCreateGamelistView(system); + GetOrCreateGamelistView(system, nullptr); } void ViewController::HideSystem(SystemData* system) diff --git a/projects/frontend/es-app/src/views/ViewController.h b/projects/frontend/es-app/src/views/ViewController.h index 3dc93e7630a1f0b990d2f31ab66353639f32ee51..77619a22cbec497fb85c88ea15d41f8992944cd7 100644 --- a/projects/frontend/es-app/src/views/ViewController.h +++ b/projects/frontend/es-app/src/views/ViewController.h @@ -72,7 +72,7 @@ class ViewController : public StaticLifeCycleControler void Launch(FileData* game, const GameLinkedData& netplay, const Vector3f& centerCameraOn, bool forceGoToGame); bool ForceGamelistViewRecreation(SystemData* system); - void InvalidateGamelist(const SystemData* system); + void InvalidateGamelist(SystemData* system); void InvalidateAllGamelistsExcept(const SystemData* systemExclude); // Navigation. @@ -104,7 +104,7 @@ class ViewController : public StaticLifeCycleControler bool CollectHelpItems(Help& help) override; - ISimpleGameListView* GetOrCreateGamelistView(SystemData* system, bool reorder = true); + ISimpleGameListView* GetOrCreateGamelistView(SystemData* system, ISimpleGameListView* sourceView); SystemView& getSystemListView() { return mSystemListView; } @@ -185,6 +185,12 @@ class ViewController : public StaticLifeCycleControler void Completed(const DelayedSystemOperationData& parameter, const bool& result) override; + /* + * RecalboxConf::ArcadeViewEnhancedConfigurationNotify + */ + + void ArcadeViewEnhancedConfigurationChanged(const bool& value) final; + private: //! Animation move enum Move @@ -230,12 +236,10 @@ class ViewController : public StaticLifeCycleControler SplashView mSplashView; GameClipView mGameClipView; CrtCalibrationView mCrtView; - HashMap mInvalidGameList; ViewType mCurrentViewType; //!< Current view type ViewType mPreviousViewType; //!< Previous view type - Transform4x4f mCamera; float mFadeOpacity; bool mLockInput; @@ -265,6 +269,9 @@ class ViewController : public StaticLifeCycleControler //! Fetch info thread signal Signal mSignal; + //! Arcade view state cache + bool mUseEnhencedArcadeView; + /*! * @brief Check if softpatching is required and let the user select * @param emulator Emulator data diff --git a/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp index f62bfe986a711d663818f348eac6c81968455837..c5c1bc1c4ddfd9c2b3a83d6c4405bc90a89fbced 100644 --- a/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp @@ -9,6 +9,7 @@ ArcadeGameListView::ArcadeGameListView(WindowManager& window, SystemManager& systemManager, SystemData& system, const IGlobalVariableResolver& resolver, PictogramCaches& flagCache) : DetailedGameListView(window, systemManager, system, resolver, flagCache) , mDatabase(nullptr) + , mIsUsingArcadeDatabaseNames(RecalboxConf::Instance().GetArcadeUseDatabaseNames()) { } @@ -95,7 +96,7 @@ void ArcadeGameListView::BuildList() colorIndexOffset = HighlightColor; } // Store - mList.add(GetIconifiedDisplayName(parent), parent.mGame, colorIndexOffset + (parent.mGame->IsFolder() ? FolderColor : GameColor), false); + mList.add(GetDisplayName(parent), (FileData*)parent.mGame, colorIndexOffset + (parent.mGame->IsFolder() ? FolderColor : GameColor), false); // Children? if (/*parent.mArcade != nullptr && */parent.mCloneList != nullptr) @@ -112,7 +113,7 @@ void ArcadeGameListView::BuildList() if (!Regions::IsIn4Regions(clone.mGame->Metadata().Region().Pack, currentRegion)) colorIndexOffset = HighlightColor; // Store - mList.add(GetIconifiedDisplayName(clone), clone.mGame, colorIndexOffset, false); + mList.add(GetDisplayName(clone), (FileData*)clone.mGame, colorIndexOffset, false); } } } @@ -131,6 +132,15 @@ bool ArcadeGameListView::HasMatchingManufacturer(const HashSet& manufacture return false; } +void ArcadeGameListView::ArcadeUseDatabaseNamesConfigurationChanged(const bool& value) +{ + if (value != mIsUsingArcadeDatabaseNames) + { + mIsUsingArcadeDatabaseNames = value; + Invalidate(); + } +} + String ArcadeGameListView::getArcadeItemIcon(const ArcadeTupple& game) { String result; @@ -168,16 +178,39 @@ String ArcadeGameListView::getArcadeItemIcon(const ArcadeTupple& game) return result.Append(' '); } -String ArcadeGameListView::GetDisplayName(const ArcadeTupple& game) +const ArcadeTupple* ArcadeGameListView::Lookup(const FileData& item) +{ + ArcadeTupple** tupple = mGameListLookup.try_get(&item); + if (tupple != nullptr) return *tupple; + { LOG(LogError) << "[ArcadeGameListView] Lookup FileData failed for game " << item.Name(); } + return nullptr; +} + +String ArcadeGameListView::GetUndecoratedDisplayName(const FileData& game) +{ + const ArcadeTupple* tupple = Lookup(game); + if (tupple != nullptr) return GetUndecoratedDisplayName(*tupple); + ArcadeTupple noParent(nullptr, &game); + return GetUndecoratedDisplayName(noParent); +} + +String ArcadeGameListView::GetDisplayName(const FileData& game) +{ + const ArcadeTupple* tupple = Lookup(game); + if (tupple != nullptr) return GetDisplayName(*tupple); + ArcadeTupple noParent(nullptr, &game); + return GetDisplayName(noParent); +} + +String ArcadeGameListView::GetUndecoratedDisplayName(const ArcadeTupple& game) { - if (RecalboxConf::Instance().GetArcadeUseDatabaseNames() && game.mArcade != nullptr) - return game.mArcade->ArcadeName(); - return RecalboxConf::Instance().GetDisplayByFileName() ? game.mGame->Metadata().RomFileOnly().ToString() : game.mGame->Name(); // TODO: Use gugue new displayable name ASAP + if (mIsUsingArcadeDatabaseNames && game.mArcade != nullptr) return game.mArcade->ArcadeName(); + return DetailedGameListView::GetUndecoratedDisplayName(*game.mGame); } -String ArcadeGameListView::GetIconifiedDisplayName(const ArcadeTupple& game) +String ArcadeGameListView::GetDisplayName(const ArcadeTupple& game) { - return getArcadeItemIcon(game).Append(GetDisplayName(game)); + return getArcadeItemIcon(game).Append(GetUndecoratedDisplayName(game)); } void ArcadeGameListView::BuildAndSortArcadeGames(FileData::List& items, FileSorts::ComparerArcade comparer, bool ascending) @@ -277,6 +310,15 @@ void ArcadeGameListView::BuildAndSortArcadeGames(FileData::List& items, FileSort AddSortedCategories({ &bios }, comparer, ascending); AddSortedCategories({ ¬Working }, comparer, ascending); + // Build fast lookup + for(ParentTupple& parent : mGameList) + { + mGameListLookup[parent.mGame] = &parent; + if (parent.mCloneList != nullptr) + for(ArcadeTupple& clone : *parent.mCloneList) + mGameListLookup[clone.mGame] = &clone; + } + // For virtual arcade systems, cleanup database if (mSystem.IsVirtualArcade()) mDatabase = nullptr; } @@ -287,7 +329,8 @@ void ArcadeGameListView::AddSortedCategories(const std::vector ArcadeGameListView::GetAvailableLetters() Array unicode; // 1 bit per unicode char used unicode.ExpandTo(UnicodeSize / (sizeof(unsigned int) * 8)); - for (auto* file : files) + for(const FileData* file : files) if (file->IsGame()) { - const ArcadeTupple& currentArcade = Lookup(*file); - if (currentArcade.mArcade != nullptr && currentArcade.mArcade->Hierarchy() == ArcadeGame::Type::Clone) continue; // Skip clones + const ArcadeTupple* currentArcade = Lookup(*file); + if (IsClone(currentArcade)) continue; // Skip clones // Tag every first characters from every game name - String::Unicode wc = String::UpperUnicode(file->Name().ReadFirstUTF8()); + String::Unicode wc = String::UpperUnicode((currentArcade != nullptr ? GetUndecoratedDisplayName(*currentArcade) : GetUndecoratedDisplayName(*file)).ReadFirstUTF8()); if (wc < UnicodeSize) // Ignore extended unicodes unicode((int)(wc >> 5)) |= 1 << (wc & 0x1F); } @@ -461,14 +504,16 @@ Array ArcadeGameListView::GetAvailableLetters() void ArcadeGameListView::JumpToLetter(unsigned int unicode) { - for(int c = 0; c < (int)getCursorIndexMax(); ++c) - if (getDataAt(c)->IsGame()) + for(int c = 0; c < mList.Count(); ++c) + if (FileData& file = *getDataAt(c); file.IsGame()) { - const ArcadeTupple& currentArcade = Lookup(*getDataAt(c)); - if (currentArcade.mArcade != nullptr && currentArcade.mArcade->Hierarchy() == ArcadeGame::Type::Clone) continue; // Skip clones - if (String::UpperUnicode(LookupDisplayName(*getDataAt(c)).ReadFirstUTF8()) == unicode) + const ArcadeTupple* currentArcade = Lookup(file); + if (IsClone(currentArcade)) continue; // Skip clones + + String::Unicode wc = String::UpperUnicode((currentArcade != nullptr ? GetUndecoratedDisplayName(*currentArcade) : GetUndecoratedDisplayName(file)).ReadFirstUTF8()); + if (wc == unicode) { - setCursor(getDataAt(c)); + setCursor(&file); break; } } @@ -476,18 +521,21 @@ void ArcadeGameListView::JumpToLetter(unsigned int unicode) void ArcadeGameListView::JumpToNextLetter(bool forward) { - const ArcadeTupple& baseArcade = Lookup(*getCursor()); - const FileData* baseGame = baseArcade.mArcade != nullptr && baseArcade.mArcade->Hierarchy() == ArcadeGame::Type::Clone ? baseArcade.mArcade->Parent() : getCursor(); - UnicodeChar baseChar = String::UpperUnicode(LookupDisplayName(*baseGame).ReadFirstUTF8()); - int max = getCursorIndexMax() + 1; + const ArcadeTupple* baseArcade = Lookup(*getCursor()); + // Clone? get parent + if (IsClone(baseArcade)) baseArcade = Lookup(*baseArcade->mArcade->Parent()); + UnicodeChar baseChar = String::UpperUnicode((baseArcade != nullptr ? GetUndecoratedDisplayName(*baseArcade) : GetUndecoratedDisplayName(*getCursor())).ReadFirstUTF8()); + int max = mList.Count(); int step = max + (forward ? 1 : -1); int cursorIndex = getCursorIndex(); for(int i = cursorIndex; (i = (i + step) % max) != cursorIndex; ) { - const ArcadeTupple& currentArcade = Lookup(*getDataAt(i)); - if (currentArcade.mArcade != nullptr && currentArcade.mArcade->Hierarchy() == ArcadeGame::Type::Clone) continue; // Skip clones - if (String::UpperUnicode(LookupDisplayName(*getDataAt(i)).ReadFirstUTF8()) != baseChar) + const ArcadeTupple* currentArcade = Lookup(*getDataAt(i)); + if (IsClone(currentArcade)) continue; // Skip clones + + String::Unicode wc = String::UpperUnicode((currentArcade != nullptr ? GetUndecoratedDisplayName(*currentArcade) : GetUndecoratedDisplayName(*getDataAt(i))).ReadFirstUTF8()); + if (wc != baseChar) { setCursorIndex(i); break; @@ -495,25 +543,6 @@ void ArcadeGameListView::JumpToNextLetter(bool forward) } } -const ArcadeTupple& ArcadeGameListView::Lookup(const FileData& item) -{ - for(const ParentTupple& parent : mGameList) - if (parent.mGame == &item) return parent; - else if (parent.mCloneList != nullptr) - for(const ArcadeTupple& clone : *parent.mCloneList) - if (clone.mGame == &item) - return clone; - - { LOG(LogError) << "[ArcadeGameListView] Lookup FileData failed for game " << item.Name(); } - static ArcadeTupple nullTupple(nullptr, nullptr); - return nullTupple; -} - -String ArcadeGameListView::LookupDisplayName(const FileData& item) -{ - return GetDisplayName(Lookup(item)); -} - std::vector ArcadeGameListView::GetManufacturerList() const { if (mDatabase == nullptr) return std::vector(); @@ -535,18 +564,6 @@ int ArcadeGameListView::GetGameCountForManufacturer(int driverIndex) const return count; } -String ArcadeGameListView::GetDisplayName(FileData& game) -{ - for(const ParentTupple& parent : mGameList) - if (parent.mGame == &game) return GetIconifiedDisplayName(parent); - else if (parent.mCloneList != nullptr) - for(const ArcadeTupple& clone : *parent.mCloneList) - if (clone.mGame == &game) return GetIconifiedDisplayName(clone); - - // Fallback - return GetIconifiedDisplayName(ArcadeTupple(nullptr, &game)); -} - String ArcadeGameListView::GetDescription(FileData& game) { String emulator; @@ -561,13 +578,3 @@ String ArcadeGameListView::GetDescription(FileData& game) } return game.Metadata().Description(); } - -void ArcadeGameListView::RefreshItem(FileData* game) -{ - if (game == nullptr || !game->IsGame()) { LOG(LogError) << "[DetailedGameListView] Trying to refresh null or empty item"; return; } - - int index = mList.Lookup(game); - if (index < 0) { LOG(LogError) << "[DetailedGameListView] Trying to refresh a not found item"; return; } - mList.changeTextAt(index, GetDisplayName(*game)); -} - diff --git a/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.h b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.h index 9645084c38c02037be5d179d04a0d91fb38cfd7a..8667553610f1a3c4867f0ca03afc6c76cad211f6 100644 --- a/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.h +++ b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.h @@ -56,6 +56,8 @@ class ArcadeGameListView : public DetailedGameListView //! List ParentTuppleList mGameList; + //! Fast lookup in Parent Tupple list + HashMap mGameListLookup; //! Last database to use const ArcadeDatabase* mDatabase; @@ -64,6 +66,11 @@ class ArcadeGameListView : public DetailedGameListView //! Default core for the current folder String mDefaultCore; + //! Cached original name + bool mIsUsingArcadeDatabaseNames; + + bool IsClone(const ArcadeTupple* tupple) { return tupple != nullptr && tupple->mArcade != nullptr && tupple->mArcade->Hierarchy() == ArcadeGame::Type::Clone; } + /*! * @brief Get Arcade interface * @return Arcade interface @@ -110,28 +117,36 @@ class ArcadeGameListView : public DetailedGameListView */ void AddSortedCategories(const std::vector& categoryLists, FileSorts::ComparerArcade comparer, bool ascending); + /*! + * @brief Get description of the given game + * @param game Game + * @return Description + */ + String GetDescription(FileData& game) override; + /*! * @brief Get display name of the given game * @param database Arcade Database * @param game Game * @return Final display name */ - static String GetDisplayName(const ArcadeTupple& game); + String GetUndecoratedDisplayName(const ArcadeTupple& game); /*! - * @brief Get description of the given game + * @brief Get display name of the given game + * @param database Arcade Database * @param game Game - * @return Description + * @return Final display name */ - String GetDescription(FileData& game) override; + String GetUndecoratedDisplayName(const FileData& game) override; /*! - * @brief Get display name of the given game + * @brief Get display name of the given game w/icons * @param database Arcade Database * @param game Game * @return Final display name */ - String GetDisplayName(FileData& game) override; + String GetDisplayName(const ArcadeTupple& game); /*! * @brief Get display name of the given game w/icons @@ -139,7 +154,7 @@ class ArcadeGameListView : public DetailedGameListView * @param game Game * @return Final display name */ - String GetIconifiedDisplayName(const ArcadeTupple& game); + String GetDisplayName(const FileData& game) override; /*! * @brief Get available regions from the given listt @@ -168,16 +183,9 @@ class ArcadeGameListView : public DetailedGameListView /*! * @brief Lookup the arcade tupple attached to the given * @param item game - * @return ArcadeTupple (or empty ArcadeTupple if the lookup fails!) - */ - const ArcadeTupple& Lookup(const FileData& item); - - /*! - * @brief Lookup display name of the given item - * @param item game - * @return Display name + * @return ArcadeTupple or nullptr if the lookup fails! */ - String LookupDisplayName(const FileData& item); + const ArcadeTupple* Lookup(const FileData& item); //! Fold all parents void FoldAll(); @@ -264,10 +272,10 @@ class ArcadeGameListView : public DetailedGameListView void ReturnedFromGame(FileData* game) override { (void)game; } /* - * RecalboxConf::IArcadeUseDatabaseNamesNotification + * RecalboxConf::ArcadeUseDatabaseNamesConfigurationNotify */ - void ConfigurationArcadeUseDatabaseNamesChanged(const bool& value) final { (void)value; ListRefreshRequired(); } + void ArcadeUseDatabaseNamesConfigurationChanged(const bool& value) final; /* * RecalboxConf::IArcadeViewHideBiosNotification diff --git a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp index 7dcf1b7223b4dfeba093aab5fb7091df226c47a3..d126d6d5d84683418de8648f59329fc74cddd742 100755 --- a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -14,6 +14,7 @@ DetailedGameListView::DetailedGameListView(WindowManager&window, SystemManager& , mElapsedTimeOnGame(0) , mIsScraping(false) , mHeaderStars(window, 0.f) + , mIsDisplayedByFileName(RecalboxConf::Instance().GetDisplayByFileName()) , mImage(window) , mNoImage(window) , mVideo(window, this) @@ -949,13 +950,23 @@ String DetailedGameListView::getItemIcon(const FileData& item) return String(); } -String DetailedGameListView::GetDisplayName(FileData& game) +void DetailedGameListView::DisplayByFileNameConfigurationChanged(const bool& value) { - // Select Icon - String result = getItemIcon(game); - // Get name - result.Append(RecalboxConf::Instance().GetDisplayByFileName() ? game.Metadata().RomFileOnly().ToString() : game.Name()); - return result; + if (value != mIsDisplayedByFileName) + { + mIsDisplayedByFileName = value; + Invalidate(); + } +} + +String DetailedGameListView::GetUndecoratedDisplayName(const FileData& game) +{ + return mIsDisplayedByFileName ? game.Metadata().RomFileOnly().ToString() : game.Name(); +} + +String DetailedGameListView::GetDisplayName(const FileData& game) +{ + return getItemIcon(game).Append(GetUndecoratedDisplayName(game)); } void DetailedGameListView::populateList(const FolderData& folder) @@ -1092,10 +1103,9 @@ void DetailedGameListView::setCursorStack(FileData* cursor) { mCursorStack.push(reverseCursorStack.top()); reverseCursorStack.pop(); - - FolderData& tmp = !mCursorStack.empty() ? *mCursorStack.top() : mSystem.MasterRoot(); - populateList(tmp); } + FolderData& tmp = !mCursorStack.empty() ? *mCursorStack.top() : mSystem.MasterRoot(); + populateList(tmp); } void DetailedGameListView::setCursor(FileData* cursor) diff --git a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.h b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.h index f4cb4850e7ac578ef597d7451c8d7eb5a34f197e..63087ae11af3452cbf2cb094d8417383fa7ca5dc 100644 --- a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.h +++ b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.h @@ -17,6 +17,7 @@ class DetailedGameListView : public ISimpleGameListView , public ITextListComponentOverlay , private IScraperEngineStage , private IVideoComponentAction + , private RecalboxConf::DisplayByFileNameConfigurationNotify { public: DetailedGameListView(WindowManager& window, SystemManager& systemManager, SystemData& system, const IGlobalVariableResolver& resolver, PictogramCaches& pictogramCache); @@ -82,12 +83,19 @@ class DetailedGameListView : public ISimpleGameListView void launch(FileData* game) override; void clean() override { mVideo.setVideo(Path::Empty); } + /*! + * @brief Get undecorated (NO icons or decorations) display name in functions of options + * @param game Game to lookup name + * @return Name + */ + String GetUndecoratedDisplayName(const FileData& game) override; + /*! * @brief Get display name of the given game * @param game Game * @return Final display name */ - virtual String GetDisplayName(FileData& game); + virtual String GetDisplayName(const FileData& game); /*! * @brief Get description of the given game @@ -118,6 +126,12 @@ class DetailedGameListView : public ISimpleGameListView void setCursorIndex(int index) override; void removeEntry(FileData* fileData) override; + /* + * RecalboxConf::DisplayByFileNameConfigurationNotify + */ + + void DisplayByFileNameConfigurationChanged(const bool& value) override; + /* * Component override */ @@ -144,6 +158,9 @@ class DetailedGameListView : public ISimpleGameListView //! Scraping state (for visual feedback) bool mIsScraping; + //! Cached value of display per filename + bool mIsDisplayedByFileName; + void initMDLabels(); void initMDValues(); diff --git a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp index 6180ef1063c3066824bc2ca9fc85d12ecc8c26b4..bfac5f6ad8c5d353aec17f7ca8caedef15b39890 100755 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -19,6 +19,7 @@ ISimpleGameListView::ISimpleGameListView(WindowManager& window, SystemManager& s , mHeaderImage(window) , mBackground(window) , mThemeExtras(window) + , mInvalidated(false) , mVerticalMove(false) , mListRefreshRequired(false) { @@ -347,7 +348,7 @@ void ISimpleGameListView::JumpToNextLetter(bool forward) void ISimpleGameListView::JumpToLetter(unsigned int unicode) { - for(int c = 0; c < (int)getCursorIndexMax(); ++c) + for(int c = 0; c <= (int)getCursorIndexMax(); ++c) if (getDataAt(c)->IsGame()) if (String::UpperUnicode(getDataAt(c)->Name().ReadFirstUTF8()) == unicode) // Change to dynamic naming ASAP { @@ -355,4 +356,3 @@ void ISimpleGameListView::JumpToLetter(unsigned int unicode) break; } } - diff --git a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h index 66aadd21ad0483dacfc0ac255b4b34e4ecefa63b..d404d6df1f28205da07ef371507df608e14d9ea4 100644 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h @@ -131,14 +131,27 @@ class ISimpleGameListView : public Gui virtual FileData::List getFileDataList() = 0; + /*! + * @brief Get undecorated (NO icons or decorations) display name in functions of options + * @param game Game to lookup name + * @return Name + */ + virtual String GetUndecoratedDisplayName(const FileData& game) = 0; + /*! * @brief Must be called right after the constructor */ - void DoInitialize() + void DoInitialize(ISimpleGameListView* sourceView) { Initialize(); SwitchToTheme(mSystem.Theme(), false, nullptr); - populateList(mSystem.MasterRoot()); + if (sourceView != nullptr) + { + mCursorStack = sourceView->mCursorStack; + populateList(*mCursorStack.top()); + setCursorIndex(sourceView->getCursorIndex()); + } + else populateList(mSystem.MasterRoot()); } /*! @@ -160,20 +173,28 @@ class ISimpleGameListView : public Gui virtual void UpdateSlowData(const SlowDataInformation& info) = 0; /*! - * @brief Refrest a list refresh + * @brief Invalidate list content. Gamelist must refresh itself */ - void ListRefreshRequired() { mListRefreshRequired = true; } + void Invalidate() { mInvalidated = true; } - //! Refresh list - void Update(int deltatime) override + /*! + * @brief Override update to enable list auto-refresh + * @param elasped + */ + void Update(int elapsed) override { - Gui::Update(deltatime); - (void)deltatime; - if (mListRefreshRequired) - refreshList(); + Gui::Update(elapsed); + if (mInvalidated) { refreshList(); Validate(); } } + /*! + * @brief Refrest a list refresh + */ + void ListRefreshRequired() { mListRefreshRequired = true; } + protected: + void Validate() { mInvalidated = false; } + /*! * @brief Called right after the constructor */ @@ -204,6 +225,8 @@ class ISimpleGameListView : public Gui std::stack mCursorStack; + bool mInvalidated; + /* * IThemeSwitchable */