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.
This commit is contained in:
flow 2022-04-01 09:10:51 -03:00
parent e22d54abd3
commit 9b8493c304
No known key found for this signature in database
GPG Key ID: 8D0F221F0A59F469
7 changed files with 172 additions and 105 deletions

View File

@ -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,6 +57,8 @@ 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();
}
@ -42,14 +68,16 @@ void SequentialTask::subTaskFailed(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;
}

View File

@ -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<Task::Ptr > m_queue;
int m_currentIndex;
qint64 m_stepProgress = 0;
qint64 m_stepTotalProgress = 100;
};

View File

@ -21,29 +21,27 @@
#include "QObjectPtr.h"
class Task : public QObject
{
class Task : public QObject {
Q_OBJECT
public:
using Ptr = shared_qobject_ptr<Task>;
enum class State
{
Inactive,
Running,
Succeeded,
Failed,
AbortedByUser
};
enum class State { Inactive, Running, Succeeded, Failed, AbortedByUser };
public:
explicit Task(QObject* parent = 0);
virtual ~Task() {};
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,20 +52,13 @@ 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 getTotalProgress()
{
return m_progressTotal;
}
qint64 getProgress() { return m_progress; }
qint64 getTotalProgress() { return m_progressTotal; }
virtual auto getStepProgress() const -> qint64 { return 0; }
virtual auto getStepTotalProgress() const -> qint64 { return 100; }
protected:
void logWarning(const QString& line);
@ -77,7 +68,7 @@ private:
signals:
void started();
void progress(qint64 current, qint64 total);
virtual void progress(qint64 current, qint64 total);
void finished();
void succeeded();
void failed(QString reason);
@ -107,4 +98,3 @@ private:
int m_progress = 0;
int m_progressTotal = 100;
};

View File

@ -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,15 +158,25 @@ 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->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)
{

View File

@ -19,6 +19,7 @@
#include <memory>
class Task;
class SequentialTask;
namespace Ui
{
@ -68,4 +69,6 @@ private:
Ui::ProgressDialog *ui;
Task *task;
bool m_is_multi_step = false;
};

View File

@ -2,14 +2,6 @@
<ui version="4.0">
<class>ProgressDialog</class>
<widget class="QDialog" name="ProgressDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>100</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
@ -26,27 +18,7 @@
<string>Please wait...</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>Task Status...</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QProgressBar" name="taskProgressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="4" column="0">
<widget class="QPushButton" name="skipButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -59,6 +31,43 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="globalStatusLabel">
<property name="text">
<string>Global Task Status...</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>Task Status...</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QProgressBar" name="taskProgressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QProgressBar" name="globalProgressBar">
<property name="enabled">
<bool>true</bool>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -58,6 +58,7 @@
#include "Version.h"
#include "ui/dialogs/ProgressDialog.h"
#include "tasks/SequentialTask.h"
namespace {
// FIXME: wasteful
@ -395,26 +396,26 @@ void ModFolderPage::on_actionInstall_mods_triggered()
}
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();
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(task, &Task::succeeded, [this, task]() {
QStringList warnings = task->warnings();
if (warnings.count()) {
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'),
QMessageBox::Warning)->show();
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);
}
task->deleteLater();
});
ProgressDialog loadDialog(this);
loadDialog.setSkipButton(true, tr("Abort"));
loadDialog.execWithTask(task);
loadDialog.execWithTask(tasks);
m_mods->update();
}
}
}
void ModFolderPage::on_actionView_configs_triggered()
{