@@ -12,7 +12,6 @@
#include <QFileInfo>
#include <QIcon>
#include <QMimeData>
#include <QProgressDialog>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWindow>
@@ -87,6 +86,7 @@
#include "DolphinQt/NetPlay/NetPlaySetupDialog.h"
#include "DolphinQt/QtUtils/FileOpenEventFilter.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/QtUtils/RunOnObject.h"
#include "DolphinQt/QtUtils/WindowActivationEventFilter.h"
@@ -615,8 +615,6 @@ void MainWindow::ConnectRenderWidget()

void MainWindow::ConnectHost()
{
connect(Host::GetInstance(), &Host::UpdateProgressDialog, this,
&MainWindow::OnUpdateProgressDialog);
connect(Host::GetInstance(), &Host::RequestStop, this, &MainWindow::RequestStop);
}

@@ -1510,23 +1508,21 @@ void MainWindow::OnImportNANDBackup()
if (file.isEmpty())
return;

QProgressDialog* dialog = new QProgressDialog(this);
dialog->setMinimum(0);
dialog->setMaximum(0);
dialog->setLabelText(tr("Importing NAND backup"));
dialog->setCancelButton(nullptr);
ParallelProgressDialog dialog(this);
dialog.GetRaw()->setMinimum(0);
dialog.GetRaw()->setMaximum(0);
dialog.GetRaw()->setLabelText(tr("Importing NAND backup"));
dialog.GetRaw()->setCancelButton(nullptr);

auto beginning = QDateTime::currentDateTime().toMSecsSinceEpoch();

auto result = std::async(std::launch::async, [&] {
std::future<void> result = std::async(std::launch::async, [&] {
DiscIO::NANDImporter().ImportNANDBin(
file.toStdString(),
[&dialog, beginning] {
QueueOnObject(dialog, [&dialog, beginning] {
dialog->setLabelText(
tr("Importing NAND backup\n Time elapsed: %1s")
.arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000));
});
dialog.SetLabelText(
tr("Importing NAND backup\n Time elapsed: %1s")
.arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000));
},
[this] {
std::optional<std::string> keys_file = RunOnObject(this, [this] {
@@ -1540,10 +1536,10 @@ void MainWindow::OnImportNANDBackup()
return *keys_file;
return std::string("");
});
QueueOnObject(dialog, &QProgressDialog::close);
dialog.Reset();
});

dialog->exec();
dialog.GetRaw()->exec();

result.wait();

@@ -1703,28 +1699,6 @@ void MainWindow::ShowCheatsManager()
m_cheats_manager->show();
}

void MainWindow::OnUpdateProgressDialog(QString title, int progress, int total)
{
if (!m_progress_dialog)
{
m_progress_dialog = new QProgressDialog(m_render_widget, Qt::WindowTitleHint);
m_progress_dialog->show();
m_progress_dialog->setCancelButton(nullptr);
m_progress_dialog->setWindowTitle(tr("Dolphin"));
}

m_progress_dialog->setValue(progress);
m_progress_dialog->setLabelText(title);
m_progress_dialog->setMaximum(total);

if (total < 0 || progress >= total)
{
m_progress_dialog->hide();
m_progress_dialog->deleteLater();
m_progress_dialog = nullptr;
}
}

void MainWindow::Show()
{
if (!Settings::Instance().IsBatchModeEnabled())
@@ -11,7 +11,6 @@
#include <optional>
#include <string>

class QProgressDialog;
class QStackedWidget;
class QString;

@@ -167,8 +166,6 @@ class MainWindow final : public QMainWindow
void OnSignal();
#endif

void OnUpdateProgressDialog(QString label, int progress, int total);

void OnPlayRecording();
void OnStartRecording();
void OnStopRecording();
@@ -193,7 +190,6 @@ class MainWindow final : public QMainWindow
std::unique_ptr<X11Utils::XRRConfiguration> m_xrr_config;
#endif

QProgressDialog* m_progress_dialog = nullptr;
QStackedWidget* m_stack;
ToolBar* m_tool_bar;
MenuBar* m_menu_bar;
@@ -14,7 +14,6 @@
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QProgressDialog>
#include <QPushButton>
#include <QSignalBlocker>
#include <QSpinBox>
@@ -0,0 +1,108 @@
#pragma once

#include <chrono>
#include <utility>

#include <QObject>
#include <QProgressDialog>
#include <QString>

#include "Common/Flag.h"

class ParallelProgressDialog final : public QObject
{
Q_OBJECT

public:
// Only use this from the main thread
template <typename... Args>
ParallelProgressDialog(Args&&... args) : m_dialog{std::forward<Args>(args)...}
{
setParent(m_dialog.parent());
m_dialog.setWindowFlags(m_dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
ConnectSignalsAndSlots();
}

// Only use this from the main thread
QProgressDialog* GetRaw() { return &m_dialog; }

// All of the following can be called from any thread
void Cancel() { emit CancelSignal(); }
void Reset() { emit ResetSignal(); }
void SetCancelButtonText(const QString& text) { emit SetCancelButtonText(text); }
void SetLabelText(const QString& text) { emit SetLabelTextSignal(text); }
void SetMaximum(int maximum) { emit SetMaximumSignal(maximum); }
void SetMinimum(int minimum) { emit SetMinimumSignal(minimum); }
void SetMinimumDuration(int ms) { emit SetMinimumDurationSignal(ms); }
void SetRange(int minimum, int maximum) { emit SetRangeSignal(minimum, maximum); }

// Can be called from any thread, but only from one thread at a time
void SetValue(int progress)
{
// HACK: To avoid the https://bugreports.qt.io/browse/QTBUG-10561 stack overflow,
// limit how often QProgressDialog::setValue can run
const auto current_time = std::chrono::steady_clock::now();
if (current_time - m_last_setvalue_time >= std::chrono::milliseconds(50))
{
m_last_setvalue_time = current_time;
emit SetValueSignal(progress);
}
}

// Can be called from any thread
bool WasCanceled() { return m_was_cancelled.IsSet(); }

signals:
void CancelSignal();
void ResetSignal();
void SetCancelButtonTextSignal(const QString& cancel_button_text);
void SetLabelTextSignal(const QString& text);
void SetMaximumSignal(int maximum);
void SetMinimumSignal(int minimum);
void SetMinimumDurationSignal(int ms);
void SetRangeSignal(int minimum, int maximum);
void SetValueSignal(int progress);

void Canceled();
void Finished(int result);

private slots:
void OnCancelled() { m_was_cancelled.Set(); }

private:
template <typename Func1, typename Func2>
void ConnectSignal(Func1 signal, Func2 slot)
{
QObject::connect(this, signal, &m_dialog, slot);
}

template <typename Func1, typename Func2>
void ConnectSlot(Func1 signal, Func2 slot)
{
QObject::connect(&m_dialog, signal, this, slot);
}

void ConnectSignalsAndSlots()
{
ConnectSignal(&ParallelProgressDialog::CancelSignal, &QProgressDialog::cancel);
ConnectSignal(&ParallelProgressDialog::ResetSignal, &QProgressDialog::reset);
ConnectSignal(&ParallelProgressDialog::SetCancelButtonTextSignal,
&QProgressDialog::setCancelButtonText);
ConnectSignal(&ParallelProgressDialog::SetLabelTextSignal, &QProgressDialog::setLabelText);
ConnectSignal(&ParallelProgressDialog::SetMaximumSignal, &QProgressDialog::setMaximum);
ConnectSignal(&ParallelProgressDialog::SetMinimumSignal, &QProgressDialog::setMinimum);
ConnectSignal(&ParallelProgressDialog::SetMinimumDurationSignal,
&QProgressDialog::setMinimumDuration);
ConnectSignal(&ParallelProgressDialog::SetRangeSignal, &QProgressDialog::setRange);
ConnectSignal(&ParallelProgressDialog::SetValueSignal, &QProgressDialog::setValue);

ConnectSlot(&QProgressDialog::canceled, &ParallelProgressDialog::OnCancelled);

ConnectSlot(&QProgressDialog::canceled, &ParallelProgressDialog::Canceled);
ConnectSlot(&QProgressDialog::finished, &ParallelProgressDialog::Finished);
}

QProgressDialog m_dialog;
Common::Flag m_was_cancelled;
std::chrono::time_point<std::chrono::steady_clock> m_last_setvalue_time;
};
@@ -45,9 +45,6 @@ bool Host_RendererIsFullscreen()
void Host_YieldToUI()
{
}
void Host_UpdateProgressDialog(const char* caption, int position, int total)
{
}
void Host_TitleChanged()
{
}
@@ -46,9 +46,6 @@ bool Host_RendererIsFullscreen()
void Host_YieldToUI()
{
}
void Host_UpdateProgressDialog(const char* caption, int position, int total)
{
}
void Host_TitleChanged()
{
}