mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-03-27 22:00:23 -06:00
GameList: Reduce time it takes for the list to be populated
This commit is contained in:
parent
f5be6bef2d
commit
556fc24896
@ -190,6 +190,7 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
|
||||
Bind(wxEVT_LIST_COL_END_DRAG, &wxGameList::OnColumnResize, this);
|
||||
Bind(wxEVT_LIST_COL_RIGHT_CLICK, &wxGameList::OnColumnRightClick, this);
|
||||
Bind(wxEVT_SIZE, &wxGameList::OnGameListSize, this);
|
||||
m_bulkUpdateTimer.Bind(wxEVT_TIMER, &wxGameList::OnTimerBulkAddEntriesToGameList, this);
|
||||
|
||||
m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxGameList*)ctx)->HandleTitleListCallback(evt); }, this);
|
||||
|
||||
@ -1079,112 +1080,142 @@ void wxGameList::OnClose(wxCloseEvent& event)
|
||||
m_exit = true;
|
||||
}
|
||||
|
||||
int wxGameList::FindInsertPosition(TitleId titleId)
|
||||
int wxGameList::FindInsertPosition(TitleId titleId, bool& entryAlreadyExists)
|
||||
{
|
||||
entryAlreadyExists = false;
|
||||
SortData data{this, ItemColumns(GetSortIndicator()), IsAscendingSortIndicator()};
|
||||
const auto itemCount = GetItemCount();
|
||||
|
||||
if (itemCount == 0)
|
||||
return 0;
|
||||
// todo - optimize this with binary search
|
||||
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
sint32 low = 0;
|
||||
sint32 high = itemCount;
|
||||
while (low < high)
|
||||
{
|
||||
if (SortComparator(titleId, (uint64)GetItemData(i), &data) <= 0)
|
||||
return i;
|
||||
sint32 mid = low + (high - low) / 2;
|
||||
auto cmp = SortComparator(titleId, (uint64)GetItemData(mid), &data);
|
||||
if (cmp <= 0)
|
||||
{
|
||||
if (cmp == 0)
|
||||
{
|
||||
entryAlreadyExists = true;
|
||||
return mid;
|
||||
}
|
||||
high = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return itemCount;
|
||||
return low;
|
||||
}
|
||||
|
||||
void wxGameList::OnTimerBulkAddEntriesToGameList(wxTimerEvent& event)
|
||||
{
|
||||
std::vector<TitleId> titleIdsToUpdate;
|
||||
std::swap(titleIdsToUpdate, m_bulkTitlesToAdd);
|
||||
|
||||
wxWindowUpdateLocker lock(this);
|
||||
bool hasAnyNewEntry = false;
|
||||
for (auto& titleId : titleIdsToUpdate)
|
||||
{
|
||||
GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId);
|
||||
if (!gameInfo.IsValid() || gameInfo.IsSystemDataTitle())
|
||||
{
|
||||
// entry no longer exists or is not a valid game
|
||||
// we dont need to remove list entries here because all delete operations should trigger a full list refresh
|
||||
continue;
|
||||
}
|
||||
TitleId baseTitleId = gameInfo.GetBaseTitleId();
|
||||
bool isNewEntry = false;
|
||||
|
||||
int icon = -1; /* 0 is the default empty icon */
|
||||
int icon_small = -1; /* 0 is the default empty icon */
|
||||
QueryIconForTitle(baseTitleId, icon, icon_small);
|
||||
|
||||
bool entryAlreadyExists = false;
|
||||
auto index = FindInsertPosition(baseTitleId, entryAlreadyExists);
|
||||
if(!entryAlreadyExists)
|
||||
{
|
||||
// entry doesn't exist
|
||||
index = InsertItem(index, wxString::FromUTF8(GetNameByTitleId(baseTitleId)));
|
||||
SetItemPtrData(index, baseTitleId);
|
||||
isNewEntry = true;
|
||||
hasAnyNewEntry = true;
|
||||
}
|
||||
|
||||
if (m_style == Style::kList)
|
||||
{
|
||||
SetItemColumnImage(index, ColumnIcon, icon_small);
|
||||
|
||||
SetItem(index, ColumnName, wxString::FromUTF8(GetNameByTitleId(baseTitleId)));
|
||||
|
||||
SetItem(index, ColumnVersion, fmt::format("{}", gameInfo.GetVersion()));
|
||||
|
||||
if(gameInfo.HasAOC())
|
||||
SetItem(index, ColumnDLC, fmt::format("{}", gameInfo.GetAOCVersion()));
|
||||
else
|
||||
SetItem(index, ColumnDLC, wxString());
|
||||
|
||||
if (isNewEntry)
|
||||
{
|
||||
iosu::pdm::GameListStat playTimeStat;
|
||||
if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat))
|
||||
{
|
||||
// time played
|
||||
uint32 minutesPlayed = playTimeStat.numMinutesPlayed;
|
||||
if (minutesPlayed == 0)
|
||||
SetItem(index, ColumnGameTime, wxEmptyString);
|
||||
else if (minutesPlayed < 60)
|
||||
SetItem(index, ColumnGameTime, formatWxString(wxPLURAL("{} minute", "{} minutes", minutesPlayed), minutesPlayed));
|
||||
else
|
||||
{
|
||||
uint32 hours = minutesPlayed / 60;
|
||||
uint32 minutes = minutesPlayed % 60;
|
||||
wxString hoursText = formatWxString(wxPLURAL("{} hour", "{} hours", hours), hours);
|
||||
wxString minutesText = formatWxString(wxPLURAL("{} minute", "{} minutes", minutes), minutes);
|
||||
SetItem(index, ColumnGameTime, hoursText + " " + minutesText);
|
||||
}
|
||||
|
||||
// last played
|
||||
if (playTimeStat.last_played.year != 0)
|
||||
{
|
||||
const wxDateTime tmp((wxDateTime::wxDateTime_t)playTimeStat.last_played.day, (wxDateTime::Month)playTimeStat.last_played.month, (wxDateTime::wxDateTime_t)playTimeStat.last_played.year, 0, 0, 0, 0);
|
||||
SetItem(index, ColumnGameStarted, tmp.FormatDate());
|
||||
}
|
||||
else
|
||||
SetItem(index, ColumnGameStarted, _("never"));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetItem(index, ColumnGameTime, wxEmptyString);
|
||||
SetItem(index, ColumnGameStarted, _("never"));
|
||||
}
|
||||
}
|
||||
const auto region_text = fmt::format("{}", gameInfo.GetRegion());
|
||||
SetItem(index, ColumnRegion, wxGetTranslation(region_text));
|
||||
SetItem(index, ColumnTitleID, fmt::format("{:016x}", baseTitleId));
|
||||
}
|
||||
else if (m_style == Style::kIcons)
|
||||
{
|
||||
SetItemImage(index, icon);
|
||||
}
|
||||
else if (m_style == Style::kSmallIcons)
|
||||
{
|
||||
SetItemImage(index, icon_small);
|
||||
}
|
||||
}
|
||||
if (hasAnyNewEntry)
|
||||
UpdateItemColors();
|
||||
}
|
||||
|
||||
void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event)
|
||||
{
|
||||
if (m_bulkTitlesToAdd.size() < 100)
|
||||
m_bulkUpdateTimer.StartOnce(100); // if timer is started already this will delay it
|
||||
const auto titleId = event.GetTitleId();
|
||||
GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId);
|
||||
if (!gameInfo.IsValid() || gameInfo.IsSystemDataTitle())
|
||||
{
|
||||
// entry no longer exists or is not a valid game
|
||||
// we dont need to remove list entries here because all delete operations should trigger a full list refresh
|
||||
return;
|
||||
}
|
||||
TitleId baseTitleId = gameInfo.GetBaseTitleId();
|
||||
bool isNewEntry = false;
|
||||
|
||||
int icon = -1; /* 0 is the default empty icon */
|
||||
int icon_small = -1; /* 0 is the default empty icon */
|
||||
QueryIconForTitle(baseTitleId, icon, icon_small);
|
||||
|
||||
auto index = FindListItemByTitleId(baseTitleId);
|
||||
if(index == wxNOT_FOUND)
|
||||
{
|
||||
// entry doesn't exist
|
||||
index = InsertItem(FindInsertPosition(baseTitleId), wxString::FromUTF8(GetNameByTitleId(baseTitleId)));
|
||||
SetItemPtrData(index, baseTitleId);
|
||||
isNewEntry = true;
|
||||
}
|
||||
|
||||
if (m_style == Style::kList)
|
||||
{
|
||||
SetItemColumnImage(index, ColumnIcon, icon_small);
|
||||
|
||||
SetItem(index, ColumnName, wxString::FromUTF8(GetNameByTitleId(baseTitleId)));
|
||||
|
||||
SetItem(index, ColumnVersion, fmt::format("{}", gameInfo.GetVersion()));
|
||||
|
||||
if(gameInfo.HasAOC())
|
||||
SetItem(index, ColumnDLC, fmt::format("{}", gameInfo.GetAOCVersion()));
|
||||
else
|
||||
SetItem(index, ColumnDLC, wxString());
|
||||
|
||||
if (isNewEntry)
|
||||
{
|
||||
iosu::pdm::GameListStat playTimeStat;
|
||||
if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat))
|
||||
{
|
||||
// time played
|
||||
uint32 minutesPlayed = playTimeStat.numMinutesPlayed;
|
||||
if (minutesPlayed == 0)
|
||||
SetItem(index, ColumnGameTime, wxEmptyString);
|
||||
else if (minutesPlayed < 60)
|
||||
SetItem(index, ColumnGameTime, formatWxString(wxPLURAL("{} minute", "{} minutes", minutesPlayed), minutesPlayed));
|
||||
else
|
||||
{
|
||||
uint32 hours = minutesPlayed / 60;
|
||||
uint32 minutes = minutesPlayed % 60;
|
||||
wxString hoursText = formatWxString(wxPLURAL("{} hour", "{} hours", hours), hours);
|
||||
wxString minutesText = formatWxString(wxPLURAL("{} minute", "{} minutes", minutes), minutes);
|
||||
SetItem(index, ColumnGameTime, hoursText + " " + minutesText);
|
||||
}
|
||||
|
||||
// last played
|
||||
if (playTimeStat.last_played.year != 0)
|
||||
{
|
||||
const wxDateTime tmp((wxDateTime::wxDateTime_t)playTimeStat.last_played.day, (wxDateTime::Month)playTimeStat.last_played.month, (wxDateTime::wxDateTime_t)playTimeStat.last_played.year, 0, 0, 0, 0);
|
||||
SetItem(index, ColumnGameStarted, tmp.FormatDate());
|
||||
}
|
||||
else
|
||||
SetItem(index, ColumnGameStarted, _("never"));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetItem(index, ColumnGameTime, wxEmptyString);
|
||||
SetItem(index, ColumnGameStarted, _("never"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const auto region_text = fmt::format("{}", gameInfo.GetRegion());
|
||||
SetItem(index, ColumnRegion, wxGetTranslation(region_text));
|
||||
SetItem(index, ColumnTitleID, fmt::format("{:016x}", baseTitleId));
|
||||
}
|
||||
else if (m_style == Style::kIcons)
|
||||
{
|
||||
SetItemImage(index, icon);
|
||||
}
|
||||
else if (m_style == Style::kSmallIcons)
|
||||
{
|
||||
SetItemImage(index, icon_small);
|
||||
}
|
||||
if (isNewEntry)
|
||||
UpdateItemColors(index);
|
||||
m_bulkTitlesToAdd.emplace_back(titleId);
|
||||
}
|
||||
|
||||
void wxGameList::OnItemActivated(wxListEvent& event)
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
#include "config/CemuConfig.h"
|
||||
#include "Cafe/TitleList/TitleId.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/panel.h>
|
||||
@ -57,6 +55,7 @@ public:
|
||||
void CreateShortcut(GameInfo2& gameInfo);
|
||||
|
||||
long FindListItemByTitleId(uint64 title_id) const;
|
||||
|
||||
void OnClose(wxCloseEvent& event);
|
||||
|
||||
private:
|
||||
@ -93,7 +92,7 @@ private:
|
||||
int dir;
|
||||
};
|
||||
|
||||
int FindInsertPosition(TitleId titleId);
|
||||
int FindInsertPosition(TitleId titleId, bool& entryAlreadyExists);
|
||||
std::weak_ordering SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData);
|
||||
static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData);
|
||||
|
||||
@ -132,6 +131,10 @@ private:
|
||||
|
||||
std::map<TitleId, std::string> m_name_cache;
|
||||
|
||||
// bulk update handling
|
||||
std::vector<TitleId> m_bulkTitlesToAdd;
|
||||
wxTimer m_bulkUpdateTimer;
|
||||
|
||||
// list mode
|
||||
void CreateListColumns();
|
||||
|
||||
@ -156,6 +159,8 @@ private:
|
||||
void AdjustLastColumnWidth();
|
||||
int GetColumnDefaultWidth(int column);
|
||||
|
||||
void OnTimerBulkAddEntriesToGameList(wxTimerEvent& event);
|
||||
|
||||
static inline std::once_flag s_launch_file_once;
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user