From 09ca16361cea8db047bf1cd9cbef676390dd1505 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 23 Sep 2021 22:30:25 -0700 Subject: [PATCH] UI: Asynchronously update size and dates on sort. This means items shift and jump into place, but it also means we don't hang even for large savedata collections. --- UI/SavedataScreen.cpp | 92 +++++++++++++++++++++++++++++-------------- UI/SavedataScreen.h | 10 +++-- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index be06271797..1fbab9ab6a 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -138,32 +138,49 @@ private: class SortedLinearLayout : public UI::LinearLayoutList { public: - typedef std::function CompareFunc; - typedef std::function DoneFunc; + typedef std::function PrepFunc; + typedef std::function CompareFunc; SortedLinearLayout(UI::Orientation orientation, UI::LayoutParams *layoutParams = nullptr) : UI::LinearLayoutList(orientation, layoutParams) { } - void SetCompare(CompareFunc lessFunc, DoneFunc doneFunc) { + void SetCompare(const PrepFunc &prepFunc, const CompareFunc &lessFunc) { + prepIndex_ = 0; + prepFunc_ = prepFunc; lessFunc_ = lessFunc; - doneFunc_ = doneFunc; } void Update() override; private: + size_t prepIndex_ = 0; + PrepFunc prepFunc_; CompareFunc lessFunc_; - DoneFunc doneFunc_; }; void SortedLinearLayout::Update() { + if (prepFunc_) { + // Try to avoid dropping more than a frame, prefer items shift. + constexpr double ALLOWED_TIME = 0.95 / 60.0; + double start_time = time_now_d(); + for (; prepIndex_ < views_.size(); ++prepIndex_) { + prepFunc_(views_[prepIndex_]); + if (time_now_d() > start_time + ALLOWED_TIME) { + break; + } + } + } if (lessFunc_) { + // We may sort several times while calculating. std::stable_sort(views_.begin(), views_.end(), lessFunc_); } - if (doneFunc_ && doneFunc_()) { + // We're done if we got through all items. + if (prepIndex_ >= views_.size()) { + prepFunc_ = PrepFunc(); lessFunc_ = CompareFunc(); } + UI::LinearLayout::Update(); } @@ -184,8 +201,15 @@ public: const Path &GamePath() const { return savePath_; } - uint64_t GetTotalSize(); - int64_t GetDateSeconds(); + uint64_t GetTotalSize() const { + return totalSize_; + } + int64_t GetDateSeconds() const { + return dateSeconds_; + } + + void UpdateTotalSize(); + void UpdateDateSeconds(); private: void UpdateText(const std::shared_ptr &ginfo); @@ -199,9 +223,9 @@ private: bool hasDateSeconds_ = false; }; -uint64_t SavedataButton::GetTotalSize() { +void SavedataButton::UpdateTotalSize() { if (hasTotalSize_) - return totalSize_; + return; File::FileInfo info; if (File::GetFileInfo(savePath_, &info)) { @@ -211,12 +235,11 @@ uint64_t SavedataButton::GetTotalSize() { } hasTotalSize_ = true; - return totalSize_; } -int64_t SavedataButton::GetDateSeconds() { +void SavedataButton::UpdateDateSeconds() { if (hasDateSeconds_) - return dateSeconds_; + return; File::FileInfo info; if (File::GetFileInfo(savePath_, &info)) { @@ -227,7 +250,6 @@ int64_t SavedataButton::GetDateSeconds() { } hasDateSeconds_ = true; - return dateSeconds_; } UI::EventReturn SavedataPopupScreen::OnDeleteButtonClick(UI::EventParams &e) { @@ -446,25 +468,34 @@ void SavedataBrowser::SetSortOption(SavedataSortOption opt) { if (gameList_) { SortedLinearLayout *gl = static_cast(gameList_); if (sortOption_ == SavedataSortOption::FILENAME) { - gl->SetCompare(&ByFilename, &SortDone); + gl->SetCompare(&PrepFilename, &ByFilename); } else if (sortOption_ == SavedataSortOption::SIZE) { - gl->SetCompare(&BySize, &SortDone); + gl->SetCompare(&PrepSize, &BySize); } else if (sortOption_ == SavedataSortOption::DATE) { - gl->SetCompare(&ByDate, &SortDone); + gl->SetCompare(&PrepDate, &ByDate); } } } -bool SavedataBrowser::ByFilename(UI::View *v1, UI::View *v2) { - SavedataButton *b1 = static_cast(v1); - SavedataButton *b2 = static_cast(v2); +void SavedataBrowser::PrepFilename(UI::View *v) { + // Nothing needed. +} + +bool SavedataBrowser::ByFilename(const UI::View *v1, const UI::View *v2) { + const SavedataButton *b1 = static_cast(v1); + const SavedataButton *b2 = static_cast(v2); return strcmp(b1->GamePath().c_str(), b2->GamePath().c_str()) < 0; } -bool SavedataBrowser::BySize(UI::View *v1, UI::View *v2) { - SavedataButton *b1 = static_cast(v1); - SavedataButton *b2 = static_cast(v2); +void SavedataBrowser::PrepSize(UI::View *v) { + SavedataButton *b = static_cast(v); + b->UpdateTotalSize(); +} + +bool SavedataBrowser::BySize(const UI::View *v1, const UI::View *v2) { + const SavedataButton *b1 = static_cast(v1); + const SavedataButton *b2 = static_cast(v2); const uint64_t size1 = b1->GetTotalSize(); const uint64_t size2 = b2->GetTotalSize(); @@ -475,9 +506,14 @@ bool SavedataBrowser::BySize(UI::View *v1, UI::View *v2) { return strcmp(b1->GamePath().c_str(), b2->GamePath().c_str()) < 0; } -bool SavedataBrowser::ByDate(UI::View *v1, UI::View *v2) { - SavedataButton *b1 = static_cast(v1); - SavedataButton *b2 = static_cast(v2); +void SavedataBrowser::PrepDate(UI::View *v) { + SavedataButton *b = static_cast(v); + b->UpdateDateSeconds(); +} + +bool SavedataBrowser::ByDate(const UI::View *v1, const UI::View *v2) { + const SavedataButton *b1 = static_cast(v1); + const SavedataButton *b2 = static_cast(v2); const int64_t time1 = b1->GetDateSeconds(); const int64_t time2 = b2->GetDateSeconds(); @@ -488,10 +524,6 @@ bool SavedataBrowser::ByDate(UI::View *v1, UI::View *v2) { return strcmp(b1->GamePath().c_str(), b2->GamePath().c_str()) < 0; } -bool SavedataBrowser::SortDone() { - return true; -} - void SavedataBrowser::Refresh() { using namespace UI; diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index 14ea48a960..1f9509e687 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -46,10 +46,12 @@ public: UI::Event OnChoice; private: - static bool ByFilename(UI::View *, UI::View *); - static bool BySize(UI::View *, UI::View *); - static bool ByDate(UI::View *, UI::View *); - static bool SortDone(); + static void PrepFilename(UI::View *); + static void PrepSize(UI::View *); + static void PrepDate(UI::View *); + static bool ByFilename(const UI::View *, const UI::View *); + static bool BySize(const UI::View *, const UI::View *); + static bool ByDate(const UI::View *, const UI::View *); void Refresh(); UI::EventReturn SavedataButtonClick(UI::EventParams &e);