From 7480205cef2c7510df40aa657d4baebb34de053d Mon Sep 17 00:00:00 2001 From: Bkg2k Date: Tue, 4 Jun 2024 22:05:15 +0200 Subject: [PATCH 1/3] fix(frontend): fix move to letter & l1/r1 when displaying not by name --- .../es-app/src/systems/arcade/ArcadeTupple.h | 4 +- .../src/views/gamelist/ArcadeGameListView.cpp | 140 +++++++++--------- .../src/views/gamelist/ArcadeGameListView.h | 44 +++--- .../views/gamelist/DetailedGameListView.cpp | 22 ++- .../src/views/gamelist/DetailedGameListView.h | 19 ++- .../src/views/gamelist/ISimpleGameListView.h | 7 + 6 files changed, 142 insertions(+), 94 deletions(-) diff --git a/projects/frontend/es-app/src/systems/arcade/ArcadeTupple.h b/projects/frontend/es-app/src/systems/arcade/ArcadeTupple.h index cd253fa867..bd697340dd 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/gamelist/ArcadeGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp index f62bfe986a..d000125bf5 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,14 @@ bool ArcadeGameListView::HasMatchingManufacturer(const HashSet& manufacture return false; } +void ArcadeGameListView::ArcadeUseDatabaseNamesConfigurationChanged(const bool& value) +{ + if (value != mIsUsingArcadeDatabaseNames) + { + mIsUsingArcadeDatabaseNames = value; + } +} + String ArcadeGameListView::getArcadeItemIcon(const ArcadeTupple& game) { String result; @@ -168,16 +177,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 +309,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 +328,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 +503,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 +520,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 +542,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 +563,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 +577,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 9645084c38..8667553610 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 7dcf1b7223..f89f24e71c 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,22 @@ 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; + } +} + +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) diff --git a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.h b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.h index f4cb4850e7..63087ae11a 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.h b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h index 66aadd21ad..d33b937b0a 100644 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h @@ -131,6 +131,13 @@ 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 */ -- GitLab From 5dfe67143e4cffeb8c4f0f92c4855dc5b51c54f4 Mon Sep 17 00:00:00 2001 From: Bkg2k Date: Tue, 4 Jun 2024 17:37:23 +0200 Subject: [PATCH 2/3] feat(frontend): rework list invalidation & fix memory leak --- .../src/guis/menus/vault/MenuArcade.cpp | 1 - .../guis/menus/vault/MenuUserInterface.cpp | 4 +-- .../es-app/src/views/ViewController.cpp | 29 ++++--------------- .../es-app/src/views/ViewController.h | 3 +- .../src/views/gamelist/ArcadeGameListView.cpp | 1 + .../views/gamelist/DetailedGameListView.cpp | 1 + .../views/gamelist/ISimpleGameListView.cpp | 1 + .../src/views/gamelist/ISimpleGameListView.h | 26 ++++++++++++----- 8 files changed, 30 insertions(+), 36 deletions(-) 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 dff9f375b1..e78d37d021 100644 --- a/projects/frontend/es-app/src/guis/menus/vault/MenuArcade.cpp +++ b/projects/frontend/es-app/src/guis/menus/vault/MenuArcade.cpp @@ -90,7 +90,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 f6306ab13f..253e12c056 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/views/ViewController.cpp b/projects/frontend/es-app/src/views/ViewController.cpp index 956fe4b382..25150169e3 100755 --- a/projects/frontend/es-app/src/views/ViewController.cpp +++ b/projects/frontend/es-app/src/views/ViewController.cpp @@ -261,18 +261,6 @@ 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(); @@ -717,7 +705,6 @@ ISimpleGameListView* ViewController::GetOrCreateGamelistView(SystemData* system, view->DoInitialize(); mGameListViews[system] = view; - mInvalidGameList[system] = false; if (reorder) ReorderGamelistViewBeforeMoving(system, Move::None); @@ -862,21 +849,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) diff --git a/projects/frontend/es-app/src/views/ViewController.h b/projects/frontend/es-app/src/views/ViewController.h index 3dc93e7630..38ba0c99a9 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. @@ -230,7 +230,6 @@ class ViewController : public StaticLifeCycleControler SplashView mSplashView; GameClipView mGameClipView; CrtCalibrationView mCrtView; - HashMap mInvalidGameList; ViewType mCurrentViewType; //!< Current view type ViewType mPreviousViewType; //!< Previous view type diff --git a/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp index d000125bf5..c5c1bc1c4d 100644 --- a/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/ArcadeGameListView.cpp @@ -137,6 +137,7 @@ void ArcadeGameListView::ArcadeUseDatabaseNamesConfigurationChanged(const bool& if (value != mIsUsingArcadeDatabaseNames) { mIsUsingArcadeDatabaseNames = value; + Invalidate(); } } diff --git a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp index f89f24e71c..cdf236f95c 100755 --- a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -955,6 +955,7 @@ void DetailedGameListView::DisplayByFileNameConfigurationChanged(const bool& val if (value != mIsDisplayedByFileName) { mIsDisplayedByFileName = value; + Invalidate(); } } diff --git a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp index 6180ef1063..75fce56b21 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) { diff --git a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h index d33b937b0a..88e4ea7c69 100644 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h @@ -167,20 +167,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 */ @@ -211,6 +219,8 @@ class ISimpleGameListView : public Gui std::stack mCursorStack; + bool mInvalidated; + /* * IThemeSwitchable */ -- GitLab From fb2098d06ce80a047a2adfeecfacd4c9de328b98 Mon Sep 17 00:00:00 2001 From: Bkg2k Date: Mon, 10 Jun 2024 08:34:16 +0200 Subject: [PATCH 3/3] feat(frontend): dynamic arcade view [wip] --- .../src/guis/menus/MenuGamelistOptions.cpp | 2 +- .../src/guis/menus/vault/MenuArcade.cpp | 7 +-- .../es-app/src/views/ViewController.cpp | 44 ++++++++++++++----- .../es-app/src/views/ViewController.h | 12 ++++- .../views/gamelist/DetailedGameListView.cpp | 5 +-- .../views/gamelist/ISimpleGameListView.cpp | 3 +- .../src/views/gamelist/ISimpleGameListView.h | 10 ++++- 7 files changed, 55 insertions(+), 28 deletions(-) diff --git a/projects/frontend/es-app/src/guis/menus/MenuGamelistOptions.cpp b/projects/frontend/es-app/src/guis/menus/MenuGamelistOptions.cpp index 627b4153b0..9506af5452 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 e78d37d021..eb4efd5da6 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(); diff --git a/projects/frontend/es-app/src/views/ViewController.cpp b/projects/frontend/es-app/src/views/ViewController.cpp index 25150169e3..fb7651de2b 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) @@ -264,7 +265,7 @@ void ViewController::goToGameList(SystemData* system) 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); @@ -690,19 +691,38 @@ void ViewController::LaunchAnimated(const EmulatorData& emulator) } } -ISimpleGameListView* ViewController::GetOrCreateGamelistView(SystemData* system, bool reorder) +void ViewController::ArcadeViewEnhancedConfigurationChanged(const bool& value) { - //if we already made one, return that one - auto exists = mGameListViews.find(system); - if(exists != mGameListViews.end()) - return exists->second; + 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 + // 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; @@ -907,7 +927,7 @@ void ViewController::ChangeView(ViewType newViewMode, SystemData* targetSystem) } case ViewType::GameList: { - mCurrentView = GetOrCreateGamelistView(targetSystem); + mCurrentView = GetOrCreateGamelistView(targetSystem, nullptr); mCurrentSystem = targetSystem; break; } @@ -947,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 38ba0c99a9..77619a22cb 100644 --- a/projects/frontend/es-app/src/views/ViewController.h +++ b/projects/frontend/es-app/src/views/ViewController.h @@ -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 @@ -234,7 +240,6 @@ class ViewController : public StaticLifeCycleControler ViewType mCurrentViewType; //!< Current view type ViewType mPreviousViewType; //!< Previous view type - Transform4x4f mCamera; float mFadeOpacity; bool mLockInput; @@ -264,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/DetailedGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp index cdf236f95c..d126d6d5d8 100755 --- a/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -1103,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/ISimpleGameListView.cpp b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp index 75fce56b21..bfac5f6ad8 100755 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -348,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 { @@ -356,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 88e4ea7c69..d404d6df1f 100644 --- a/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/projects/frontend/es-app/src/views/gamelist/ISimpleGameListView.h @@ -141,11 +141,17 @@ class ISimpleGameListView : public Gui /*! * @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()); } /*! -- GitLab