From 0f2ef98747c036a15ee4ebf2291f347b6660d330 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 18 Jul 2025 13:04:11 +1000 Subject: [PATCH] Qt: Turn cover downloader into a non-modal window Same as the others. --- src/duckstation-qt/CMakeLists.txt | 6 +-- ...loaddialog.cpp => coverdownloadwindow.cpp} | 53 ++++++++++--------- ...downloaddialog.h => coverdownloadwindow.h} | 21 +++++--- ...wnloaddialog.ui => coverdownloadwindow.ui} | 4 +- src/duckstation-qt/duckstation-qt.vcxproj | 8 +-- .../duckstation-qt.vcxproj.filters | 8 +-- src/duckstation-qt/mainwindow.cpp | 27 ++++++++-- src/duckstation-qt/mainwindow.h | 2 + 8 files changed, 79 insertions(+), 50 deletions(-) rename src/duckstation-qt/{coverdownloaddialog.cpp => coverdownloadwindow.cpp} (62%) rename src/duckstation-qt/{coverdownloaddialog.h => coverdownloadwindow.h} (77%) rename src/duckstation-qt/{coverdownloaddialog.ui => coverdownloadwindow.ui} (97%) diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 2f3594baa..30b77e2c3 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -52,9 +52,9 @@ set(SRCS controllersettingswindow.h controllersettingswindow.ui controllersettingwidgetbinder.h - coverdownloaddialog.cpp - coverdownloaddialog.h - coverdownloaddialog.ui + coverdownloadwindow.cpp + coverdownloadwindow.h + coverdownloadwindow.ui debuggeraddbreakpointdialog.ui debuggermodels.cpp debuggermodels.h diff --git a/src/duckstation-qt/coverdownloaddialog.cpp b/src/duckstation-qt/coverdownloadwindow.cpp similarity index 62% rename from src/duckstation-qt/coverdownloaddialog.cpp rename to src/duckstation-qt/coverdownloadwindow.cpp index f4e436fc5..144fc3f71 100644 --- a/src/duckstation-qt/coverdownloaddialog.cpp +++ b/src/duckstation-qt/coverdownloadwindow.cpp @@ -1,41 +1,45 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 -#include "coverdownloaddialog.h" +#include "coverdownloadwindow.h" #include "qthost.h" #include "core/game_list.h" #include "common/assert.h" -CoverDownloadDialog::CoverDownloadDialog(QWidget* parent /*= nullptr*/) : QDialog(parent) +CoverDownloadWindow::CoverDownloadWindow() : QWidget() { m_ui.setupUi(this); setWindowIcon(QtHost::GetAppIcon()); m_ui.coverIcon->setPixmap(QIcon::fromTheme(QStringLiteral("artboard-2-line")).pixmap(32)); updateEnabled(); + QtUtils::RestoreWindowGeometry("CoverDownloadWindow", this); - connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadDialog::onStartClicked); - connect(m_ui.close, &QPushButton::clicked, this, &CoverDownloadDialog::onCloseClicked); - connect(m_ui.urls, &QTextEdit::textChanged, this, &CoverDownloadDialog::updateEnabled); + connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadWindow::onStartClicked); + connect(m_ui.close, &QPushButton::clicked, this, &CoverDownloadWindow::onCloseClicked); + connect(m_ui.urls, &QTextEdit::textChanged, this, &CoverDownloadWindow::updateEnabled); } -CoverDownloadDialog::~CoverDownloadDialog() +CoverDownloadWindow::~CoverDownloadWindow() { Assert(!m_thread); } -void CoverDownloadDialog::closeEvent(QCloseEvent* ev) +void CoverDownloadWindow::closeEvent(QCloseEvent* ev) { + QtUtils::SaveWindowGeometry("CoverDownloadWindow", this); + QWidget::closeEvent(ev); cancelThread(); + emit closed(); } -void CoverDownloadDialog::onDownloadStatus(const QString& text) +void CoverDownloadWindow::onDownloadStatus(const QString& text) { m_ui.status->setText(text); } -void CoverDownloadDialog::onDownloadProgress(int value, int range) +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. @@ -50,7 +54,7 @@ void CoverDownloadDialog::onDownloadProgress(int value, int range) m_ui.progress->setValue(value); } -void CoverDownloadDialog::onDownloadComplete() +void CoverDownloadWindow::onDownloadComplete() { emit coverRefreshRequested(); @@ -65,7 +69,7 @@ void CoverDownloadDialog::onDownloadComplete() m_ui.status->setText(tr("Download complete.")); } -void CoverDownloadDialog::onStartClicked() +void CoverDownloadWindow::onStartClicked() { if (m_thread) cancelThread(); @@ -73,15 +77,15 @@ void CoverDownloadDialog::onStartClicked() startThread(); } -void CoverDownloadDialog::onCloseClicked() +void CoverDownloadWindow::onCloseClicked() { if (m_thread) cancelThread(); - reject(); + close(); } -void CoverDownloadDialog::updateEnabled() +void CoverDownloadWindow::updateEnabled() { const bool running = static_cast(m_thread); m_ui.start->setText(running ? tr("Stop") : tr("Start")); @@ -90,18 +94,19 @@ void CoverDownloadDialog::updateEnabled() m_ui.urls->setEnabled(!running); } -void CoverDownloadDialog::startThread() +void CoverDownloadWindow::startThread() { - m_thread = std::make_unique(this, m_ui.urls->toPlainText(), m_ui.useSerialFileNames->isChecked()); + m_thread = + std::make_unique(this, m_ui.urls->toPlainText(), m_ui.useSerialFileNames->isChecked()); m_last_refresh_time.Reset(); - connect(m_thread.get(), &CoverDownloadThread::statusUpdated, this, &CoverDownloadDialog::onDownloadStatus); - connect(m_thread.get(), &CoverDownloadThread::progressUpdated, this, &CoverDownloadDialog::onDownloadProgress); - connect(m_thread.get(), &CoverDownloadThread::threadFinished, this, &CoverDownloadDialog::onDownloadComplete); + connect(m_thread.get(), &CoverDownloadThread::statusUpdated, this, &CoverDownloadWindow::onDownloadStatus); + connect(m_thread.get(), &CoverDownloadThread::progressUpdated, this, &CoverDownloadWindow::onDownloadProgress); + connect(m_thread.get(), &CoverDownloadThread::threadFinished, this, &CoverDownloadWindow::onDownloadComplete); m_thread->start(); updateEnabled(); } -void CoverDownloadDialog::cancelThread() +void CoverDownloadWindow::cancelThread() { if (!m_thread) return; @@ -111,16 +116,16 @@ void CoverDownloadDialog::cancelThread() m_thread.reset(); } -CoverDownloadDialog::CoverDownloadThread::CoverDownloadThread(QWidget* parent, const QString& urls, bool use_serials) +CoverDownloadWindow::CoverDownloadThread::CoverDownloadThread(QWidget* parent, const QString& urls, bool use_serials) : QtAsyncProgressThread(parent), m_use_serials(use_serials) { for (const QString& str : urls.split(QChar('\n'))) m_urls.push_back(str.toStdString()); } -CoverDownloadDialog::CoverDownloadThread::~CoverDownloadThread() = default; +CoverDownloadWindow::CoverDownloadThread::~CoverDownloadThread() = default; -void CoverDownloadDialog::CoverDownloadThread::runAsync() +void CoverDownloadWindow::CoverDownloadThread::runAsync() { GameList::DownloadCovers(m_urls, m_use_serials, this); } diff --git a/src/duckstation-qt/coverdownloaddialog.h b/src/duckstation-qt/coverdownloadwindow.h similarity index 77% rename from src/duckstation-qt/coverdownloaddialog.h rename to src/duckstation-qt/coverdownloadwindow.h index ff1ebbe13..8a95a5d56 100644 --- a/src/duckstation-qt/coverdownloaddialog.h +++ b/src/duckstation-qt/coverdownloadwindow.h @@ -1,25 +1,30 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once + +#include "qtprogresscallback.h" +#include "ui_coverdownloadwindow.h" + #include "common/timer.h" #include "common/types.h" -#include "qtprogresscallback.h" -#include "ui_coverdownloaddialog.h" -#include + +#include + #include #include #include -class CoverDownloadDialog final : public QDialog +class CoverDownloadWindow final : public QWidget { Q_OBJECT public: - CoverDownloadDialog(QWidget* parent = nullptr); - ~CoverDownloadDialog(); + CoverDownloadWindow(); + ~CoverDownloadWindow(); Q_SIGNALS: + void closed(); void coverRefreshRequested(); protected: @@ -51,7 +56,7 @@ private: void startThread(); void cancelThread(); - Ui::CoverDownloadDialog m_ui; + Ui::CoverDownloadWindow m_ui; std::unique_ptr m_thread; Timer m_last_refresh_time; }; diff --git a/src/duckstation-qt/coverdownloaddialog.ui b/src/duckstation-qt/coverdownloadwindow.ui similarity index 97% rename from src/duckstation-qt/coverdownloaddialog.ui rename to src/duckstation-qt/coverdownloadwindow.ui index 05c420d36..053494559 100644 --- a/src/duckstation-qt/coverdownloaddialog.ui +++ b/src/duckstation-qt/coverdownloadwindow.ui @@ -1,7 +1,7 @@ - CoverDownloadDialog - + CoverDownloadWindow + 0 diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index f356258f8..4ab4bbf6b 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -14,7 +14,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -202,7 +202,7 @@ Document - + Document @@ -224,7 +224,7 @@ - + diff --git a/src/duckstation-qt/duckstation-qt.vcxproj.filters b/src/duckstation-qt/duckstation-qt.vcxproj.filters index fdbc3dc49..eb60e74ea 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj.filters +++ b/src/duckstation-qt/duckstation-qt.vcxproj.filters @@ -36,7 +36,7 @@ - + @@ -77,7 +77,7 @@ moc - + moc @@ -227,7 +227,7 @@ - + @@ -272,7 +272,7 @@ - + diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 0ad82314d..5ac193b5e 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -5,7 +5,7 @@ #include "aboutdialog.h" #include "achievementlogindialog.h" #include "autoupdaterwindow.h" -#include "coverdownloaddialog.h" +#include "coverdownloadwindow.h" #include "debuggerwindow.h" #include "displaywidget.h" #include "gamelistsettingswidget.h" @@ -834,6 +834,7 @@ void MainWindow::recreate() void MainWindow::destroySubWindows() { + QtUtils::CloseAndDeleteWindow(m_cover_download_window); QtUtils::CloseAndDeleteWindow(m_memory_scanner_window); QtUtils::CloseAndDeleteWindow(m_debugger_window); QtUtils::CloseAndDeleteWindow(m_memory_card_editor_window); @@ -2983,10 +2984,26 @@ void MainWindow::onToolsMemoryCardEditorTriggered() void MainWindow::onToolsCoverDownloaderTriggered() { // This can be invoked via big picture, so exit fullscreen. - SystemLock lock(pauseAndLockSystem()); - CoverDownloadDialog dlg(lock.getDialogParent()); - connect(&dlg, &CoverDownloadDialog::coverRefreshRequested, m_game_list_widget, &GameListWidget::refreshGridCovers); - dlg.exec(); + if (isRenderingFullscreen()) + { + g_emu_thread->setFullscreen(false, true); + + // wait for the fullscreen request to actually go through, otherwise the downloader appears behind the main window. + QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents, [this]() { return isRenderingFullscreen(); }); + } + + if (!m_cover_download_window) + { + m_cover_download_window = new CoverDownloadWindow(); + connect(m_cover_download_window, &CoverDownloadWindow::coverRefreshRequested, m_game_list_widget, + &GameListWidget::refreshGridCovers); + connect(m_cover_download_window, &CoverDownloadWindow::closed, this, [this]() { + m_cover_download_window->deleteLater(); + m_cover_download_window = nullptr; + }); + } + + QtUtils::ShowOrRaiseWindow(m_cover_download_window); } void MainWindow::onToolsMediaCaptureToggled(bool checked) diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 7d5ef2acc..dea06a4cc 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -33,6 +33,7 @@ class AutoUpdaterWindow; class MemoryCardEditorWindow; class DebuggerWindow; class MemoryScannerWindow; +class CoverDownloadWindow; struct SystemBootParameters; @@ -339,6 +340,7 @@ private: MemoryCardEditorWindow* m_memory_card_editor_window = nullptr; DebuggerWindow* m_debugger_window = nullptr; MemoryScannerWindow* m_memory_scanner_window = nullptr; + CoverDownloadWindow* m_cover_download_window = nullptr; bool m_was_paused_by_focus_loss = false; bool m_relative_mouse_mode = false;