diff --git a/src/duckstation-qt/coverdownloadwindow.cpp b/src/duckstation-qt/coverdownloadwindow.cpp index 8feedd31a..40d1b8b6b 100644 --- a/src/duckstation-qt/coverdownloadwindow.cpp +++ b/src/duckstation-qt/coverdownloadwindow.cpp @@ -3,10 +3,11 @@ #include "coverdownloadwindow.h" #include "qthost.h" +#include "qtprogresscallback.h" #include "core/game_list.h" -#include "common/assert.h" +#include "common/error.h" #include "moc_coverdownloadwindow.cpp" @@ -17,152 +18,77 @@ CoverDownloadWindow::CoverDownloadWindow() : QWidget() updateEnabled(); connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadWindow::onStartClicked); - connect(m_ui.close, &QPushButton::clicked, this, &CoverDownloadWindow::onCloseClicked); + connect(m_ui.close, &QPushButton::clicked, this, &CoverDownloadWindow::close); connect(m_ui.urls, &QTextEdit::textChanged, this, &CoverDownloadWindow::updateEnabled); } -CoverDownloadWindow::~CoverDownloadWindow() -{ - Assert(!m_thread); -} +CoverDownloadWindow::~CoverDownloadWindow() = default; void CoverDownloadWindow::closeEvent(QCloseEvent* ev) { QtUtils::SaveWindowGeometry(this); QWidget::closeEvent(ev); - cancelThread(); + if (m_task) + m_task->cancel(); emit closed(); } -void CoverDownloadWindow::onDownloadStatus(const QString& text) -{ - m_ui.status->setText(text); -} - -void CoverDownloadWindow::onDownloadProgress(int value, int range) -{ - // Limit to once every five seconds, otherwise it's way too flickery. - // Ideally in the future we'd have some way to invalidate only a single cover. - if (m_last_refresh_time.GetTimeSeconds() >= 5.0f) - { - emit coverRefreshRequested(); - m_last_refresh_time.Reset(); - } - - if (range != m_ui.progress->maximum()) - m_ui.progress->setMaximum(range); - m_ui.progress->setValue(value); -} - -void CoverDownloadWindow::onDownloadComplete() -{ - emit coverRefreshRequested(); - - m_ui.status->setText(tr("Download complete.")); - - QString error; - if (m_thread) - { - m_thread->wait(); - if (!m_thread->getResult()) - { - if (const std::string& err_str = m_thread->getError().GetDescription(); !err_str.empty()) - m_ui.status->setText(QString::fromStdString(err_str)); - } - - delete m_thread; - m_thread = nullptr; - } - - updateEnabled(); -} - void CoverDownloadWindow::onStartClicked() { - if (m_thread) - cancelThread(); - else - startThread(); + if (m_task) + { + m_task->cancel(); + return; + } + + std::vector urls; + const bool use_serials = m_ui.useSerialFileNames->isChecked(); + for (const QString& str : m_ui.urls->toPlainText().split(QChar('\n'))) + urls.push_back(str.toStdString()); + + m_task = QtAsyncTaskWithProgress::create( + this, [this, urls = std::move(urls), use_serials](ProgressCallback* const progress) { + Error error; + const bool result = GameList::DownloadCovers(urls, use_serials, progress, &error); + return [this, result, error = std::move(error)]() { downloadComplete(result, error); }; + }); + + m_task->connectWidgets(m_ui.status, m_ui.progress, m_ui.start); + + connect(m_task, &QtAsyncTaskWithProgress::progressValueUpdated, this, [this]() { + // Limit to once every five seconds, otherwise it's way too flickery. + // Ideally in the future we'd have some way to invalidate only a single cover. + if (m_last_refresh_time.GetTimeSeconds() >= 5.0f) + { + emit coverRefreshRequested(); + m_last_refresh_time.Reset(); + } + }); + + m_task->start(); + + updateEnabled(); } -void CoverDownloadWindow::onCloseClicked() +void CoverDownloadWindow::downloadComplete(bool result, const Error& error) { - if (m_thread) - cancelThread(); + emit coverRefreshRequested(); + m_ui.status->setText(tr("Download complete.")); + if (!result) + { + if (const std::string& err_str = error.GetDescription(); !err_str.empty()) + m_ui.status->setText(QString::fromStdString(err_str)); + } - close(); + m_task = nullptr; + updateEnabled(); } void CoverDownloadWindow::updateEnabled() { - const bool running = static_cast(m_thread); + const bool running = static_cast(m_task); m_ui.start->setText(running ? tr("Stop") : tr("Start")); m_ui.start->setEnabled(running || !m_ui.urls->toPlainText().isEmpty()); m_ui.close->setEnabled(!running); m_ui.urls->setEnabled(!running); } - -void CoverDownloadWindow::startThread() -{ - m_thread = new CoverDownloadThread(m_ui.urls->toPlainText(), m_ui.useSerialFileNames->isChecked()); - m_last_refresh_time.Reset(); - m_thread->moveToThread(m_thread); - connect(m_thread, &CoverDownloadThread::statusUpdated, this, &CoverDownloadWindow::onDownloadStatus); - connect(m_thread, &CoverDownloadThread::progressUpdated, this, &CoverDownloadWindow::onDownloadProgress); - connect(m_thread, &CoverDownloadThread::threadFinished, this, &CoverDownloadWindow::onDownloadComplete); - m_thread->start(); - updateEnabled(); -} - -void CoverDownloadWindow::cancelThread() -{ - if (!m_thread) - return; - - m_thread->requestInterruption(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; -} - -CoverDownloadThread::CoverDownloadThread(const QString& urls, bool use_serials) : QThread(), m_use_serials(use_serials) -{ - for (const QString& str : urls.split(QChar('\n'))) - m_urls.push_back(str.toStdString()); -} - -CoverDownloadThread::~CoverDownloadThread() = default; - -bool CoverDownloadThread::IsCancelled() const -{ - return isInterruptionRequested(); -} - -void CoverDownloadThread::SetTitle(const std::string_view title) -{ - emit titleUpdated(QtUtils::StringViewToQString(title)); -} - -void CoverDownloadThread::SetStatusText(const std::string_view text) -{ - ProgressCallback::SetStatusText(text); - emit statusUpdated(QtUtils::StringViewToQString(text)); -} - -void CoverDownloadThread::SetProgressRange(u32 range) -{ - ProgressCallback::SetProgressRange(range); - emit progressUpdated(static_cast(m_progress_value), static_cast(m_progress_range)); -} - -void CoverDownloadThread::SetProgressValue(u32 value) -{ - ProgressCallback::SetProgressValue(value); - emit progressUpdated(static_cast(m_progress_value), static_cast(m_progress_range)); -} - -void CoverDownloadThread::run() -{ - m_result = GameList::DownloadCovers(m_urls, m_use_serials, static_cast(this), &m_error); - emit threadFinished(); -} diff --git a/src/duckstation-qt/coverdownloadwindow.h b/src/duckstation-qt/coverdownloadwindow.h index 8bd2aad36..7efdfd3fb 100644 --- a/src/duckstation-qt/coverdownloadwindow.h +++ b/src/duckstation-qt/coverdownloadwindow.h @@ -5,8 +5,6 @@ #include "ui_coverdownloadwindow.h" -#include "common/error.h" -#include "common/progress_callback.h" #include "common/timer.h" #include "common/types.h" @@ -16,7 +14,9 @@ #include #include -class CoverDownloadThread; +class Error; + +class QtAsyncTaskWithProgress; class CoverDownloadWindow final : public QWidget { @@ -34,50 +34,11 @@ protected: void closeEvent(QCloseEvent* ev) override; private: - void startThread(); - void cancelThread(); - - void onDownloadStatus(const QString& text); - void onDownloadProgress(int value, int range); - void onDownloadComplete(); void onStartClicked(); - void onCloseClicked(); + void downloadComplete(bool result, const Error& error); void updateEnabled(); Ui::CoverDownloadWindow m_ui; - CoverDownloadThread* m_thread = nullptr; + QtAsyncTaskWithProgress* m_task = nullptr; Timer m_last_refresh_time; }; - -class CoverDownloadThread final : public QThread, private ProgressCallback -{ - Q_OBJECT - -public: - CoverDownloadThread(const QString& urls, bool use_serials); - ~CoverDownloadThread(); - - ALWAYS_INLINE const Error& getError() const { return m_error; } - ALWAYS_INLINE bool getResult() const { return m_result; } - -Q_SIGNALS: - void titleUpdated(const QString& title); - void statusUpdated(const QString& status); - void progressUpdated(int value, int range); - void threadFinished(); - -protected: - void run() override; - - bool IsCancelled() const override; - void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; - -private: - std::vector m_urls; - Error m_error; - bool m_use_serials = false; - bool m_result = false; -}; \ No newline at end of file