From 9b8493c30499e06bbef7b96ff415f80c140c1a7f Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 1 Apr 2022 09:10:51 -0300 Subject: [PATCH] feat: Use a single progress dialog when doing multiple tasks This puts all mod downloading tasks inside a SequentialTask, which is, for more than one task, a multi step task. This is handled by the ProgressDialog by showing both the global progress of tasks executed, and the individual progress of each of them. --- launcher/tasks/SequentialTask.cpp | 50 ++++++++++---- launcher/tasks/SequentialTask.h | 28 ++++++-- launcher/tasks/Task.h | 68 +++++++++----------- launcher/ui/dialogs/ProgressDialog.cpp | 22 ++++++- launcher/ui/dialogs/ProgressDialog.h | 5 +- launcher/ui/dialogs/ProgressDialog.ui | 67 ++++++++++--------- launcher/ui/pages/instance/ModFolderPage.cpp | 37 +++++------ 7 files changed, 172 insertions(+), 105 deletions(-) diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp index a66b9d78..1573e476 100644 --- a/launcher/tasks/SequentialTask.cpp +++ b/launcher/tasks/SequentialTask.cpp @@ -1,7 +1,23 @@ #include "SequentialTask.h" -SequentialTask::SequentialTask(QObject *parent) : Task(parent), m_currentIndex(-1) +SequentialTask::SequentialTask(QObject* parent, const QString& task_name) : Task(parent), m_name(task_name), m_currentIndex(-1) {} + +SequentialTask::~SequentialTask() { + for(auto task : m_queue){ + if(task) + task->deleteLater(); + } +} + +auto SequentialTask::getStepProgress() const -> qint64 +{ + return m_stepProgress; +} + +auto SequentialTask::getStepTotalProgress() const -> qint64 +{ + return m_stepTotalProgress; } void SequentialTask::addTask(Task::Ptr task) @@ -15,16 +31,24 @@ void SequentialTask::executeTask() startNext(); } +bool SequentialTask::abort() +{ + bool succeeded = true; + for (auto& task : m_queue) { + if (!task->abort()) succeeded = false; + } + + return succeeded; +} + void SequentialTask::startNext() { - if (m_currentIndex != -1) - { + if (m_currentIndex != -1) { Task::Ptr previous = m_queue[m_currentIndex]; disconnect(previous.get(), 0, this, 0); } m_currentIndex++; - if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) - { + if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) { emitSucceeded(); return; } @@ -33,23 +57,27 @@ void SequentialTask::startNext() connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString))); connect(next.get(), SIGNAL(progress(qint64, qint64)), this, SLOT(subTaskProgress(qint64, qint64))); connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext())); + + setStatus(tr("Executing task %1 out of %2").arg(m_currentIndex + 1).arg(m_queue.size())); next->start(); } -void SequentialTask::subTaskFailed(const QString &msg) +void SequentialTask::subTaskFailed(const QString& msg) { emitFailed(msg); } -void SequentialTask::subTaskStatus(const QString &msg) +void SequentialTask::subTaskStatus(const QString& msg) { - setStatus(msg); + setStepStatus(m_queue[m_currentIndex]->getStatus()); } void SequentialTask::subTaskProgress(qint64 current, qint64 total) { - if(total == 0) - { + if (total == 0) { setProgress(0, 100); return; } - setProgress(current, total); + setProgress(m_currentIndex, m_queue.count()); + + m_stepProgress = current; + m_stepTotalProgress = total; } diff --git a/launcher/tasks/SequentialTask.h b/launcher/tasks/SequentialTask.h index 027744f3..5b3c0111 100644 --- a/launcher/tasks/SequentialTask.h +++ b/launcher/tasks/SequentialTask.h @@ -9,13 +9,21 @@ class SequentialTask : public Task { Q_OBJECT public: - explicit SequentialTask(QObject *parent = 0); - virtual ~SequentialTask() {}; + explicit SequentialTask(QObject *parent = nullptr, const QString& task_name = ""); + virtual ~SequentialTask(); + + inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; }; + auto getStepProgress() const -> qint64 override; + auto getStepTotalProgress() const -> qint64 override; + + inline auto getStepStatus() const -> QString override { return m_step_status; } void addTask(Task::Ptr task); -protected: - void executeTask(); +protected slots: + void executeTask() override; +public slots: + bool abort() override; private slots: @@ -24,7 +32,19 @@ slots: void subTaskStatus(const QString &msg); void subTaskProgress(qint64 current, qint64 total); +signals: + void stepStatus(QString status); + private: + void setStepStatus(QString status) { m_step_status = status; }; + +private: + QString m_name; + QString m_step_status; + QQueue m_queue; int m_currentIndex; + + qint64 m_stepProgress = 0; + qint64 m_stepTotalProgress = 100; }; diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 9cf08dbd..47c249b3 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -21,29 +21,27 @@ #include "QObjectPtr.h" -class Task : public QObject -{ +class Task : public QObject { Q_OBJECT -public: + public: using Ptr = shared_qobject_ptr; - enum class State - { - Inactive, - Running, - Succeeded, - Failed, - AbortedByUser - }; + enum class State { Inactive, Running, Succeeded, Failed, AbortedByUser }; -public: - explicit Task(QObject *parent = 0); - virtual ~Task() {}; + public: + explicit Task(QObject* parent = 0); + virtual ~Task() = default; bool isRunning() const; bool isFinished() const; bool wasSuccessful() const; + /*! + * MultiStep tasks are combinations of multiple tasks into a single logical task. + * The main usage of this is in SequencialTask. + */ + virtual auto isMultiStep() const -> bool { return false; } + /*! * Returns the string that was passed to emitFailed as the error message when the task failed. * If the task hasn't failed, returns an empty string. @@ -54,52 +52,45 @@ public: virtual bool canAbort() const { return false; } - QString getStatus() - { - return m_status; - } + QString getStatus() { return m_status; } + virtual auto getStepStatus() const -> QString { return {}; } - qint64 getProgress() - { - return m_progress; - } + qint64 getProgress() { return m_progress; } + qint64 getTotalProgress() { return m_progressTotal; } + virtual auto getStepProgress() const -> qint64 { return 0; } + virtual auto getStepTotalProgress() const -> qint64 { return 100; } - qint64 getTotalProgress() - { - return m_progressTotal; - } + protected: + void logWarning(const QString& line); -protected: - void logWarning(const QString & line); - -private: + private: QString describe(); -signals: + signals: void started(); - void progress(qint64 current, qint64 total); + virtual void progress(qint64 current, qint64 total); void finished(); void succeeded(); void failed(QString reason); void status(QString status); -public slots: + public slots: virtual void start(); virtual bool abort() { return false; }; -protected: + protected: virtual void executeTask() = 0; -protected slots: + protected slots: virtual void emitSucceeded(); virtual void emitAborted(); virtual void emitFailed(QString reason); -public slots: - void setStatus(const QString &status); + public slots: + void setStatus(const QString& status); void setProgress(qint64 current, qint64 total); -private: + private: State m_state = State::Inactive; QStringList m_Warnings; QString m_failReason = ""; @@ -107,4 +98,3 @@ private: int m_progress = 0; int m_progressTotal = 100; }; - diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 4b092859..648bd88b 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -81,6 +81,12 @@ int ProgressDialog::execWithTask(Task *task) connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &))); connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64))); + m_is_multi_step = task->isMultiStep(); + if(!m_is_multi_step){ + ui->globalStatusLabel->setHidden(true); + ui->globalProgressBar->setHidden(true); + } + // if this didn't connect to an already running task, invoke start if(!task->isRunning()) { @@ -152,14 +158,24 @@ void ProgressDialog::onTaskSucceeded() void ProgressDialog::changeStatus(const QString &status) { - ui->statusLabel->setText(status); + ui->statusLabel->setText(task->getStepStatus()); + ui->globalStatusLabel->setText(status); updateSize(); } void ProgressDialog::changeProgress(qint64 current, qint64 total) { - ui->taskProgressBar->setMaximum(total); - ui->taskProgressBar->setValue(current); + ui->globalProgressBar->setMaximum(total); + ui->globalProgressBar->setValue(current); + + if(!m_is_multi_step){ + ui->taskProgressBar->setMaximum(total); + ui->taskProgressBar->setValue(current); + } + else{ + ui->taskProgressBar->setMaximum(task->getStepProgress()); + ui->taskProgressBar->setValue(task->getStepTotalProgress()); + } } void ProgressDialog::keyPressEvent(QKeyEvent *e) diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h index b28ad4fa..0b4b78a4 100644 --- a/launcher/ui/dialogs/ProgressDialog.h +++ b/launcher/ui/dialogs/ProgressDialog.h @@ -19,6 +19,7 @@ #include class Task; +class SequentialTask; namespace Ui { @@ -35,7 +36,7 @@ public: void updateSize(); - int execWithTask(Task *task); + int execWithTask(Task* task); int execWithTask(std::unique_ptr &&task); int execWithTask(std::unique_ptr &task); @@ -68,4 +69,6 @@ private: Ui::ProgressDialog *ui; Task *task; + + bool m_is_multi_step = false; }; diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui index 04b8fef3..bf119a78 100644 --- a/launcher/ui/dialogs/ProgressDialog.ui +++ b/launcher/ui/dialogs/ProgressDialog.ui @@ -2,14 +2,6 @@ ProgressDialog - - - 0 - 0 - 400 - 100 - - 400 @@ -26,27 +18,7 @@ Please wait... - - - - Task Status... - - - true - - - - - - - 24 - - - false - - - - + @@ -59,6 +31,43 @@ + + + + Global Task Status... + + + + + + + Task Status... + + + true + + + + + + + 24 + + + false + + + + + + + true + + + 24 + + + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 599f0e11..fcb6022d 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -58,6 +58,7 @@ #include "Version.h" #include "ui/dialogs/ProgressDialog.h" +#include "tasks/SequentialTask.h" namespace { // FIXME: wasteful @@ -394,25 +395,25 @@ void ModFolderPage::on_actionInstall_mods_triggered() return; } ModDownloadDialog mdownload(m_mods, this, m_inst); - if(mdownload.exec()) { - for(auto task : mdownload.getTasks()){ - connect(task, &Task::failed, [this, task](QString reason) { - task->deleteLater(); - CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); - }); - connect(task, &Task::succeeded, [this, task]() { - QStringList warnings = task->warnings(); - if (warnings.count()) { - CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), - QMessageBox::Warning)->show(); - } - task->deleteLater(); - }); - ProgressDialog loadDialog(this); - loadDialog.setSkipButton(true, tr("Abort")); - loadDialog.execWithTask(task); - m_mods->update(); + if (mdownload.exec()) { + SequentialTask* tasks = new SequentialTask(this); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); } + tasks->deleteLater(); + }); + + for (auto task : mdownload.getTasks()) { + tasks->addTask(task); } + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + m_mods->update(); } }