mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-08 15:24:33 +00:00
Qt: Don't reset model for every scanned game
Make the UI a little more responsive.
This commit is contained in:
@@ -14,27 +14,30 @@
|
||||
|
||||
#include "moc_gamelistrefreshthread.cpp"
|
||||
|
||||
AsyncRefreshProgressCallback::AsyncRefreshProgressCallback(GameListRefreshThread* parent) : m_parent(parent)
|
||||
GameListRefreshThread::GameListRefreshThread(bool invalidate_cache) : QThread(), m_invalidate_cache(invalidate_cache)
|
||||
{
|
||||
}
|
||||
|
||||
float AsyncRefreshProgressCallback::timeSinceStart() const
|
||||
{
|
||||
return m_start_time.GetTimeSeconds();
|
||||
}
|
||||
GameListRefreshThread::~GameListRefreshThread() = default;
|
||||
|
||||
void AsyncRefreshProgressCallback::Cancel()
|
||||
void GameListRefreshThread::cancel()
|
||||
{
|
||||
// Not atomic, but we don't need to cancel immediately.
|
||||
m_cancelled = true;
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::PushState()
|
||||
void GameListRefreshThread::run()
|
||||
{
|
||||
GameList::Refresh(m_invalidate_cache, false, this);
|
||||
emit refreshComplete();
|
||||
}
|
||||
|
||||
void GameListRefreshThread::PushState()
|
||||
{
|
||||
ProgressCallback::PushState();
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::PopState()
|
||||
void GameListRefreshThread::PopState()
|
||||
{
|
||||
ProgressCallback::PopState();
|
||||
|
||||
@@ -46,7 +49,7 @@ void AsyncRefreshProgressCallback::PopState()
|
||||
fireUpdate();
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::SetStatusText(const std::string_view text)
|
||||
void GameListRefreshThread::SetStatusText(const std::string_view text)
|
||||
{
|
||||
const QString new_text = QtUtils::StringViewToQString(text);
|
||||
if (new_text == m_status_text)
|
||||
@@ -56,7 +59,7 @@ void AsyncRefreshProgressCallback::SetStatusText(const std::string_view text)
|
||||
fireUpdate();
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::SetProgressRange(u32 range)
|
||||
void GameListRefreshThread::SetProgressRange(u32 range)
|
||||
{
|
||||
ProgressCallback::SetProgressRange(range);
|
||||
if (static_cast<int>(m_progress_range) == m_last_range)
|
||||
@@ -66,7 +69,7 @@ void AsyncRefreshProgressCallback::SetProgressRange(u32 range)
|
||||
fireUpdate();
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::SetProgressValue(u32 value)
|
||||
void GameListRefreshThread::SetProgressValue(u32 value)
|
||||
{
|
||||
ProgressCallback::SetProgressValue(value);
|
||||
if (static_cast<int>(m_progress_value) == m_last_value)
|
||||
@@ -76,46 +79,24 @@ void AsyncRefreshProgressCallback::SetProgressValue(u32 value)
|
||||
fireUpdate();
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::ModalError(const std::string_view message)
|
||||
void GameListRefreshThread::ModalError(const std::string_view message)
|
||||
{
|
||||
QMessageBox::critical(nullptr, QStringLiteral("Error"), QtUtils::StringViewToQString(message));
|
||||
}
|
||||
|
||||
bool AsyncRefreshProgressCallback::ModalConfirmation(const std::string_view message)
|
||||
bool GameListRefreshThread::ModalConfirmation(const std::string_view message)
|
||||
{
|
||||
return QMessageBox::question(nullptr, QStringLiteral("Question"), QtUtils::StringViewToQString(message)) ==
|
||||
QMessageBox::Yes;
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::ModalInformation(const std::string_view message)
|
||||
void GameListRefreshThread::ModalInformation(const std::string_view message)
|
||||
{
|
||||
QMessageBox::information(nullptr, QStringLiteral("Information"), QtUtils::StringViewToQString(message));
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::fireUpdate()
|
||||
void GameListRefreshThread::fireUpdate()
|
||||
{
|
||||
m_parent->refreshProgress(m_status_text, m_last_value, m_last_range, m_start_time.GetTimeSeconds());
|
||||
}
|
||||
|
||||
GameListRefreshThread::GameListRefreshThread(bool invalidate_cache)
|
||||
: QThread(), m_progress(this), m_invalidate_cache(invalidate_cache)
|
||||
{
|
||||
}
|
||||
|
||||
GameListRefreshThread::~GameListRefreshThread() = default;
|
||||
|
||||
float GameListRefreshThread::timeSinceStart() const
|
||||
{
|
||||
return m_progress.timeSinceStart();
|
||||
}
|
||||
|
||||
void GameListRefreshThread::cancel()
|
||||
{
|
||||
m_progress.Cancel();
|
||||
}
|
||||
|
||||
void GameListRefreshThread::run()
|
||||
{
|
||||
GameList::Refresh(m_invalidate_cache, false, &m_progress);
|
||||
emit refreshComplete();
|
||||
emit refreshProgress(m_status_text, m_last_value, m_last_range, static_cast<int>(GameList::GetEntryCount()),
|
||||
m_start_time.GetTimeSeconds());
|
||||
}
|
||||
|
||||
@@ -8,39 +8,7 @@
|
||||
#include "common/progress_callback.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
class GameListRefreshThread;
|
||||
|
||||
class AsyncRefreshProgressCallback : public ProgressCallback
|
||||
{
|
||||
public:
|
||||
explicit AsyncRefreshProgressCallback(GameListRefreshThread* parent);
|
||||
|
||||
float timeSinceStart() const;
|
||||
|
||||
void Cancel();
|
||||
|
||||
void PushState() override;
|
||||
void PopState() override;
|
||||
|
||||
void SetStatusText(const std::string_view text) override;
|
||||
void SetProgressRange(u32 range) override;
|
||||
void SetProgressValue(u32 value) override;
|
||||
|
||||
void ModalError(const std::string_view message) override;
|
||||
bool ModalConfirmation(const std::string_view message) override;
|
||||
void ModalInformation(const std::string_view message) override;
|
||||
|
||||
private:
|
||||
void fireUpdate();
|
||||
|
||||
GameListRefreshThread* m_parent;
|
||||
Timer m_start_time;
|
||||
QString m_status_text;
|
||||
int m_last_range = 1;
|
||||
int m_last_value = 0;
|
||||
};
|
||||
|
||||
class GameListRefreshThread final : public QThread
|
||||
class GameListRefreshThread final : public QThread, public ProgressCallback
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -53,13 +21,29 @@ public:
|
||||
void cancel();
|
||||
|
||||
Q_SIGNALS:
|
||||
void refreshProgress(const QString& status, int current, int total, float time);
|
||||
void refreshProgress(const QString& status, int current, int total, int entry_count, float time);
|
||||
void refreshComplete();
|
||||
|
||||
protected:
|
||||
void run() final;
|
||||
|
||||
private:
|
||||
AsyncRefreshProgressCallback m_progress;
|
||||
void PushState() override;
|
||||
void PopState() override;
|
||||
|
||||
void SetStatusText(const std::string_view text) override;
|
||||
void SetProgressRange(u32 range) override;
|
||||
void SetProgressValue(u32 value) override;
|
||||
|
||||
void ModalError(const std::string_view message) override;
|
||||
bool ModalConfirmation(const std::string_view message) override;
|
||||
void ModalInformation(const std::string_view message) override;
|
||||
|
||||
void fireUpdate();
|
||||
|
||||
Timer m_start_time;
|
||||
QString m_status_text;
|
||||
int m_last_range = 1;
|
||||
int m_last_value = 0;
|
||||
bool m_invalidate_cache;
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <QtWidgets/QScrollBar>
|
||||
#include <QtWidgets/QStyledItemDelegate>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "moc_gamelistwidget.cpp"
|
||||
|
||||
@@ -1281,6 +1282,7 @@ void GameListWidget::refresh(bool invalidate_cache)
|
||||
Qt::QueuedConnection);
|
||||
connect(m_refresh_thread, &GameListRefreshThread::refreshComplete, this, &GameListWidget::onRefreshComplete,
|
||||
Qt::QueuedConnection);
|
||||
m_refresh_last_entry_count = std::numeric_limits<int>::max(); // force reset on first progress update
|
||||
m_refresh_thread->start();
|
||||
}
|
||||
|
||||
@@ -1336,12 +1338,25 @@ void GameListWidget::updateBackground(bool reload_image)
|
||||
});
|
||||
}
|
||||
|
||||
void GameListWidget::onRefreshProgress(const QString& status, int current, int total, float time)
|
||||
void GameListWidget::onRefreshProgress(const QString& status, int current, int total, int entry_count, float time)
|
||||
{
|
||||
// Avoid spamming the UI on very short refresh (e.g. game exit).
|
||||
static constexpr float SHORT_REFRESH_TIME = 0.5f;
|
||||
if (!m_model->hasTakenGameList())
|
||||
m_model->refresh();
|
||||
{
|
||||
if (entry_count > m_refresh_last_entry_count)
|
||||
{
|
||||
m_model->beginInsertRows(QModelIndex(), m_refresh_last_entry_count, entry_count - 1);
|
||||
m_model->endInsertRows();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_model->beginResetModel();
|
||||
m_model->endResetModel();
|
||||
}
|
||||
|
||||
m_refresh_last_entry_count = entry_count;
|
||||
}
|
||||
|
||||
// switch away from the placeholder while we scan, in case we find anything
|
||||
if (m_ui.stack->currentIndex() == 2)
|
||||
|
||||
@@ -27,11 +27,14 @@ Q_DECLARE_METATYPE(const GameList::Entry*);
|
||||
|
||||
class GameListSortModel;
|
||||
class GameListRefreshThread;
|
||||
class GameListWidget;
|
||||
|
||||
class GameListModel final : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend GameListWidget;
|
||||
|
||||
public:
|
||||
enum Column : int
|
||||
{
|
||||
@@ -242,7 +245,7 @@ Q_SIGNALS:
|
||||
void addGameDirectoryRequested();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onRefreshProgress(const QString& status, int current, int total, float time);
|
||||
void onRefreshProgress(const QString& status, int current, int total, int entry_count, float time);
|
||||
void onRefreshComplete();
|
||||
|
||||
void onCoverScaleChanged(float scale);
|
||||
@@ -280,7 +283,8 @@ private:
|
||||
QWidget* m_empty_widget = nullptr;
|
||||
Ui::EmptyGameListWidget m_empty_ui;
|
||||
|
||||
GameListRefreshThread* m_refresh_thread = nullptr;
|
||||
|
||||
QImage m_background_image;
|
||||
|
||||
GameListRefreshThread* m_refresh_thread = nullptr;
|
||||
int m_refresh_last_entry_count = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user