mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-16 19:34:38 +00:00
Qt: Simplify cover and icon invalidation
Get rid of multiple functions for doing the same thing, sync Big Picture and Qt UIs.
This commit is contained in:
@@ -30,7 +30,7 @@ void OnSystemDestroyed();
|
||||
|
||||
void Shutdown(bool clear_state);
|
||||
void Render();
|
||||
void InvalidateCoverCache();
|
||||
void InvalidateCoverCache(std::string path = {});
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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())
|
||||
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,
|
||||
|
||||
@@ -101,7 +101,7 @@ void DoStartPath(std::string path, std::string state = std::string(), std::optio
|
||||
|
||||
GPUTexture* GetCoverForCurrentGame(const std::string& game_path);
|
||||
void SetCoverCacheEntry(std::string path, std::string cover_path);
|
||||
void ClearCoverCache();
|
||||
void RemoveCoverCacheEntry(const std::string& path);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Settings
|
||||
|
||||
@@ -1467,7 +1467,8 @@ void FullscreenUI::DrawFolderSetting(SettingsInterface* bsi, std::string_view ti
|
||||
SetSettingsChanged(bsi);
|
||||
|
||||
Host::RunOnCPUThread(&EmuFolders::Update);
|
||||
ClearCoverCache();
|
||||
if (key == "Covers")
|
||||
RemoveCoverCacheEntry({});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "coverdownloadwindow.h"
|
||||
#include "gamelistwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qthost.h"
|
||||
#include "qtprogresscallback.h"
|
||||
@@ -52,7 +53,9 @@ void CoverDownloadWindow::onStartClicked()
|
||||
Error error;
|
||||
const bool result = GameList::DownloadCovers(
|
||||
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); };
|
||||
});
|
||||
@@ -66,7 +69,7 @@ void CoverDownloadWindow::onStartClicked()
|
||||
|
||||
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."));
|
||||
if (!result)
|
||||
|
||||
@@ -252,12 +252,7 @@ bool GameListModel::getShowLocalizedTitles() const
|
||||
void GameListModel::setShowLocalizedTitles(bool enabled)
|
||||
{
|
||||
m_show_localized_titles = enabled;
|
||||
|
||||
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();
|
||||
invalidateColumn(Column_Title);
|
||||
}
|
||||
|
||||
bool GameListModel::getShowCoverTitles() const
|
||||
@@ -303,13 +298,7 @@ void GameListModel::setIconSize(int size)
|
||||
emit headerDataChanged(Qt::Vertical, 0, rowCount() - 1);
|
||||
|
||||
loadSizeDependentPixmaps();
|
||||
refreshIcons();
|
||||
}
|
||||
|
||||
void GameListModel::refreshIcons()
|
||||
{
|
||||
m_icon_pixmap_cache.Clear();
|
||||
emit dataChanged(index(0, Column_Icon), index(rowCount() - 1, Column_Icon), {Qt::DecorationRole});
|
||||
invalidateColumn(Column_Icon);
|
||||
}
|
||||
|
||||
float GameListModel::getCoverScale() const
|
||||
@@ -372,12 +361,6 @@ void GameListModel::updateCoverScale()
|
||||
{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,
|
||||
int top_left_row)
|
||||
{
|
||||
@@ -422,8 +405,8 @@ void GameListModel::setDevicePixelRatio(qreal dpr)
|
||||
m_flag_pixmap_cache.clear();
|
||||
loadCommonImages();
|
||||
loadCoverScaleDependentPixmaps();
|
||||
refreshCovers();
|
||||
refreshIcons();
|
||||
invalidateColumn(Column_Icon);
|
||||
invalidateColumn(Column_Cover);
|
||||
}
|
||||
|
||||
void GameListModel::reloadThemeSpecificImages()
|
||||
@@ -510,7 +493,7 @@ void GameListModel::coverLoaded(const std::string& path, const QImage& image, fl
|
||||
pmp->scale = scale;
|
||||
pmp->is_loading = false;
|
||||
|
||||
invalidateCoverForPath(path);
|
||||
invalidateColumnForPath(path, Column_Cover, false);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
std::optional<u32> row;
|
||||
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 */)
|
||||
{
|
||||
// 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())
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(m_taken_entries->size()); i++)
|
||||
{
|
||||
if (path == m_taken_entries.value()[i].path)
|
||||
{
|
||||
row = i;
|
||||
break;
|
||||
remove_entry(&m_taken_entries.value()[i], static_cast<int>(i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,28 +590,14 @@ void GameListModel::invalidateCoverForPath(const std::string& path)
|
||||
const size_t count = GameList::GetEntryCount();
|
||||
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);
|
||||
break;
|
||||
remove_entry(entry, static_cast<int>(i));
|
||||
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
|
||||
@@ -672,7 +690,8 @@ void GameListModel::setShowGameIcons(bool enabled)
|
||||
|
||||
if (enabled)
|
||||
GameList::ReloadMemcardTimestampCache();
|
||||
refreshIcons();
|
||||
|
||||
invalidateColumn(Column_Icon);
|
||||
}
|
||||
|
||||
QIcon GameListModel::getIconForGame(const QString& path)
|
||||
@@ -2053,12 +2072,6 @@ void GameListWidget::onGridViewContextMenuRequested(const QPoint& point)
|
||||
emit entryContextMenuRequested(m_grid_view->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void GameListWidget::refreshGridCovers()
|
||||
{
|
||||
m_model->refreshCovers();
|
||||
Host::RunOnCPUThread(&FullscreenUI::InvalidateCoverCache);
|
||||
}
|
||||
|
||||
void GameListWidget::focusSearchWidget()
|
||||
{
|
||||
m_ui.searchText->setFocus(Qt::ShortcutFocusReason);
|
||||
@@ -2110,7 +2123,6 @@ void GameListWidget::setMergeDiscSets(bool enabled)
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListMergeDiscSets", enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_sort_model->setMergeDiscSets(enabled);
|
||||
m_model->refreshIcons();
|
||||
}
|
||||
|
||||
void GameListWidget::setShowLocalizedTitles(bool enabled)
|
||||
@@ -2160,7 +2172,7 @@ void GameListWidget::setPreferAchievementGameIcons(bool enabled)
|
||||
if (!enabled)
|
||||
GameList::ReloadMemcardTimestampCache();
|
||||
|
||||
m_model->refreshIcons();
|
||||
m_model->invalidateColumn(GameListModel::Column_Icon);
|
||||
}
|
||||
|
||||
void GameListWidget::setShowCoverTitles(bool enabled)
|
||||
|
||||
@@ -102,7 +102,6 @@ public:
|
||||
|
||||
int getIconSize() const;
|
||||
int getIconSizeWithPadding() const;
|
||||
void refreshIcons();
|
||||
void setIconSize(int size);
|
||||
|
||||
bool getShowGameIcons() const;
|
||||
@@ -116,7 +115,6 @@ public:
|
||||
QSize getDeviceScaledCoverArtSize() const;
|
||||
int getCoverArtSpacing() const;
|
||||
QFont getCoverCaptionFont() const;
|
||||
void refreshCovers();
|
||||
void updateCacheSize(int num_rows, int num_columns, QSortFilterProxyModel* const sort_model, int top_left_row);
|
||||
|
||||
qreal getDevicePixelRatio() const;
|
||||
@@ -125,7 +123,9 @@ public:
|
||||
const QPixmap* lookupIconPixmapForEntry(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:
|
||||
void coverScaleChanged(float scale);
|
||||
@@ -146,7 +146,6 @@ private:
|
||||
void updateCoverScale();
|
||||
void loadCoverScaleDependentPixmaps();
|
||||
void loadOrGenerateCover(const GameList::Entry* ge);
|
||||
void invalidateCoverForPath(const std::string& path);
|
||||
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,
|
||||
@@ -155,6 +154,8 @@ private:
|
||||
static void createPlaceholderImage(QImage& image, const QImage& placeholder_image, const QSize& size, float scale,
|
||||
const QString& title);
|
||||
|
||||
static Qt::ItemDataRole getRoleToInvalidate(int column);
|
||||
|
||||
const QPixmap& getIconPixmapForEntry(const GameList::Entry* ge) const;
|
||||
const QPixmap& getFlagPixmapForEntry(const GameList::Entry* ge) const;
|
||||
|
||||
@@ -284,7 +285,6 @@ public:
|
||||
void setAnimateGameIcons(bool enabled);
|
||||
void setPreferAchievementGameIcons(bool enabled);
|
||||
void setShowCoverTitles(bool enabled);
|
||||
void refreshGridCovers();
|
||||
void focusSearchWidget();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "gamesummarywidget.h"
|
||||
#include "gamelistwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qthost.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)
|
||||
@@ -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)
|
||||
@@ -298,7 +299,7 @@ void GameSummaryWidget::onCustomLanguageChanged(int language)
|
||||
m_path, (language > 0) ? std::optional<GameDatabase::Language>(static_cast<GameDatabase::Language>(language - 1)) :
|
||||
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)
|
||||
|
||||
@@ -1753,7 +1753,8 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
|
||||
tr("Failed to remove '%1'").arg(old_filename));
|
||||
return;
|
||||
}
|
||||
m_game_list_widget->refreshGridCovers();
|
||||
|
||||
m_game_list_widget->getModel()->invalidateColumnForPath(entry->path, GameListModel::Column_Cover);
|
||||
}
|
||||
|
||||
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.actionViewZoomIn, &QAction::triggered, this, &MainWindow::onViewZoomInActionTriggered);
|
||||
connect(m_ui.actionViewZoomOut, &QAction::triggered, this, &MainWindow::onViewZoomOutActionTriggered);
|
||||
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget,
|
||||
&GameListWidget::refreshGridCovers);
|
||||
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, this,
|
||||
[this]() { m_game_list_widget->getModel()->invalidateColumn(GameListModel::Column_Cover); });
|
||||
connect(m_ui.actionChangeGameListBackground, &QAction::triggered, this,
|
||||
&MainWindow::onViewChangeGameListBackgroundTriggered);
|
||||
connect(m_ui.actionClearGameListBackground, &QAction::triggered, this,
|
||||
@@ -2944,11 +2945,6 @@ void MainWindow::refreshGameList(bool invalidate_cache)
|
||||
m_game_list_widget->refresh(invalidate_cache);
|
||||
}
|
||||
|
||||
void MainWindow::refreshGameListModel()
|
||||
{
|
||||
m_game_list_widget->getModel()->refresh();
|
||||
}
|
||||
|
||||
void MainWindow::cancelGameListRefresh()
|
||||
{
|
||||
m_game_list_widget->cancelRefresh();
|
||||
@@ -2959,16 +2955,6 @@ QIcon MainWindow::getIconForGame(const QString& 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)
|
||||
{
|
||||
func();
|
||||
@@ -3293,11 +3279,9 @@ void MainWindow::onToolsDownloadAchievementGameIconsTriggered()
|
||||
const bool result = Achievements::DownloadGameIcons(progress, &error);
|
||||
return [error = std::move(error), result]() {
|
||||
if (!result)
|
||||
{
|
||||
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);
|
||||
return [error = std::move(error), result]() {
|
||||
if (!result)
|
||||
{
|
||||
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->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* getStatusFPSWidget() const { return m_status_fps_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; }
|
||||
|
||||
/// Opens the editor for a specific input profile.
|
||||
@@ -120,11 +121,8 @@ public:
|
||||
void updateDebugMenuVisibility();
|
||||
|
||||
void refreshGameList(bool invalidate_cache);
|
||||
void refreshGameListModel();
|
||||
void cancelGameListRefresh();
|
||||
QIcon getIconForGame(const QString& path);
|
||||
void invalidateCoverCacheForPath(const std::string& path);
|
||||
void refreshGameGridCovers();
|
||||
void refreshAchievementProgress();
|
||||
|
||||
void runOnUIThread(const std::function<void()>& func);
|
||||
|
||||
Reference in New Issue
Block a user