diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp index 1573e476c..ee57cac1b 100644 --- a/launcher/tasks/SequentialTask.cpp +++ b/launcher/tasks/SequentialTask.cpp @@ -33,11 +33,22 @@ void SequentialTask::executeTask() bool SequentialTask::abort() { - bool succeeded = true; - for (auto& task : m_queue) { - if (!task->abort()) succeeded = false; + if(m_currentIndex == -1 || m_currentIndex >= m_queue.size()) { + if(m_currentIndex == -1) { + // Don't call emitAborted() here, we want to bypass the 'is the task running' check + emit aborted(); + emit finished(); + } + m_queue.clear(); + return true; } + bool succeeded = m_queue[m_currentIndex]->abort(); + m_queue.clear(); + + if(succeeded) + emitAborted(); + return succeeded; } @@ -76,7 +87,7 @@ void SequentialTask::subTaskProgress(qint64 current, qint64 total) setProgress(0, 100); return; } - setProgress(m_currentIndex, m_queue.count()); + setProgress(m_currentIndex + 1, m_queue.count()); m_stepProgress = current; m_stepTotalProgress = total; diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index 305e85c06..f01c9c07f 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -77,18 +77,20 @@ void ModDownloadDialog::confirm() auto keys = modTask.keys(); keys.sort(Qt::CaseInsensitive); - auto confirm_dialog = ReviewMessageBox::create( - this, - tr("Confirm mods to download") - ); + auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm mods to download")); - for(auto& task : keys){ - confirm_dialog->appendMod(task, modTask.find(task).value()->getFilename()); + for (auto& task : keys) { + confirm_dialog->appendMod({ task, modTask.find(task).value()->getFilename() }); } - connect(confirm_dialog, &QDialog::accepted, this, &ModDownloadDialog::accept); + if (confirm_dialog->exec()) { + auto deselected = confirm_dialog->deselectedMods(); + for (auto name : deselected) { + modTask.remove(name); + } - confirm_dialog->open(); + this->accept(); + } } void ModDownloadDialog::accept() @@ -132,6 +134,12 @@ bool ModDownloadDialog::isModSelected(const QString &name, const QString& filena return iter != modTask.end() && (iter.value()->getFilename() == filename); } +bool ModDownloadDialog::isModSelected(const QString &name) const +{ + auto iter = modTask.find(name); + return iter != modTask.end(); +} + ModDownloadDialog::~ModDownloadDialog() { } diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index 782dc3619..5c565ad31 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -32,6 +32,7 @@ public: void addSelectedMod(const QString & name = QString(), ModDownloadTask * task = nullptr); void removeSelectedMod(const QString & name = QString()); bool isModSelected(const QString & name, const QString & filename) const; + bool isModSelected(const QString & name) const; const QList getTasks(); const std::shared_ptr &mods; @@ -41,8 +42,6 @@ public slots: void accept() override; void reject() override; -//private slots: - private: Ui::ModDownloadDialog *ui = nullptr; PageContainer * m_container = nullptr; diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 648bd88bb..e5226016e 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -16,12 +16,12 @@ #include "ProgressDialog.h" #include "ui_ProgressDialog.h" -#include #include +#include #include "tasks/Task.h" -ProgressDialog::ProgressDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ProgressDialog) +ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog) { ui->setupUi(this); this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -44,6 +44,7 @@ void ProgressDialog::on_skipButton_clicked(bool checked) { Q_UNUSED(checked); task->abort(); + QDialog::reject(); } ProgressDialog::~ProgressDialog() @@ -53,24 +54,22 @@ ProgressDialog::~ProgressDialog() void ProgressDialog::updateSize() { - QSize qSize = QSize(480, minimumSizeHint().height()); + QSize qSize = QSize(480, minimumSizeHint().height()); resize(qSize); - setFixedSize(qSize); + setFixedSize(qSize); } -int ProgressDialog::execWithTask(Task *task) +int ProgressDialog::execWithTask(Task* task) { this->task = task; QDialog::DialogCode result; - if(!task) - { + if (!task) { qDebug() << "Programmer error: progress dialog created with null task."; return Accepted; } - if(handleImmediateResult(result)) - { + if (handleImmediateResult(result)) { return result; } @@ -78,58 +77,51 @@ int ProgressDialog::execWithTask(Task *task) connect(task, SIGNAL(started()), SLOT(onTaskStarted())); connect(task, SIGNAL(failed(QString)), SLOT(onTaskFailed(QString))); connect(task, SIGNAL(succeeded()), SLOT(onTaskSucceeded())); - connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &))); + connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString&))); + connect(task, SIGNAL(stepStatus(QString)), SLOT(changeStatus(const QString&))); connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64))); + connect(task, &Task::aborted, [this] { onTaskFailed(tr("Aborted by user")); }); + m_is_multi_step = task->isMultiStep(); - if(!m_is_multi_step){ + 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()) - { + if (!task->isRunning()) { task->start(); } - if(task->isRunning()) - { + if (task->isRunning()) { changeProgress(task->getProgress(), task->getTotalProgress()); changeStatus(task->getStatus()); return QDialog::exec(); - } - else if(handleImmediateResult(result)) - { + } else if (handleImmediateResult(result)) { return result; - } - else - { + } else { return QDialog::Rejected; } } // TODO: only provide the unique_ptr overloads -int ProgressDialog::execWithTask(std::unique_ptr &&task) +int ProgressDialog::execWithTask(std::unique_ptr&& task) { connect(this, &ProgressDialog::destroyed, task.get(), &Task::deleteLater); return execWithTask(task.release()); } -int ProgressDialog::execWithTask(std::unique_ptr &task) +int ProgressDialog::execWithTask(std::unique_ptr& task) { connect(this, &ProgressDialog::destroyed, task.get(), &Task::deleteLater); return execWithTask(task.release()); } -bool ProgressDialog::handleImmediateResult(QDialog::DialogCode &result) +bool ProgressDialog::handleImmediateResult(QDialog::DialogCode& result) { - if(task->isFinished()) - { - if(task->wasSuccessful()) - { + if (task->isFinished()) { + if (task->wasSuccessful()) { result = QDialog::Accepted; - } - else - { + } else { result = QDialog::Rejected; } return true; @@ -137,14 +129,12 @@ bool ProgressDialog::handleImmediateResult(QDialog::DialogCode &result) return false; } -Task *ProgressDialog::getTask() +Task* ProgressDialog::getTask() { return task; } -void ProgressDialog::onTaskStarted() -{ -} +void ProgressDialog::onTaskStarted() {} void ProgressDialog::onTaskFailed(QString failure) { @@ -156,10 +146,11 @@ void ProgressDialog::onTaskSucceeded() accept(); } -void ProgressDialog::changeStatus(const QString &status) +void ProgressDialog::changeStatus(const QString& status) { + ui->globalStatusLabel->setText(task->getStatus()); ui->statusLabel->setText(task->getStepStatus()); - ui->globalStatusLabel->setText(status); + updateSize(); } @@ -168,27 +159,22 @@ void ProgressDialog::changeProgress(qint64 current, qint64 total) ui->globalProgressBar->setMaximum(total); ui->globalProgressBar->setValue(current); - if(!m_is_multi_step){ + if (!m_is_multi_step) { ui->taskProgressBar->setMaximum(total); ui->taskProgressBar->setValue(current); - } - else{ + } else { ui->taskProgressBar->setMaximum(task->getStepProgress()); ui->taskProgressBar->setValue(task->getStepTotalProgress()); } } -void ProgressDialog::keyPressEvent(QKeyEvent *e) +void ProgressDialog::keyPressEvent(QKeyEvent* e) { - if(ui->skipButton->isVisible()) - { - if (e->key() == Qt::Key_Escape) - { + if (ui->skipButton->isVisible()) { + if (e->key() == Qt::Key_Escape) { on_skipButton_clicked(true); return; - } - else if(e->key() == Qt::Key_Tab) - { + } else if (e->key() == Qt::Key_Tab) { ui->skipButton->setFocusPolicy(Qt::StrongFocus); ui->skipButton->setFocus(); ui->skipButton->setAutoDefault(true); @@ -199,14 +185,11 @@ void ProgressDialog::keyPressEvent(QKeyEvent *e) QDialog::keyPressEvent(e); } -void ProgressDialog::closeEvent(QCloseEvent *e) +void ProgressDialog::closeEvent(QCloseEvent* e) { - if (task && task->isRunning()) - { + if (task && task->isRunning()) { e->ignore(); - } - else - { + } else { QDialog::closeEvent(e); } } diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui index bf119a785..34ab71e32 100644 --- a/launcher/ui/dialogs/ProgressDialog.ui +++ b/launcher/ui/dialogs/ProgressDialog.ui @@ -40,6 +40,12 @@ + + + 0 + 0 + + Task Status... diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 2bfd02e0c..c92234a40 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -5,6 +5,9 @@ ReviewMessageBox::ReviewMessageBox(QWidget* parent, QString const& title, QStrin : QDialog(parent), ui(new Ui::ReviewMessageBox) { ui->setupUi(this); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &ReviewMessageBox::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &ReviewMessageBox::reject); } ReviewMessageBox::~ReviewMessageBox() @@ -17,15 +20,33 @@ auto ReviewMessageBox::create(QWidget* parent, QString&& title, QString&& icon) return new ReviewMessageBox(parent, title, icon); } -void ReviewMessageBox::appendMod(const QString& name, const QString& filename) +void ReviewMessageBox::appendMod(ModInformation&& info) { auto itemTop = new QTreeWidgetItem(ui->modTreeWidget); - itemTop->setText(0, name); + itemTop->setCheckState(0, Qt::CheckState::Checked); + itemTop->setText(0, info.name); auto filenameItem = new QTreeWidgetItem(itemTop); - filenameItem->setText(0, tr("Filename: %1").arg(filename)); + filenameItem->setText(0, tr("Filename: %1").arg(info.filename)); itemTop->insertChildren(0, { filenameItem }); ui->modTreeWidget->addTopLevelItem(itemTop); } + +auto ReviewMessageBox::deselectedMods() -> QStringList +{ + QStringList list; + + auto* item = ui->modTreeWidget->topLevelItem(0); + + for (int i = 0; item != nullptr; ++i) { + if (item->checkState(0) == Qt::CheckState::Unchecked) { + list.append(item->text(0)); + } + + item = ui->modTreeWidget->topLevelItem(i); + } + + return list; +} diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index 48742cd9f..9cfa679a5 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -6,17 +6,23 @@ namespace Ui { class ReviewMessageBox; } -class ReviewMessageBox final : public QDialog { +class ReviewMessageBox : public QDialog { Q_OBJECT public: static auto create(QWidget* parent, QString&& title, QString&& icon = "") -> ReviewMessageBox*; - void appendMod(const QString& name, const QString& filename); + using ModInformation = struct { + QString name; + QString filename; + }; + + void appendMod(ModInformation&& info); + auto deselectedMods() -> QStringList; ~ReviewMessageBox(); - private: + protected: ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon); Ui::ReviewMessageBox* ui; diff --git a/launcher/ui/dialogs/ReviewMessageBox.ui b/launcher/ui/dialogs/ReviewMessageBox.ui index d04f3b3f4..ab3bcc2fa 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.ui +++ b/launcher/ui/dialogs/ReviewMessageBox.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 300 + 500 + 350 @@ -20,24 +20,7 @@ true - - - - You're about to download the following mods: - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - true @@ -58,41 +41,33 @@ + + + + You're about to download the following mods: + + + + + + + + + Only mods with a check will be downloaded! + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + - - - buttonBox - accepted() - ReviewMessageBox - accept() - - - 200 - 265 - - - 199 - 149 - - - - - buttonBox - rejected() - ReviewMessageBox - reject() - - - 200 - 265 - - - 199 - 149 - - - - + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 5574f9d2f..b0cd405f0 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -402,6 +402,10 @@ void ModFolderPage::on_actionInstall_mods_triggered() CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->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(); } @@ -411,6 +415,7 @@ void ModFolderPage::on_actionInstall_mods_triggered() for (auto task : mdownload.getTasks()) { tasks->addTask(task); } + ProgressDialog loadDialog(this); loadDialog.setSkipButton(true, tr("Abort")); loadDialog.execWithTask(tasks); diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 9dd8f7379..1eb5837b7 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -38,27 +38,44 @@ auto ListModel::data(const QModelIndex& index, int role) const -> QVariant } ModPlatform::IndexedPack pack = modpacks.at(pos); - if (role == Qt::DisplayRole) { - return pack.name; - } else if (role == Qt::ToolTipRole) { - if (pack.description.length() > 100) { - // some magic to prevent to long tooltips and replace html linebreaks - QString edit = pack.description.left(97); - edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); - return edit; + switch (role) { + case Qt::DisplayRole: { + return pack.name; } - return pack.description; - } else if (role == Qt::DecorationRole) { - if (m_logoMap.contains(pack.logoName)) { - return (m_logoMap.value(pack.logoName)); + case Qt::ToolTipRole: { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; } - QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); - ((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl); - return icon; - } else if (role == Qt::UserRole) { - QVariant v; - v.setValue(pack); - return v; + case Qt::DecorationRole: { + if (m_logoMap.contains(pack.logoName)) { + return (m_logoMap.value(pack.logoName)); + } + QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); + // un-const-ify this + ((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl); + return icon; + } + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; + } + case Qt::FontRole: { + QFont font; + if (m_parent->getDialog()->isModSelected(pack.name)) { + font.setBold(true); + font.setUnderline(true); + } + + return font; + } + default: + break; } return {}; diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 0e658a8de..32affd206 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -41,6 +41,7 @@ class ModPage : public QWidget, public BasePage { auto apiProvider() const -> const ModAPI* { return api.get(); }; auto getFilter() const -> const std::shared_ptr { return m_filter; } + auto getDialog() const -> const ModDownloadDialog* { return dialog; } auto getCurrent() -> ModPlatform::IndexedPack& { return current; } void updateModVersions(int prev_count = -1);