diff options
Diffstat (limited to 'launcher/ui/dialogs')
-rw-r--r-- | launcher/ui/dialogs/ExportInstanceDialog.cpp | 304 | ||||
-rw-r--r-- | launcher/ui/dialogs/ExportInstanceDialog.h | 46 | ||||
-rw-r--r-- | launcher/ui/dialogs/ExportMrPackDialog.cpp | 123 | ||||
-rw-r--r-- | launcher/ui/dialogs/ExportMrPackDialog.h | 45 | ||||
-rw-r--r-- | launcher/ui/dialogs/ExportMrPackDialog.ui | 136 | ||||
-rw-r--r-- | launcher/ui/dialogs/NewInstanceDialog.cpp | 2 | ||||
-rw-r--r-- | launcher/ui/dialogs/ProgressDialog.cpp | 136 | ||||
-rw-r--r-- | launcher/ui/dialogs/ProgressDialog.h | 54 | ||||
-rw-r--r-- | launcher/ui/dialogs/ProgressDialog.ui | 150 | ||||
-rw-r--r-- | launcher/ui/dialogs/ResourceDownloadDialog.cpp | 90 | ||||
-rw-r--r-- | launcher/ui/dialogs/ResourceDownloadDialog.h | 17 |
11 files changed, 655 insertions, 448 deletions
diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index 07ec3c70..8ecd91a9 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,301 +47,21 @@ #include <QSaveFile> #include <QStack> #include <QFileInfo> - -#include "StringUtils.h" #include "SeparatorPrefixTree.h" #include "Application.h" #include <icons/IconList.h> #include <FileSystem.h> -class PackIgnoreProxy : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - PackIgnoreProxy(InstancePtr instance, QObject *parent) : QSortFilterProxyModel(parent) - { - m_instance = instance; - } - // NOTE: Sadly, we have to do sorting ourselves. - bool lessThan(const QModelIndex &left, const QModelIndex &right) const - { - QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel()); - if (!fsm) - { - return QSortFilterProxyModel::lessThan(left, right); - } - bool asc = sortOrder() == Qt::AscendingOrder ? true : false; - - QFileInfo leftFileInfo = fsm->fileInfo(left); - QFileInfo rightFileInfo = fsm->fileInfo(right); - - if (!leftFileInfo.isDir() && rightFileInfo.isDir()) - { - return !asc; - } - if (leftFileInfo.isDir() && !rightFileInfo.isDir()) - { - return asc; - } - - // sort and proxy model breaks the original model... - if (sortColumn() == 0) - { - return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), - Qt::CaseInsensitive) < 0; - } - if (sortColumn() == 1) - { - auto leftSize = leftFileInfo.size(); - auto rightSize = rightFileInfo.size(); - if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir())) - { - return StringUtils::naturalCompare(leftFileInfo.fileName(), - rightFileInfo.fileName(), - Qt::CaseInsensitive) < 0 - ? asc - : !asc; - } - return leftSize < rightSize; - } - return QSortFilterProxyModel::lessThan(left, right); - } - - virtual Qt::ItemFlags flags(const QModelIndex &index) const - { - if (!index.isValid()) - return Qt::NoItemFlags; - - auto sourceIndex = mapToSource(index); - Qt::ItemFlags flags = sourceIndex.flags(); - if (index.column() == 0) - { - flags |= Qt::ItemIsUserCheckable; - if (sourceIndex.model()->hasChildren(sourceIndex)) - { - flags |= Qt::ItemIsAutoTristate; - } - } - - return flags; - } - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const - { - QModelIndex sourceIndex = mapToSource(index); - - if (index.column() == 0 && role == Qt::CheckStateRole) - { - QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel()); - auto blockedPath = relPath(fsm->filePath(sourceIndex)); - auto cover = blocked.cover(blockedPath); - if (!cover.isNull()) - { - return QVariant(Qt::Unchecked); - } - else if (blocked.exists(blockedPath)) - { - return QVariant(Qt::PartiallyChecked); - } - else - { - return QVariant(Qt::Checked); - } - } - - return sourceIndex.data(role); - } - - virtual bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole) - { - if (index.column() == 0 && role == Qt::CheckStateRole) - { - Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt()); - return setFilterState(index, state); - } - - QModelIndex sourceIndex = mapToSource(index); - return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role); - } - - QString relPath(const QString &path) const - { - QString prefix = QDir().absoluteFilePath(m_instance->instanceRoot()); - prefix += '/'; - if (!path.startsWith(prefix)) - { - return QString(); - } - return path.mid(prefix.size()); - } - - bool setFilterState(QModelIndex index, Qt::CheckState state) - { - QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel()); - - if (!fsm) - { - return false; - } - - QModelIndex sourceIndex = mapToSource(index); - auto blockedPath = relPath(fsm->filePath(sourceIndex)); - bool changed = false; - if (state == Qt::Unchecked) - { - // blocking a path - auto &node = blocked.insert(blockedPath); - // get rid of all blocked nodes below - node.clear(); - changed = true; - } - else if (state == Qt::Checked || state == Qt::PartiallyChecked) - { - if (!blocked.remove(blockedPath)) - { - auto cover = blocked.cover(blockedPath); - qDebug() << "Blocked by cover" << cover; - // uncover - blocked.remove(cover); - // block all contents, except for any cover - QModelIndex rootIndex = - fsm->index(FS::PathCombine(m_instance->instanceRoot(), cover)); - QModelIndex doing = rootIndex; - int row = 0; - QStack<QModelIndex> todo; - while (1) - { - auto node = fsm->index(row, 0, doing); - if (!node.isValid()) - { - if (!todo.size()) - { - break; - } - else - { - doing = todo.pop(); - row = 0; - continue; - } - } - auto relpath = relPath(fsm->filePath(node)); - if (blockedPath.startsWith(relpath)) // cover found? - { - // continue processing cover later - todo.push(node); - } - else - { - // or just block this one. - blocked.insert(relpath); - } - row++; - } - } - changed = true; - } - if (changed) - { - // update the thing - emit dataChanged(index, index, {Qt::CheckStateRole}); - // update everything above index - QModelIndex up = index.parent(); - while (1) - { - if (!up.isValid()) - break; - emit dataChanged(up, up, {Qt::CheckStateRole}); - up = up.parent(); - } - // and everything below the index - QModelIndex doing = index; - int row = 0; - QStack<QModelIndex> todo; - while (1) - { - auto node = this->index(row, 0, doing); - if (!node.isValid()) - { - if (!todo.size()) - { - break; - } - else - { - doing = todo.pop(); - row = 0; - continue; - } - } - emit dataChanged(node, node, {Qt::CheckStateRole}); - todo.push(node); - row++; - } - // siblings and unrelated nodes are ignored - } - return true; - } - - bool shouldExpand(QModelIndex index) - { - QModelIndex sourceIndex = mapToSource(index); - QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel()); - if (!fsm) - { - return false; - } - auto blockedPath = relPath(fsm->filePath(sourceIndex)); - auto found = blocked.find(blockedPath); - if(found) - { - return !found->leaf(); - } - return false; - } - - void setBlockedPaths(QStringList paths) - { - beginResetModel(); - blocked.clear(); - blocked.insert(paths); - endResetModel(); - } - - const SeparatorPrefixTree<'/'> & blockedPaths() const - { - return blocked; - } - -protected: - bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const - { - Q_UNUSED(source_parent) - - // adjust the columns you want to filter out here - // return false for those that will be hidden - if (source_column == 2 || source_column == 3) - return false; - - return true; - } - -private: - InstancePtr m_instance; - SeparatorPrefixTree<'/'> blocked; -}; - ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget *parent) : QDialog(parent), ui(new Ui::ExportInstanceDialog), m_instance(instance) { ui->setupUi(this); auto model = new QFileSystemModel(this); - proxyModel = new PackIgnoreProxy(m_instance, this); + model->setIconProvider(&icons); + auto root = instance->instanceRoot(); + proxyModel = new FileIgnoreProxy(root, this); loadPackIgnore(); proxyModel->setSourceModel(model); - auto root = instance->instanceRoot(); ui->treeView->setModel(proxyModel); ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root))); ui->treeView->sortByColumn(0, Qt::AscendingOrder); @@ -404,22 +125,11 @@ bool ExportInstanceDialog::doExport() const QString output = QFileDialog::getSaveFileName( this, tr("Export %1").arg(m_instance->name()), - FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr, QFileDialog::DontConfirmOverwrite); + FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr); if (output.isEmpty()) { return false; } - if (QFile::exists(output)) - { - int ret = - QMessageBox::question(this, tr("Overwrite?"), - tr("This file already exists. Do you want to overwrite it?"), - QMessageBox::No, QMessageBox::Yes); - if (ret == QMessageBox::No) - { - return false; - } - } SaveIcon(m_instance); @@ -511,5 +221,3 @@ void ExportInstanceDialog::savePackIgnore() qWarning() << e.cause(); } } - -#include "ExportInstanceDialog.moc" diff --git a/launcher/ui/dialogs/ExportInstanceDialog.h b/launcher/ui/dialogs/ExportInstanceDialog.h index dea02d1b..5e801875 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.h +++ b/launcher/ui/dialogs/ExportInstanceDialog.h @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #pragma once @@ -18,9 +38,10 @@ #include <QDialog> #include <QModelIndex> #include <memory> +#include "FileIgnoreProxy.h" +#include "FastFileIconProvider.h" class BaseInstance; -class PackIgnoreProxy; typedef std::shared_ptr<BaseInstance> InstancePtr; namespace Ui @@ -47,7 +68,8 @@ private: private: Ui::ExportInstanceDialog *ui; InstancePtr m_instance; - PackIgnoreProxy * proxyModel; + FileIgnoreProxy * proxyModel; + FastFileIconProvider icons; private slots: void rowsInserted(QModelIndex parent, int top, int bottom); diff --git a/launcher/ui/dialogs/ExportMrPackDialog.cpp b/launcher/ui/dialogs/ExportMrPackDialog.cpp new file mode 100644 index 00000000..239873f6 --- /dev/null +++ b/launcher/ui/dialogs/ExportMrPackDialog.cpp @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "ExportMrPackDialog.h" +#include "minecraft/mod/ModFolderModel.h" +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui_ExportMrPackDialog.h" + +#include <QFileDialog> +#include <QFileSystemModel> +#include <QJsonDocument> +#include <QMessageBox> +#include <QPushButton> +#include "FastFileIconProvider.h" +#include "FileSystem.h" +#include "MMCZip.h" +#include "modplatform/modrinth/ModrinthPackExportTask.h" + +ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent) + : QDialog(parent), instance(instance), ui(new Ui::ExportMrPackDialog) +{ + ui->setupUi(this); + ui->name->setText(instance->name()); + ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]); + + // ensure a valid pack is generated + // the name and version fields mustn't be empty + connect(ui->name, &QLineEdit::textEdited, this, &ExportMrPackDialog::validate); + connect(ui->version, &QLineEdit::textEdited, this, &ExportMrPackDialog::validate); + // the instance name can technically be empty + validate(); + + QFileSystemModel* model = new QFileSystemModel(this); + model->setIconProvider(&icons); + + // use the game root - everything outside cannot be exported + const QDir root(instance->gameRoot()); + proxy = new FileIgnoreProxy(instance->gameRoot(), this); + proxy->setSourceModel(model); + + const QDir::Filters filter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Hidden); + + for (const QString& file : root.entryList(filter)) { + if (!(file == "mods" || file == "coremods" || file == "datapacks" || file == "config" || file == "options.txt" || + file == "servers.dat")) + proxy->blockedPaths().insert(file); + } + + MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get()); + if (mcInstance) { + const QDir index = mcInstance->loaderModList()->indexDir(); + if (index.exists()) + proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath())); + } + + ui->treeView->setModel(proxy); + ui->treeView->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); + ui->treeView->sortByColumn(0, Qt::AscendingOrder); + + model->setFilter(filter); + model->setRootPath(instance->gameRoot()); + + QHeaderView* headerView = ui->treeView->header(); + headerView->setSectionResizeMode(QHeaderView::ResizeToContents); + headerView->setSectionResizeMode(0, QHeaderView::Stretch); +} + +ExportMrPackDialog::~ExportMrPackDialog() +{ + delete ui; +} + +void ExportMrPackDialog::done(int result) +{ + if (result == Accepted) { + const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text()); + const QString output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()), + FS::PathCombine(QDir::homePath(), filename + ".mrpack"), + "Modrinth pack (*.mrpack *.zip)", nullptr); + + if (output.isEmpty()) + return; + + ModrinthPackExportTask task(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output, + [this](const QString& path) { return proxy->blockedPaths().covers(path); }); + + connect(&task, &Task::failed, + [this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(&task, &Task::aborted, [this] { + CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information) + ->show(); + }); + + ProgressDialog progress(this); + progress.setSkipButton(true, tr("Abort")); + if (progress.execWithTask(&task) != QDialog::Accepted) + return; + } + + QDialog::done(result); +} + +void ExportMrPackDialog::validate() +{ + const bool invalid = ui->name->text().isEmpty() || ui->version->text().isEmpty(); + ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid); +} diff --git a/launcher/ui/dialogs/ExportMrPackDialog.h b/launcher/ui/dialogs/ExportMrPackDialog.h new file mode 100644 index 00000000..1c70c4ae --- /dev/null +++ b/launcher/ui/dialogs/ExportMrPackDialog.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <QDialog> +#include "BaseInstance.h" +#include "FastFileIconProvider.h" +#include "FileIgnoreProxy.h" + +namespace Ui { +class ExportMrPackDialog; +} + +class ExportMrPackDialog : public QDialog { + Q_OBJECT + + public: + explicit ExportMrPackDialog(InstancePtr instance, QWidget* parent = nullptr); + ~ExportMrPackDialog(); + + void done(int result) override; + void validate(); + + private: + const InstancePtr instance; + Ui::ExportMrPackDialog* ui; + FileIgnoreProxy* proxy; + FastFileIconProvider icons; +}; diff --git a/launcher/ui/dialogs/ExportMrPackDialog.ui b/launcher/ui/dialogs/ExportMrPackDialog.ui new file mode 100644 index 00000000..9a789737 --- /dev/null +++ b/launcher/ui/dialogs/ExportMrPackDialog.ui @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ExportMrPackDialog</class> + <widget class="QDialog" name="ExportMrPackDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>650</width> + <height>413</height> + </rect> + </property> + <property name="windowTitle"> + <string>Export Modrinth Pack</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="information"> + <property name="title"> + <string>Information</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="3" column="0"> + <widget class="QLabel" name="versionLabel"> + <property name="text"> + <string>Summary</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="summary"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Name</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="summaryLabel"> + <property name="text"> + <string>Version</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="name"/> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="version"> + <property name="text"> + <string>1.0.0</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="filesLabel"> + <property name="text"> + <string>Files</string> + </property> + </widget> + </item> + <item> + <widget class="QTreeView" name="treeView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <attribute name="headerStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>name</tabstop> + <tabstop>version</tabstop> + <tabstop>summary</tabstop> + <tabstop>treeView</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ExportMrPackDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>324</x> + <y>390</y> + </hint> + <hint type="destinationlabel"> + <x>324</x> + <y>206</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ExportMrPackDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>324</x> + <y>390</y> + </hint> + <hint type="destinationlabel"> + <x>324</x> + <y>206</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 64ed7673..aafaf220 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -99,7 +99,7 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString // NOTE: m_buttons must be initialized before PageContainer, because it indirectly accesses m_buttons through setSuggestedPack! Do not move this below. m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_container = new PageContainer(this); + m_container = new PageContainer(this, {}, this); m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding); m_container->layout()->setContentsMargins(0, 0, 0, 0); ui->verticalLayout->insertWidget(2, m_container); diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index da73a449..246a0fd4 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -1,29 +1,69 @@ -/* Copyright 2013-2021 MultiMC Contributors +/// SPDX-License-Identifier: GPL-3.0-only +/* + * PrismLaucher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #include "ProgressDialog.h" #include "ui_ProgressDialog.h" +#include <limits> #include <QDebug> #include <QKeyEvent> #include "tasks/Task.h" +#include "ui/widgets/SubTaskProgressBar.h" + + +// map a value in a numeric range of an arbitrary type to between 0 and INT_MAX +// for getting the best precision out of the qt progress bar +template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true> +std::tuple<int, int> map_int_zero_max(T current, T range_max, T range_min) +{ + int int_max = std::numeric_limits<int>::max(); + + auto type_range = range_max - range_min; + double percentage = static_cast<double>(current - range_min) / static_cast<double>(type_range); + int mapped_current = percentage * int_max; + + return {mapped_current, int_max}; +} + + ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog) { ui->setupUi(this); + ui->taskProgressScrollArea->setHidden(true); this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true); setSkipButton(false); @@ -54,10 +94,24 @@ ProgressDialog::~ProgressDialog() } void ProgressDialog::updateSize() -{ +{ + QSize lastSize = this->size(); QSize qSize = QSize(480, minimumSizeHint().height()); - resize(qSize); - setFixedSize(qSize); + + // if the current window is too small + if ((lastSize != qSize) && (lastSize.height() < qSize.height())) + { + resize(qSize); + + // keep the dialog in the center after a resize + this->move( + this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2, + this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2 + ); + } + + setMinimumSize(qSize); + } int ProgressDialog::execWithTask(Task* task) @@ -79,17 +133,15 @@ int ProgressDialog::execWithTask(Task* task) connect(task, &Task::failed, this, &ProgressDialog::onTaskFailed); connect(task, &Task::succeeded, this, &ProgressDialog::onTaskSucceeded); connect(task, &Task::status, this, &ProgressDialog::changeStatus); - connect(task, &Task::stepStatus, this, &ProgressDialog::changeStatus); + connect(task, &Task::details, this, &ProgressDialog::changeStatus); + connect(task, &Task::stepProgress, this, &ProgressDialog::changeStepProgress); connect(task, &Task::progress, this, &ProgressDialog::changeProgress); - connect(task, &Task::aborted, this, &ProgressDialog::hide); connect(task, &Task::abortStatusChanged, ui->skipButton, &QPushButton::setEnabled); m_is_multi_step = task->isMultiStep(); - if (!m_is_multi_step) { - ui->globalStatusLabel->setHidden(true); - ui->globalProgressBar->setHidden(true); - } + ui->taskProgressScrollArea->setHidden(!m_is_multi_step); + updateSize(); // It's a good idea to start the task after we entered the dialog's event loop :^) if (!task->isRunning()) { @@ -149,23 +201,53 @@ void ProgressDialog::onTaskSucceeded() void ProgressDialog::changeStatus(const QString& status) { ui->globalStatusLabel->setText(task->getStatus()); - ui->statusLabel->setText(task->getStepStatus()); + ui->globalStatusDetailsLabel->setText(task->getDetails()); updateSize(); } +void ProgressDialog::addTaskProgress(TaskStepProgress const& progress) +{ + SubTaskProgressBar* task_bar = new SubTaskProgressBar(this); + taskProgress.insert(progress.uid, task_bar); + ui->taskProgressLayout->addWidget(task_bar); +} + +void ProgressDialog::changeStepProgress(TaskStepProgress const& task_progress) +{ + m_is_multi_step = true; + if(ui->taskProgressScrollArea->isHidden()) { + ui->taskProgressScrollArea->setHidden(false); + updateSize(); + } + + if (!taskProgress.contains(task_progress.uid)) + addTaskProgress(task_progress); + auto task_bar = taskProgress.value(task_progress.uid); + + + auto const [mapped_current, mapped_total] = map_int_zero_max<qint64>(task_progress.current, task_progress.total, 0); + if (task_progress.total <= 0) { + task_bar->setRange(0, 0); + } else { + task_bar->setRange(0, mapped_total); + } + + task_bar->setValue(mapped_current); + task_bar->setStatus(task_progress.status); + task_bar->setDetails(task_progress.details); + + if (task_progress.isDone()) { + task_bar->setVisible(false); + } + +} + 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) diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h index 0b4b78a4..fc9a0fbc 100644 --- a/launcher/ui/dialogs/ProgressDialog.h +++ b/launcher/ui/dialogs/ProgressDialog.h @@ -1,22 +1,50 @@ -/* Copyright 2013-2021 MultiMC Contributors +/// SPDX-License-Identifier: GPL-3.0-only +/* + * PrismLaucher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + #pragma once #include <QDialog> #include <memory> +#include <QHash> +#include <QUuid> + +#include "QObjectPtr.h" +#include "tasks/Task.h" + +#include "ui/widgets/SubTaskProgressBar.h" class Task; class SequentialTask; @@ -52,6 +80,7 @@ slots: void changeStatus(const QString &status); void changeProgress(qint64 current, qint64 total); + void changeStepProgress(TaskStepProgress const& task_progress); private @@ -64,6 +93,7 @@ protected: private: bool handleImmediateResult(QDialog::DialogCode &result); + void addTaskProgress(TaskStepProgress const& progress); private: Ui::ProgressDialog *ui; @@ -71,4 +101,8 @@ private: Task *task; bool m_is_multi_step = false; + QHash<QUuid, SubTaskProgressBar*> taskProgress; + + }; + diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui index 34ab71e3..a4d08124 100644 --- a/launcher/ui/dialogs/ProgressDialog.ui +++ b/launcher/ui/dialogs/ProgressDialog.ui @@ -2,75 +2,135 @@ <ui version="4.0"> <class>ProgressDialog</class> <widget class="QDialog" name="ProgressDialog"> - <property name="minimumSize"> - <size> - <width>400</width> - <height>0</height> - </size> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>210</height> + </rect> </property> - <property name="maximumSize"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> <size> - <width>600</width> - <height>16777215</height> + <width>480</width> + <height>210</height> </size> </property> <property name="windowTitle"> <string>Please wait...</string> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="4" column="0"> - <widget class="QPushButton" name="skipButton"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> + <item> + <widget class="QLabel" name="globalStatusLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>15</height> + </size> + </property> + <property name="text"> + <string>Global Task Status...</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="globalStatusDetailsLabel"> + <property name="text"> + <string>Global Status Details...</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QProgressBar" name="globalProgressBar"> + <property name="enabled"> + <bool>true</bool> </property> - <property name="text"> - <string>Skip</string> + <property name="minimumSize"> + <size> + <width>0</width> + <height>24</height> + </size> </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="globalStatusLabel"> - <property name="text"> - <string>Global Task Status...</string> + <property name="value"> + <number>24</number> </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="statusLabel"> + <item> + <widget class="QScrollArea" name="taskProgressScrollArea"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>Task Status...</string> + <property name="minimumSize"> + <size> + <width>0</width> + <height>100</height> + </size> </property> - <property name="wordWrap"> - <bool>true</bool> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QProgressBar" name="taskProgressBar"> - <property name="value"> - <number>24</number> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAsNeeded</enum> + </property> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustToContents</enum> </property> - <property name="textVisible"> - <bool>false</bool> + <property name="widgetResizable"> + <bool>true</bool> </property> + <widget class="QWidget" name="taskProgressContainer"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>464</width> + <height>96</height> + </rect> + </property> + <layout class="QVBoxLayout" name="taskProgressLayout"> + <property name="spacing"> + <number>2</number> + </property> + </layout> + </widget> </widget> </item> - <item row="1" column="0"> - <widget class="QProgressBar" name="globalProgressBar"> - <property name="enabled"> - <bool>true</bool> + <item> + <widget class="QPushButton" name="skipButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="value"> - <number>24</number> + <property name="text"> + <string>Skip</string> </property> </widget> </item> diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index edb7d063..6d90480f 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -20,14 +20,15 @@ #include "ResourceDownloadDialog.h" #include <QPushButton> +#include <algorithm> #include "Application.h" #include "ResourceDownloadTask.h" #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" -#include "minecraft/mod/TexturePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" +#include "minecraft/mod/TexturePackFolderModel.h" #include "ui/dialogs/ReviewMessageBox.h" @@ -41,7 +42,10 @@ namespace ResourceDownload { ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent, const std::shared_ptr<ResourceFolderModel> base_model) - : QDialog(parent), m_base_model(base_model), m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel), m_vertical_layout(this) + : QDialog(parent) + , m_base_model(base_model) + , m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel) + , m_vertical_layout(this) { setObjectName(QStringLiteral("ResourceDownloadDialog")); @@ -89,7 +93,7 @@ void ResourceDownloadDialog::reject() // won't work with subclasses if we put it in this ctor. void ResourceDownloadDialog::initializeContainer() { - m_container = new PageContainer(this); + m_container = new PageContainer(this, {}, this); m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding); m_container->layout()->setContentsMargins(0, 0, 0, 0); m_vertical_layout.addWidget(m_container); @@ -102,7 +106,8 @@ void ResourceDownloadDialog::initializeContainer() void ResourceDownloadDialog::connectButtons() { auto OkButton = m_buttons.button(QDialogButtonBox::Ok); - OkButton->setToolTip(tr("Opens a new popup to review your selected %1 and confirm your selection. Shortcut: Ctrl+Return").arg(resourcesString())); + OkButton->setToolTip( + tr("Opens a new popup to review your selected %1 and confirm your selection. Shortcut: Ctrl+Return").arg(resourcesString())); connect(OkButton, &QPushButton::clicked, this, &ResourceDownloadDialog::confirm); auto CancelButton = m_buttons.button(QDialogButtonBox::Cancel); @@ -114,21 +119,24 @@ void ResourceDownloadDialog::connectButtons() void ResourceDownloadDialog::confirm() { - auto keys = m_selected.keys(); - keys.sort(Qt::CaseInsensitive); + auto selected = getTasks(); + std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) { + return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0; + }); auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); confirm_dialog->retranslateUi(resourcesString()); - for (auto& task : keys) { - auto selected = m_selected.constFind(task).value(); - confirm_dialog->appendResource({ task, selected->getFilename(), selected->getCustomPath() }); + for (auto& task : selected) { + confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath() }); } if (confirm_dialog->exec()) { auto deselected = confirm_dialog->deselectedResources(); - for (auto name : deselected) { - m_selected.remove(name); + for (auto page : m_container->getPages()) { + auto res = static_cast<ResourcePage*>(page); + for (auto name : deselected) + res->removeResourceFromPage(name); } this->accept(); @@ -145,46 +153,39 @@ ResourcePage* ResourceDownloadDialog::getSelectedPage() return m_selectedPage; } -void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, bool is_indexed) +void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver) { - removeResource(pack, ver); - - ver.is_currently_selected = true; - m_selected.insert(pack.name, makeShared<ResourceDownloadTask>(pack, ver, getBaseModel(), is_indexed)); - - m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); + removeResource(pack->name); + m_selectedPage->addResourceToPage(pack, ver, getBaseModel()); + setButtonStatus(); } -static ModPlatform::IndexedVersion& getVersionWithID(ModPlatform::IndexedPack& pack, QVariant id) +void ResourceDownloadDialog::removeResource(const QString& pack_name) { - Q_ASSERT(pack.versionsLoaded); - auto it = std::find_if(pack.versions.begin(), pack.versions.end(), [id](auto const& v) { return v.fileId == id; }); - Q_ASSERT(it != pack.versions.end()); - return *it; + for (auto page : m_container->getPages()) { + static_cast<ResourcePage*>(page)->removeResourceFromPage(pack_name); + } + setButtonStatus(); } -void ResourceDownloadDialog::removeResource(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver) +void ResourceDownloadDialog::setButtonStatus() { - if (auto selected_task_it = m_selected.find(pack.name); selected_task_it != m_selected.end()) { - auto selected_task = *selected_task_it; - auto old_version_id = selected_task->getVersionID(); - - // If the new and old version IDs don't match, search for the old one and deselect it. - if (ver.fileId != old_version_id) - getVersionWithID(pack, old_version_id).is_currently_selected = false; + auto selected = false; + for (auto page : m_container->getPages()) { + auto res = static_cast<ResourcePage*>(page); + selected = selected || res->hasSelectedPacks(); } - - // Deselect the new version too, since all versions of that pack got removed. - ver.is_currently_selected = false; - - m_selected.remove(pack.name); - - m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); + m_buttons.button(QDialogButtonBox::Ok)->setEnabled(selected); } const QList<ResourceDownloadDialog::DownloadTaskPtr> ResourceDownloadDialog::getTasks() { - return m_selected.values(); + QList<DownloadTaskPtr> selected; + for (auto page : m_container->getPages()) { + auto res = static_cast<ResourcePage*>(page); + selected.append(res->selectedPacks()); + } + return selected; } void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* selected) @@ -205,8 +206,6 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); } - - ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr<ModFolderModel>& mods, BaseInstance* instance) : ResourceDownloadDialog(parent, mods), m_instance(instance) { @@ -232,7 +231,6 @@ QList<BasePage*> ModDownloadDialog::getPages() return pages; } - ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent, const std::shared_ptr<ResourcePackFolderModel>& resource_packs, BaseInstance* instance) @@ -255,10 +253,11 @@ QList<BasePage*> ResourcePackDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(FlameResourcePackPage::create(this, *m_instance)); + m_selectedPage = dynamic_cast<ResourcePackResourcePage*>(pages[0]); + return pages; } - TexturePackDownloadDialog::TexturePackDownloadDialog(QWidget* parent, const std::shared_ptr<TexturePackFolderModel>& resource_packs, BaseInstance* instance) @@ -281,10 +280,11 @@ QList<BasePage*> TexturePackDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(FlameTexturePackPage::create(this, *m_instance)); + m_selectedPage = dynamic_cast<TexturePackResourcePage*>(pages[0]); + return pages; } - ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent, const std::shared_ptr<ShaderPackFolderModel>& shaders, BaseInstance* instance) @@ -305,6 +305,8 @@ QList<BasePage*> ShaderPackDownloadDialog::getPages() pages.append(ModrinthShaderPackPage::create(this, *m_instance)); + m_selectedPage = dynamic_cast<ShaderPackResourcePage*>(pages[0]); + return pages; } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 5678dc8b..5b5b48c6 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -62,8 +62,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { bool selectPage(QString pageId); ResourcePage* getSelectedPage(); - void addResource(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&, bool is_indexed = false); - void removeResource(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); + void addResource(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); + void removeResource(const QString&); const QList<DownloadTaskPtr> getTasks(); [[nodiscard]] const std::shared_ptr<ResourceFolderModel> getBaseModel() const { return m_base_model; } @@ -79,6 +79,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { protected: [[nodiscard]] virtual QString geometrySaveKey() const { return ""; } + void setButtonStatus(); protected: const std::shared_ptr<ResourceFolderModel> m_base_model; @@ -88,12 +89,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { QDialogButtonBox m_buttons; QVBoxLayout m_vertical_layout; - - QHash<QString, DownloadTaskPtr> m_selected; }; - - class ModDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT @@ -135,8 +132,8 @@ class TexturePackDownloadDialog final : public ResourceDownloadDialog { public: explicit TexturePackDownloadDialog(QWidget* parent, - const std::shared_ptr<TexturePackFolderModel>& resource_packs, - BaseInstance* instance); + const std::shared_ptr<TexturePackFolderModel>& resource_packs, + BaseInstance* instance); ~TexturePackDownloadDialog() override = default; //: String that gets appended to the texture pack download dialog title ("Download " + resourcesString()) @@ -153,9 +150,7 @@ class ShaderPackDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT public: - explicit ShaderPackDownloadDialog(QWidget* parent, - const std::shared_ptr<ShaderPackFolderModel>& shader_packs, - BaseInstance* instance); + explicit ShaderPackDownloadDialog(QWidget* parent, const std::shared_ptr<ShaderPackFolderModel>& shader_packs, BaseInstance* instance); ~ShaderPackDownloadDialog() override = default; //: String that gets appended to the shader pack download dialog title ("Download " + resourcesString()) |