Qt: Simplify cover and icon invalidation

Get rid of multiple functions for doing the same thing, sync Big Picture
and Qt UIs.
pull/3662/head
Stenzek 9 months ago
parent c9177bc398
commit 9eaf58613a
No known key found for this signature in database

@ -30,7 +30,7 @@ void OnSystemDestroyed();
void Shutdown(bool clear_state); void Shutdown(bool clear_state);
void Render(); void Render();
void InvalidateCoverCache(); void InvalidateCoverCache(std::string path = {});
float GetBackgroundAlpha(); float GetBackgroundAlpha();

@ -1140,17 +1140,20 @@ void FullscreenUI::SetCoverCacheEntry(std::string path, std::string cover_path)
s_game_list_locals.cover_image_map.emplace(std::move(path), std::move(cover_path)); s_game_list_locals.cover_image_map.emplace(std::move(path), std::move(cover_path));
} }
void FullscreenUI::ClearCoverCache() void FullscreenUI::RemoveCoverCacheEntry(const std::string& path)
{ {
s_game_list_locals.cover_image_map.clear(); if (path.empty())
s_game_list_locals.cover_image_map.clear();
else
s_game_list_locals.cover_image_map.erase(path);
} }
void FullscreenUI::InvalidateCoverCache() void FullscreenUI::InvalidateCoverCache(std::string path)
{ {
if (!GPUThread::IsFullscreenUIRequested()) if (!GPUThread::IsFullscreenUIRequested())
return; return;
GPUThread::RunOnThread(&FullscreenUI::ClearCoverCache); GPUThread::RunOnThread([path = std::move(path)]() { RemoveCoverCacheEntry(path); });
} }
void FullscreenUI::DrawGameListCover(const GameList::Entry* entry, bool fallback_to_achievements_icon, void FullscreenUI::DrawGameListCover(const GameList::Entry* entry, bool fallback_to_achievements_icon,

@ -101,7 +101,7 @@ void DoStartPath(std::string path, std::string state = std::string(), std::optio
GPUTexture* GetCoverForCurrentGame(const std::string& game_path); GPUTexture* GetCoverForCurrentGame(const std::string& game_path);
void SetCoverCacheEntry(std::string path, std::string cover_path); void SetCoverCacheEntry(std::string path, std::string cover_path);
void ClearCoverCache(); void RemoveCoverCacheEntry(const std::string& path);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Settings // Settings

@ -1467,7 +1467,8 @@ void FullscreenUI::DrawFolderSetting(SettingsInterface* bsi, std::string_view ti
SetSettingsChanged(bsi); SetSettingsChanged(bsi);
Host::RunOnCPUThread(&EmuFolders::Update); Host::RunOnCPUThread(&EmuFolders::Update);
ClearCoverCache(); if (key == "Covers")
RemoveCoverCacheEntry({});
}); });
} }
} }

@ -2,6 +2,7 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "coverdownloadwindow.h" #include "coverdownloadwindow.h"
#include "gamelistwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "qthost.h" #include "qthost.h"
#include "qtprogresscallback.h" #include "qtprogresscallback.h"
@ -52,7 +53,9 @@ void CoverDownloadWindow::onStartClicked()
Error error; Error error;
const bool result = GameList::DownloadCovers( const bool result = GameList::DownloadCovers(
urls, use_serials, progress, &error, [](const GameList::Entry* entry, std::string) mutable { urls, use_serials, progress, &error, [](const GameList::Entry* entry, std::string) mutable {
Host::RunOnUIThread([path = entry->path]() { g_main_window->invalidateCoverCacheForPath(path); }); Host::RunOnUIThread([path = entry->path]() {
g_main_window->getGameListWidget()->getModel()->invalidateColumnForPath(path, GameListModel::Column_Cover);
});
}); });
return [this, result, error = std::move(error)]() { downloadComplete(result, error); }; return [this, result, error = std::move(error)]() { downloadComplete(result, error); };
}); });
@ -66,7 +69,7 @@ void CoverDownloadWindow::onStartClicked()
void CoverDownloadWindow::downloadComplete(bool result, const Error& error) void CoverDownloadWindow::downloadComplete(bool result, const Error& error)
{ {
g_main_window->refreshGameGridCovers(); g_main_window->getGameListWidget()->getModel()->invalidateColumn(GameListModel::Column_Cover);
m_ui.status->setText(tr("Download complete.")); m_ui.status->setText(tr("Download complete."));
if (!result) if (!result)

@ -252,12 +252,7 @@ bool GameListModel::getShowLocalizedTitles() const
void GameListModel::setShowLocalizedTitles(bool enabled) void GameListModel::setShowLocalizedTitles(bool enabled)
{ {
m_show_localized_titles = enabled; m_show_localized_titles = enabled;
invalidateColumn(Column_Title);
emit dataChanged(index(0, Column_Title), index(rowCount() - 1, Column_Title), {Qt::DisplayRole, Qt::ToolTipRole});
if (m_show_titles_for_covers)
emit dataChanged(index(0, Column_Cover), index(rowCount() - 1, Column_Cover), {Qt::DisplayRole});
// emit cover changed as well since the autogenerated covers will differ
refreshCovers();
} }
bool GameListModel::getShowCoverTitles() const bool GameListModel::getShowCoverTitles() const
@ -303,13 +298,7 @@ void GameListModel::setIconSize(int size)
emit headerDataChanged(Qt::Vertical, 0, rowCount() - 1); emit headerDataChanged(Qt::Vertical, 0, rowCount() - 1);
loadSizeDependentPixmaps(); loadSizeDependentPixmaps();
refreshIcons(); invalidateColumn(Column_Icon);
}
void GameListModel::refreshIcons()
{
m_icon_pixmap_cache.Clear();
emit dataChanged(index(0, Column_Icon), index(rowCount() - 1, Column_Icon), {Qt::DecorationRole});
} }
float GameListModel::getCoverScale() const float GameListModel::getCoverScale() const
@ -372,12 +361,6 @@ void GameListModel::updateCoverScale()
{Qt::DecorationRole, Qt::FontRole, Qt::SizeHintRole}); {Qt::DecorationRole, Qt::FontRole, Qt::SizeHintRole});
} }
void GameListModel::refreshCovers()
{
m_cover_pixmap_cache.Clear();
emit dataChanged(index(0, Column_Cover), index(rowCount() - 1, Column_Cover), {Qt::DecorationRole});
}
void GameListModel::updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model, void GameListModel::updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model,
int top_left_row) int top_left_row)
{ {
@ -422,8 +405,8 @@ void GameListModel::setDevicePixelRatio(qreal dpr)
m_flag_pixmap_cache.clear(); m_flag_pixmap_cache.clear();
loadCommonImages(); loadCommonImages();
loadCoverScaleDependentPixmaps(); loadCoverScaleDependentPixmaps();
refreshCovers(); invalidateColumn(Column_Icon);
refreshIcons(); invalidateColumn(Column_Cover);
} }
void GameListModel::reloadThemeSpecificImages() void GameListModel::reloadThemeSpecificImages()
@ -510,7 +493,7 @@ void GameListModel::coverLoaded(const std::string& path, const QImage& image, fl
pmp->scale = scale; pmp->scale = scale;
pmp->is_loading = false; pmp->is_loading = false;
invalidateCoverForPath(path); invalidateColumnForPath(path, Column_Cover, false);
} }
void GameListModel::rowsChanged(const QList<int>& rows) void GameListModel::rowsChanged(const QList<int>& rows)
@ -537,17 +520,66 @@ void GameListModel::rowsChanged(const QList<int>& rows)
} }
} }
void GameListModel::invalidateCoverForPath(const std::string& path) Qt::ItemDataRole GameListModel::getRoleToInvalidate(int column)
{
if (column == Column_Icon || column == Column_Cover || column == Column_Region)
return Qt::DecorationRole;
else
return Qt::DisplayRole;
}
void GameListModel::invalidateColumn(int column, bool invalidate_cache /* = true */)
{
if (invalidate_cache)
{
if (column == Column_Icon)
{
m_icon_pixmap_cache.Clear();
}
else if (column == Column_Cover)
{
m_cover_pixmap_cache.Clear();
if (QtHost::IsFullscreenUIStarted())
Host::RunOnCPUThread([]() { FullscreenUI::InvalidateCoverCache(); });
}
}
emit dataChanged(index(0, column), index(rowCount() - 1, column), {getRoleToInvalidate(column)});
}
void GameListModel::invalidateColumnForPath(const std::string& path, int column, bool invalidate_cache /* = true */)
{ {
std::optional<u32> row; // if we're changing the title, the cover could change
if (column == Column_Title)
invalidateColumnForPath(path, Column_Cover, invalidate_cache);
if (invalidate_cache)
{
if (column == Column_Icon)
{
m_icon_pixmap_cache.Remove(path);
}
else if (column == Column_Cover)
{
m_cover_pixmap_cache.Remove(path);
if (QtHost::IsFullscreenUIStarted())
Host::RunOnCPUThread([path]() mutable { FullscreenUI::InvalidateCoverCache(std::move(path)); });
}
}
const auto remove_entry = [this, &column](const GameList::Entry* ge, int row) {
const QModelIndex mi(index(row, column));
emit dataChanged(mi, mi, {getRoleToInvalidate(column)});
};
if (hasTakenGameList()) if (hasTakenGameList())
{ {
for (u32 i = 0; i < static_cast<u32>(m_taken_entries->size()); i++) for (u32 i = 0; i < static_cast<u32>(m_taken_entries->size()); i++)
{ {
if (path == m_taken_entries.value()[i].path) if (path == m_taken_entries.value()[i].path)
{ {
row = i; remove_entry(&m_taken_entries.value()[i], static_cast<int>(i));
break; return;
} }
} }
} }
@ -558,28 +590,14 @@ void GameListModel::invalidateCoverForPath(const std::string& path)
const size_t count = GameList::GetEntryCount(); const size_t count = GameList::GetEntryCount();
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
{ {
if (GameList::GetEntryByIndex(i)->path == path) const GameList::Entry* const entry = GameList::GetEntryByIndex(static_cast<u32>(i));
if (entry->path == path)
{ {
row = static_cast<int>(i); remove_entry(entry, static_cast<int>(i));
break; return;
} }
} }
} }
if (!row.has_value())
{
// Game removed?
return;
}
const QModelIndex mi(index(static_cast<int>(row.value()), Column_Cover));
emit dataChanged(mi, mi, {Qt::DecorationRole});
}
void GameListModel::invalidateCoverCacheForPath(const std::string& path)
{
m_cover_pixmap_cache.Remove(path);
invalidateCoverForPath(path);
} }
const QPixmap& GameListModel::getCoverForEntry(const GameList::Entry* ge) const const QPixmap& GameListModel::getCoverForEntry(const GameList::Entry* ge) const
@ -672,7 +690,8 @@ void GameListModel::setShowGameIcons(bool enabled)
if (enabled) if (enabled)
GameList::ReloadMemcardTimestampCache(); GameList::ReloadMemcardTimestampCache();
refreshIcons();
invalidateColumn(Column_Icon);
} }
QIcon GameListModel::getIconForGame(const QString& path) QIcon GameListModel::getIconForGame(const QString& path)
@ -2053,12 +2072,6 @@ void GameListWidget::onGridViewContextMenuRequested(const QPoint& point)
emit entryContextMenuRequested(m_grid_view->mapToGlobal(point)); emit entryContextMenuRequested(m_grid_view->mapToGlobal(point));
} }
void GameListWidget::refreshGridCovers()
{
m_model->refreshCovers();
Host::RunOnCPUThread(&FullscreenUI::InvalidateCoverCache);
}
void GameListWidget::focusSearchWidget() void GameListWidget::focusSearchWidget()
{ {
m_ui.searchText->setFocus(Qt::ShortcutFocusReason); m_ui.searchText->setFocus(Qt::ShortcutFocusReason);
@ -2110,7 +2123,6 @@ void GameListWidget::setMergeDiscSets(bool enabled)
Host::SetBaseBoolSettingValue("UI", "GameListMergeDiscSets", enabled); Host::SetBaseBoolSettingValue("UI", "GameListMergeDiscSets", enabled);
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();
m_sort_model->setMergeDiscSets(enabled); m_sort_model->setMergeDiscSets(enabled);
m_model->refreshIcons();
} }
void GameListWidget::setShowLocalizedTitles(bool enabled) void GameListWidget::setShowLocalizedTitles(bool enabled)
@ -2160,7 +2172,7 @@ void GameListWidget::setPreferAchievementGameIcons(bool enabled)
if (!enabled) if (!enabled)
GameList::ReloadMemcardTimestampCache(); GameList::ReloadMemcardTimestampCache();
m_model->refreshIcons(); m_model->invalidateColumn(GameListModel::Column_Icon);
} }
void GameListWidget::setShowCoverTitles(bool enabled) void GameListWidget::setShowCoverTitles(bool enabled)

@ -102,7 +102,6 @@ public:
int getIconSize() const; int getIconSize() const;
int getIconSizeWithPadding() const; int getIconSizeWithPadding() const;
void refreshIcons();
void setIconSize(int size); void setIconSize(int size);
bool getShowGameIcons() const; bool getShowGameIcons() const;
@ -116,7 +115,6 @@ public:
QSize getDeviceScaledCoverArtSize() const; QSize getDeviceScaledCoverArtSize() const;
int getCoverArtSpacing() const; int getCoverArtSpacing() const;
QFont getCoverCaptionFont() const; QFont getCoverCaptionFont() const;
void refreshCovers();
void updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model, int top_left_row); void updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model, int top_left_row);
qreal getDevicePixelRatio() const; qreal getDevicePixelRatio() const;
@ -125,7 +123,9 @@ public:
const QPixmap* lookupIconPixmapForEntry(const GameList::Entry* ge) const; const QPixmap* lookupIconPixmapForEntry(const GameList::Entry* ge) const;
const QPixmap& getCoverForEntry(const GameList::Entry* ge) const; const QPixmap& getCoverForEntry(const GameList::Entry* ge) const;
void invalidateCoverCacheForPath(const std::string& path);
void invalidateColumn(int column, bool invalidate_cache = true);
void invalidateColumnForPath(const std::string& path, int column, bool invalidate_cache = true);
Q_SIGNALS: Q_SIGNALS:
void coverScaleChanged(float scale); void coverScaleChanged(float scale);
@ -146,7 +146,6 @@ private:
void updateCoverScale(); void updateCoverScale();
void loadCoverScaleDependentPixmaps(); void loadCoverScaleDependentPixmaps();
void loadOrGenerateCover(const GameList::Entry* ge); void loadOrGenerateCover(const GameList::Entry* ge);
void invalidateCoverForPath(const std::string& path);
void coverLoaded(const std::string& path, const QImage& image, float scale); void coverLoaded(const std::string& path, const QImage& image, float scale);
static void loadOrGenerateCover(QImage& image, const QImage& placeholder_image, const QSize& size, float scale, static void loadOrGenerateCover(QImage& image, const QImage& placeholder_image, const QSize& size, float scale,
@ -155,6 +154,8 @@ private:
static void createPlaceholderImage(QImage& image, const QImage& placeholder_image, const QSize& size, float scale, static void createPlaceholderImage(QImage& image, const QImage& placeholder_image, const QSize& size, float scale,
const QString& title); const QString& title);
static Qt::ItemDataRole getRoleToInvalidate(int column);
const QPixmap& getIconPixmapForEntry(const GameList::Entry* ge) const; const QPixmap& getIconPixmapForEntry(const GameList::Entry* ge) const;
const QPixmap& getFlagPixmapForEntry(const GameList::Entry* ge) const; const QPixmap& getFlagPixmapForEntry(const GameList::Entry* ge) const;
@ -284,7 +285,6 @@ public:
void setAnimateGameIcons(bool enabled); void setAnimateGameIcons(bool enabled);
void setPreferAchievementGameIcons(bool enabled); void setPreferAchievementGameIcons(bool enabled);
void setShowCoverTitles(bool enabled); void setShowCoverTitles(bool enabled);
void refreshGridCovers();
void focusSearchWidget(); void focusSearchWidget();
Q_SIGNALS: Q_SIGNALS:

@ -2,6 +2,7 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "gamesummarywidget.h" #include "gamesummarywidget.h"
#include "gamelistwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "qthost.h" #include "qthost.h"
#include "qtprogresscallback.h" #include "qtprogresscallback.h"
@ -270,7 +271,7 @@ void GameSummaryWidget::setCustomTitle(const std::string& text)
} }
} }
g_main_window->refreshGameListModel(); g_main_window->getGameListWidget()->getModel()->invalidateColumnForPath(m_path, GameListModel::Column_Title);
} }
void GameSummaryWidget::setCustomRegion(int region) void GameSummaryWidget::setCustomRegion(int region)
@ -289,7 +290,7 @@ void GameSummaryWidget::setCustomRegion(int region)
} }
} }
g_main_window->refreshGameListModel(); g_main_window->getGameListWidget()->getModel()->invalidateColumnForPath(m_path, GameListModel::Column_Region);
} }
void GameSummaryWidget::onCustomLanguageChanged(int language) void GameSummaryWidget::onCustomLanguageChanged(int language)
@ -298,7 +299,7 @@ void GameSummaryWidget::onCustomLanguageChanged(int language)
m_path, (language > 0) ? std::optional<GameDatabase::Language>(static_cast<GameDatabase::Language>(language - 1)) : m_path, (language > 0) ? std::optional<GameDatabase::Language>(static_cast<GameDatabase::Language>(language - 1)) :
std::optional<GameDatabase::Language>()); std::optional<GameDatabase::Language>());
g_main_window->refreshGameListModel(); g_main_window->getGameListWidget()->getModel()->invalidateColumnForPath(m_path, GameListModel::Column_Region);
} }
static QString MSFToString(const CDImage::Position& position) static QString MSFToString(const CDImage::Position& position)

@ -1753,7 +1753,8 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
tr("Failed to remove '%1'").arg(old_filename)); tr("Failed to remove '%1'").arg(old_filename));
return; return;
} }
m_game_list_widget->refreshGridCovers();
m_game_list_widget->getModel()->invalidateColumnForPath(entry->path, GameListModel::Column_Cover);
} }
void MainWindow::clearGameListEntryPlayTime(const GameList::Entry* entry) void MainWindow::clearGameListEntryPlayTime(const GameList::Entry* entry)
@ -2501,8 +2502,8 @@ void MainWindow::connectSignals()
connect(m_ui.actionGridViewShowTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowCoverTitles); connect(m_ui.actionGridViewShowTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowCoverTitles);
connect(m_ui.actionViewZoomIn, &QAction::triggered, this, &MainWindow::onViewZoomInActionTriggered); connect(m_ui.actionViewZoomIn, &QAction::triggered, this, &MainWindow::onViewZoomInActionTriggered);
connect(m_ui.actionViewZoomOut, &QAction::triggered, this, &MainWindow::onViewZoomOutActionTriggered); connect(m_ui.actionViewZoomOut, &QAction::triggered, this, &MainWindow::onViewZoomOutActionTriggered);
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget, connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, this,
&GameListWidget::refreshGridCovers); [this]() { m_game_list_widget->getModel()->invalidateColumn(GameListModel::Column_Cover); });
connect(m_ui.actionChangeGameListBackground, &QAction::triggered, this, connect(m_ui.actionChangeGameListBackground, &QAction::triggered, this,
&MainWindow::onViewChangeGameListBackgroundTriggered); &MainWindow::onViewChangeGameListBackgroundTriggered);
connect(m_ui.actionClearGameListBackground, &QAction::triggered, this, connect(m_ui.actionClearGameListBackground, &QAction::triggered, this,
@ -2944,11 +2945,6 @@ void MainWindow::refreshGameList(bool invalidate_cache)
m_game_list_widget->refresh(invalidate_cache); m_game_list_widget->refresh(invalidate_cache);
} }
void MainWindow::refreshGameListModel()
{
m_game_list_widget->getModel()->refresh();
}
void MainWindow::cancelGameListRefresh() void MainWindow::cancelGameListRefresh()
{ {
m_game_list_widget->cancelRefresh(); m_game_list_widget->cancelRefresh();
@ -2959,16 +2955,6 @@ QIcon MainWindow::getIconForGame(const QString& path)
return m_game_list_widget->getModel()->getIconForGame(path); return m_game_list_widget->getModel()->getIconForGame(path);
} }
void MainWindow::invalidateCoverCacheForPath(const std::string& path)
{
m_game_list_widget->getModel()->invalidateCoverCacheForPath(path);
}
void MainWindow::refreshGameGridCovers()
{
m_game_list_widget->getModel()->refreshCovers();
}
void MainWindow::runOnUIThread(const std::function<void()>& func) void MainWindow::runOnUIThread(const std::function<void()>& func)
{ {
func(); func();
@ -3293,11 +3279,9 @@ void MainWindow::onToolsDownloadAchievementGameIconsTriggered()
const bool result = Achievements::DownloadGameIcons(progress, &error); const bool result = Achievements::DownloadGameIcons(progress, &error);
return [error = std::move(error), result]() { return [error = std::move(error), result]() {
if (!result) if (!result)
{
g_main_window->reportError(tr("Error"), QString::fromStdString(error.GetDescription())); g_main_window->reportError(tr("Error"), QString::fromStdString(error.GetDescription()));
}
g_main_window->refreshGameListModel(); g_main_window->m_game_list_widget->getModel()->invalidateColumn(GameListModel::Column_Icon);
}; };
}); });
} }
@ -3311,12 +3295,10 @@ void MainWindow::refreshAchievementProgress()
const bool result = Achievements::RefreshAllProgressDatabase(progress, &error); const bool result = Achievements::RefreshAllProgressDatabase(progress, &error);
return [error = std::move(error), result]() { return [error = std::move(error), result]() {
if (!result) if (!result)
{
g_main_window->reportError(tr("Error"), QString::fromStdString(error.GetDescription())); g_main_window->reportError(tr("Error"), QString::fromStdString(error.GetDescription()));
}
g_main_window->m_ui.statusBar->showMessage(tr("RA: Updated achievement progress database.")); g_main_window->m_ui.statusBar->showMessage(tr("RA: Updated achievement progress database."));
g_main_window->refreshGameListModel(); g_main_window->m_game_list_widget->getModel()->invalidateColumn(GameListModel::Column_Achievements);
}; };
}); });
} }

@ -106,6 +106,7 @@ public:
ALWAYS_INLINE QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; } ALWAYS_INLINE QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; }
ALWAYS_INLINE QLabel* getStatusFPSWidget() const { return m_status_fps_widget; } ALWAYS_INLINE QLabel* getStatusFPSWidget() const { return m_status_fps_widget; }
ALWAYS_INLINE QLabel* getStatusVPSWidget() const { return m_status_vps_widget; } ALWAYS_INLINE QLabel* getStatusVPSWidget() const { return m_status_vps_widget; }
ALWAYS_INLINE GameListWidget* getGameListWidget() const { return m_game_list_widget; }
ALWAYS_INLINE DebuggerWindow* getDebuggerWindow() const { return m_debugger_window; } ALWAYS_INLINE DebuggerWindow* getDebuggerWindow() const { return m_debugger_window; }
/// Opens the editor for a specific input profile. /// Opens the editor for a specific input profile.
@ -120,11 +121,8 @@ public:
void updateDebugMenuVisibility(); void updateDebugMenuVisibility();
void refreshGameList(bool invalidate_cache); void refreshGameList(bool invalidate_cache);
void refreshGameListModel();
void cancelGameListRefresh(); void cancelGameListRefresh();
QIcon getIconForGame(const QString& path); QIcon getIconForGame(const QString& path);
void invalidateCoverCacheForPath(const std::string& path);
void refreshGameGridCovers();
void refreshAchievementProgress(); void refreshAchievementProgress();
void runOnUIThread(const std::function<void()>& func); void runOnUIThread(const std::function<void()>& func);

Loading…
Cancel
Save