diff --git a/launcher/ModDownloadTask.h b/launcher/ModDownloadTask.h index 7e4f1b7d9..ddada5a21 100644 --- a/launcher/ModDownloadTask.h +++ b/launcher/ModDownloadTask.h @@ -10,6 +10,7 @@ class ModDownloadTask : public Task { Q_OBJECT public: explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr mods); + const QString& getFilename() const { return filename; } public slots: bool abort() override; diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index 6b807b8c6..23ca87314 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -5,6 +5,7 @@ #include #include "ProgressDialog.h" +#include "CustomMessageBox.h" #include #include @@ -39,9 +40,10 @@ ModDownloadDialog::ModDownloadDialog(const std::shared_ptr &mods // Bonk Qt over its stupid head and make sure it understands which button is the default one... // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button auto OkButton = m_buttons->button(QDialogButtonBox::Ok); + OkButton->setEnabled(false); OkButton->setDefault(true); OkButton->setAutoDefault(true); - connect(OkButton, &QPushButton::clicked, this, &ModDownloadDialog::accept); + connect(OkButton, &QPushButton::clicked, this, &ModDownloadDialog::confirm); auto CancelButton = m_buttons->button(QDialogButtonBox::Cancel); CancelButton->setDefault(false); @@ -52,6 +54,7 @@ ModDownloadDialog::ModDownloadDialog(const std::shared_ptr &mods HelpButton->setDefault(false); HelpButton->setAutoDefault(false); connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); + QMetaObject::connectSlotsByName(this); setWindowModality(Qt::WindowModal); setWindowTitle("Download mods"); @@ -67,6 +70,36 @@ void ModDownloadDialog::reject() QDialog::reject(); } +void ModDownloadDialog::confirm() +{ + auto keys = modTask.keys(); + keys.sort(Qt::CaseInsensitive); + + auto info = QString(tr("You're about to download the following mods:")); + info.append("\n\n"); + for(auto task : keys){ + info.append(task); + info.append("\n --> "); + info.append(tr("File name: ")); + info.append(modTask.find(task).value()->getFilename()); + info.append('\n'); + } + + auto confirm_dialog = CustomMessageBox::selectable( + this, + tr("Confirm mods to download"), + info, + QMessageBox::NoIcon, + QMessageBox::Cancel | QMessageBox::Ok, + QMessageBox::Ok + ); + + auto AcceptButton = confirm_dialog->button(QMessageBox::Ok); + connect(AcceptButton, &QPushButton::clicked, this, &ModDownloadDialog::accept); + + confirm_dialog->open(); +} + void ModDownloadDialog::accept() { QDialog::accept(); @@ -83,16 +116,35 @@ QList ModDownloadDialog::getPages() }; } -void ModDownloadDialog::setSuggestedMod(const QString& name, ModDownloadTask* task) +void ModDownloadDialog::addSelectedMod(const QString& name, ModDownloadTask* task) { - modTask.reset(task); - m_buttons->button(QDialogButtonBox::Ok)->setEnabled(task); + removeSelectedMod(name); + modTask.insert(name, task); + + m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!modTask.isEmpty()); +} + +void ModDownloadDialog::removeSelectedMod(const QString &name) +{ + if(modTask.contains(name)) + delete modTask.find(name).value(); + modTask.remove(name); + + m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!modTask.isEmpty()); +} + +bool ModDownloadDialog::isModSelected(const QString &name, const QString& filename) const +{ + // FIXME: Is there a way to check for versions without checking the filename + // as a heuristic, other than adding such info to ModDownloadTask itself? + auto iter = modTask.find(name); + return iter != modTask.end() && (iter.value()->getFilename() == filename); } ModDownloadDialog::~ModDownloadDialog() { } -ModDownloadTask *ModDownloadDialog::getTask() { - return modTask.release(); +const QList ModDownloadDialog::getTasks() { + return modTask.values(); } diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index ece8e328f..309d89d06 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -29,12 +29,15 @@ public: QString dialogTitle() override; QList getPages() override; - void setSuggestedMod(const QString & name = QString(), ModDownloadTask * task = nullptr); + void addSelectedMod(const QString & name = QString(), ModDownloadTask * task = nullptr); + void removeSelectedMod(const QString & name = QString()); + bool isModSelected(const QString & name, const QString & filename) const; - ModDownloadTask * getTask(); + const QList getTasks(); const std::shared_ptr &mods; public slots: + void confirm(); void accept() override; void reject() override; @@ -49,6 +52,6 @@ private: ModrinthPage *modrinthPage = nullptr; FlameModPage *flameModPage = nullptr; - std::unique_ptr modTask; + QHash modTask; BaseInstance *m_instance; }; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 494d32f03..b342accfc 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -365,8 +365,7 @@ void ModFolderPage::on_actionInstall_mods_triggered() } ModDownloadDialog mdownload(m_mods, this, m_inst); if(mdownload.exec()) { - ModDownloadTask *task = mdownload.getTask(); - if (task) { + for(auto task : mdownload.getTasks()){ connect(task, &Task::failed, [this, task](QString reason) { task->deleteLater(); CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp index 4afdd1422..6d33a6ac0 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp @@ -4,196 +4,211 @@ #include #include "Application.h" -#include "Json.h" -#include "ui/dialogs/ModDownloadDialog.h" -#include "InstanceImportTask.h" #include "FlameModModel.h" +#include "InstanceImportTask.h" +#include "Json.h" #include "ModDownloadTask.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "ui/dialogs/ModDownloadDialog.h" FlameModPage::FlameModPage(ModDownloadDialog *dialog, BaseInstance *instance) - : QWidget(dialog), m_instance(instance), ui(new Ui::FlameModPage), dialog(dialog) -{ - ui->setupUi(this); - connect(ui->searchButton, &QPushButton::clicked, this, &FlameModPage::triggerSearch); - ui->searchEdit->installEventFilter(this); - listModel = new FlameMod::ListModel(this); - ui->packView->setModel(listModel); + : QWidget(dialog), m_instance(instance), ui(new Ui::FlameModPage), + dialog(dialog) { + ui->setupUi(this); + connect(ui->searchButton, &QPushButton::clicked, this, + &FlameModPage::triggerSearch); + ui->searchEdit->installEventFilter(this); + listModel = new FlameMod::ListModel(this); + ui->packView->setModel(listModel); - ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); + ui->versionSelectionBox->view()->setVerticalScrollBarPolicy( + Qt::ScrollBarAsNeeded); + ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); - // index is used to set the sorting with the flame api - ui->sortByBox->addItem(tr("Sort by Featured")); - ui->sortByBox->addItem(tr("Sort by Popularity")); - ui->sortByBox->addItem(tr("Sort by last updated")); - ui->sortByBox->addItem(tr("Sort by Name")); - ui->sortByBox->addItem(tr("Sort by Author")); - ui->sortByBox->addItem(tr("Sort by Downloads")); + // index is used to set the sorting with the flame api + ui->sortByBox->addItem(tr("Sort by Featured")); + ui->sortByBox->addItem(tr("Sort by Popularity")); + ui->sortByBox->addItem(tr("Sort by last updated")); + ui->sortByBox->addItem(tr("Sort by Name")); + ui->sortByBox->addItem(tr("Sort by Author")); + ui->sortByBox->addItem(tr("Sort by Downloads")); - connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); - connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged); - connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameModPage::onVersionSelectionChanged); + connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, + SLOT(triggerSearch())); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &FlameModPage::onSelectionChanged); + connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, + &FlameModPage::onVersionSelectionChanged); + connect(ui->modSelectionButton, &QPushButton::clicked, this, + &FlameModPage::onModSelected); } -FlameModPage::~FlameModPage() -{ - delete ui; -} +FlameModPage::~FlameModPage() { delete ui; } -bool FlameModPage::eventFilter(QObject* watched, QEvent* event) -{ - if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Return) { - triggerSearch(); - keyEvent->accept(); - return true; - } +bool FlameModPage::eventFilter(QObject *watched, QEvent *event) { + if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return) { + triggerSearch(); + keyEvent->accept(); + return true; } - return QWidget::eventFilter(watched, event); + } + return QWidget::eventFilter(watched, event); } -bool FlameModPage::shouldDisplay() const -{ - return true; +bool FlameModPage::shouldDisplay() const { return true; } + +void FlameModPage::openedImpl() { + updateSelectionButton(); + triggerSearch(); } -void FlameModPage::openedImpl() -{ - suggestCurrent(); - triggerSearch(); +void FlameModPage::triggerSearch() { + listModel->searchWithTerm(ui->searchEdit->text(), + ui->sortByBox->currentIndex()); } -void FlameModPage::triggerSearch() -{ - listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); -} +void FlameModPage::onSelectionChanged(QModelIndex first, QModelIndex second) { + ui->versionSelectionBox->clear(); -void FlameModPage::onSelectionChanged(QModelIndex first, QModelIndex second) -{ - ui->versionSelectionBox->clear(); + if (!first.isValid()) { + return; + } - if(!first.isValid()) - { - if(isOpened) - { - dialog->setSuggestedMod(); - } + current = listModel->data(first, Qt::UserRole).value(); + QString text = ""; + QString name = current.name; + + if (current.websiteUrl.isEmpty()) + text = name; + else + text = "" + name + ""; + if (!current.authors.empty()) { + auto authorToStr = [](FlameMod::ModpackAuthor &author) { + if (author.url.isEmpty()) { + return author.name; + } + return QString("%2").arg(author.url, author.name); + }; + QStringList authorStrs; + for (auto &author : current.authors) { + authorStrs.push_back(authorToStr(author)); + } + text += "
" + tr(" by ") + authorStrs.join(", "); + } + text += "

"; + + ui->packDescription->setHtml(text + current.description); + + if (!current.versionsLoaded) { + qDebug() << "Loading flame mod versions"; + + ui->modSelectionButton->setText(tr("Loading versions...")); + ui->modSelectionButton->setEnabled(false); + + auto netJob = + new NetJob(QString("Flame::ModVersions(%1)").arg(current.name), + APPLICATION->network()); + auto response = new QByteArray(); + int addonId = current.addonId; + netJob->addNetAction(Net::Download::makeByteArray( + QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files") + .arg(addonId), + response)); + + QObject::connect(netJob, &NetJob::succeeded, this, [this, response] { + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Flame at " + << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; return; - } - - current = listModel->data(first, Qt::UserRole).value(); - QString text = ""; - QString name = current.name; - - if (current.websiteUrl.isEmpty()) - text = name; - else - text = "" + name + ""; - if (!current.authors.empty()) { - auto authorToStr = [](FlameMod::ModpackAuthor & author) { - if(author.url.isEmpty()) { - return author.name; - } - return QString("%2").arg(author.url, author.name); - }; - QStringList authorStrs; - for(auto & author: current.authors) { - authorStrs.push_back(authorToStr(author)); + } + QJsonArray arr = doc.array(); + try { + FlameMod::loadIndexedPackVersions(current, arr, APPLICATION->network(), + m_instance); + } catch (const JSONValidationError &e) { + qDebug() << *response; + qWarning() << "Error while reading Flame mod version: " << e.cause(); + } + auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile(); + QString mcVersion = packProfile->getComponentVersion("net.minecraft"); + QString loaderString = + (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) + ? "fabric" + : "forge"; + for (int i = 0; i < current.versions.size(); i++) { + auto version = current.versions[i]; + if (!version.mcVersion.contains(mcVersion)) { + continue; } - text += "
" + tr(" by ") + authorStrs.join(", "); - } - text += "

"; + ui->versionSelectionBox->addItem(version.version, QVariant(i)); + } + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found!"), + QVariant(-1)); + } - ui->packDescription->setHtml(text + current.description); - - if (!current.versionsLoaded) - { - qDebug() << "Loading flame mod versions"; - auto netJob = new NetJob(QString("Flame::ModVersions(%1)").arg(current.name), APPLICATION->network()); - auto response = new QByteArray(); - int addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId), response)); - - QObject::connect(netJob, &NetJob::succeeded, this, [this, response] - { - QJsonParseError parse_error; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if(parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *response; - return; - } - QJsonArray arr = doc.array(); - try - { - FlameMod::loadIndexedPackVersions(current, arr, APPLICATION->network(), m_instance); - } - catch(const JSONValidationError &e) - { - qDebug() << *response; - qWarning() << "Error while reading Flame mod version: " << e.cause(); - } - auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile(); - QString mcVersion = packProfile->getComponentVersion("net.minecraft"); - QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge"; - for(int i = 0; i < current.versions.size(); i++) { - auto version = current.versions[i]; - if(!version.mcVersion.contains(mcVersion)){ - continue; - } - ui->versionSelectionBox->addItem(version.version, QVariant(i)); - } - if(ui->versionSelectionBox->count() == 0){ - ui->versionSelectionBox->addItem(tr("No Valid Version found!"), QVariant(-1)); - } - suggestCurrent(); - }); - QObject::connect(netJob, &NetJob::finished, this, [response, netJob] - { - netJob->deleteLater(); - delete response; - }); - netJob->start(); + ui->modSelectionButton->setText(tr("Cannot select invalid version :(")); + updateSelectionButton(); + }); + QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { + netJob->deleteLater(); + delete response; + }); + netJob->start(); + } else { + for (int i = 0; i < current.versions.size(); i++) { + ui->versionSelectionBox->addItem(current.versions[i].version, + QVariant(i)); } - else - { - for(int i = 0; i < current.versions.size(); i++) { - ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i)); - } - if(ui->versionSelectionBox->count() == 0){ - ui->versionSelectionBox->addItem(tr("No Valid Version found!"), QVariant(-1)); - } - suggestCurrent(); + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found!"), + QVariant(-1)); } + + updateSelectionButton(); + } } -void FlameModPage::suggestCurrent() -{ - if(!isOpened) - { - return; - } +void FlameModPage::updateSelectionButton() { + if (!isOpened || selectedVersion < 0) { + ui->modSelectionButton->setEnabled(false); + return; + } - if (selectedVersion == -1) - { - dialog->setSuggestedMod(); - return; - } - - auto version = current.versions[selectedVersion]; - dialog->setSuggestedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods)); + ui->modSelectionButton->setEnabled(true); + auto &version = current.versions[selectedVersion]; + if (!dialog->isModSelected(current.name, version.fileName)) { + ui->modSelectionButton->setText(tr("Select mod for download")); + } else { + ui->modSelectionButton->setText(tr("Deselect mod for download")); + } } -void FlameModPage::onVersionSelectionChanged(QString data) -{ - if(data.isNull() || data.isEmpty()) - { - selectedVersion = -1; - return; - } - selectedVersion = ui->versionSelectionBox->currentData().toInt(); - suggestCurrent(); +void FlameModPage::onVersionSelectionChanged(QString data) { + if (data.isNull() || data.isEmpty()) { + selectedVersion = -1; + return; + } + selectedVersion = ui->versionSelectionBox->currentData().toInt(); + updateSelectionButton(); +} + +void FlameModPage::onModSelected() { + auto &version = current.versions[selectedVersion]; + if (dialog->isModSelected(current.name, version.fileName)) { + dialog->removeSelectedMod(current.name); + } else { + dialog->addSelectedMod(current.name, + new ModDownloadTask(version.downloadUrl, + version.fileName, dialog->mods)); + } + + updateSelectionButton(); } diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h index 8fa3248af..b5b19a4f3 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.h +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.h @@ -50,12 +50,13 @@ public: BaseInstance *m_instance; private: - void suggestCurrent(); + void updateSelectionButton(); private slots: void triggerSearch(); void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); + void onModSelected(); private: Ui::FlameModPage *ui = nullptr; diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.ui b/launcher/ui/pages/modplatform/flame/FlameModPage.ui index 7da0bb4aa..36df7e8a4 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.ui +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.ui @@ -1,90 +1,97 @@ - FlameModPage - - - - 0 - 0 - 837 - 685 - - - - - - - - - - 48 - 48 - - - - Qt::ScrollBarAlwaysOff - - - true - - - - - - - true - - - true - - - - - - - - - - - - - - Version selected: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - Search - - - - - - - Search and filter ... - - - + FlameModPage + + + + 0 + 0 + 837 + 685 + + + + + + + + + + + + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Select mod for download + + + - - - searchEdit - searchButton - packView - packDescription - sortByBox - versionSelectionBox - - - + + + + + Search and filter ... + + + + + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + + + + + true + + + true + + + + + + + + + Search + + + + + + + searchEdit + searchButton + packView + packDescription + sortByBox + versionSelectionBox + + + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 577a7bcb6..fc6aff962 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -4,180 +4,199 @@ #include #include "Application.h" -#include "Json.h" -#include "ui/dialogs/ModDownloadDialog.h" #include "InstanceImportTask.h" -#include "ModrinthModel.h" +#include "Json.h" #include "ModDownloadTask.h" +#include "ModrinthModel.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "ui/dialogs/ModDownloadDialog.h" ModrinthPage::ModrinthPage(ModDownloadDialog *dialog, BaseInstance *instance) - : QWidget(dialog), m_instance(instance), ui(new Ui::ModrinthPage), dialog(dialog) -{ - ui->setupUi(this); - connect(ui->searchButton, &QPushButton::clicked, this, &ModrinthPage::triggerSearch); - ui->searchEdit->installEventFilter(this); - listModel = new Modrinth::ListModel(this); - ui->packView->setModel(listModel); + : QWidget(dialog), m_instance(instance), ui(new Ui::ModrinthPage), + dialog(dialog) { + ui->setupUi(this); + connect(ui->searchButton, &QPushButton::clicked, this, + &ModrinthPage::triggerSearch); + ui->searchEdit->installEventFilter(this); + listModel = new Modrinth::ListModel(this); + ui->packView->setModel(listModel); - ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); + ui->versionSelectionBox->view()->setVerticalScrollBarPolicy( + Qt::ScrollBarAsNeeded); + ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); - // index is used to set the sorting with the modrinth api - ui->sortByBox->addItem(tr("Sort by Relevence")); - ui->sortByBox->addItem(tr("Sort by Downloads")); - ui->sortByBox->addItem(tr("Sort by Follows")); - ui->sortByBox->addItem(tr("Sort by last updated")); - ui->sortByBox->addItem(tr("Sort by newest")); + // index is used to set the sorting with the modrinth api + ui->sortByBox->addItem(tr("Sort by Relevence")); + ui->sortByBox->addItem(tr("Sort by Downloads")); + ui->sortByBox->addItem(tr("Sort by Follows")); + ui->sortByBox->addItem(tr("Sort by last updated")); + ui->sortByBox->addItem(tr("Sort by newest")); - connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); - connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged); - connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthPage::onVersionSelectionChanged); + connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, + SLOT(triggerSearch())); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &ModrinthPage::onSelectionChanged); + connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, + &ModrinthPage::onVersionSelectionChanged); + connect(ui->modSelectionButton, &QPushButton::clicked, this, + &ModrinthPage::onModSelected); } -ModrinthPage::~ModrinthPage() -{ - delete ui; -} +ModrinthPage::~ModrinthPage() { delete ui; } -bool ModrinthPage::eventFilter(QObject* watched, QEvent* event) -{ - if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Return) { - triggerSearch(); - keyEvent->accept(); - return true; - } +bool ModrinthPage::eventFilter(QObject *watched, QEvent *event) { + if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return) { + triggerSearch(); + keyEvent->accept(); + return true; } - return QWidget::eventFilter(watched, event); + } + return QWidget::eventFilter(watched, event); } -bool ModrinthPage::shouldDisplay() const -{ - return true; +bool ModrinthPage::shouldDisplay() const { return true; } + +void ModrinthPage::openedImpl() { + updateSelectionButton(); + triggerSearch(); } -void ModrinthPage::openedImpl() -{ - suggestCurrent(); - triggerSearch(); +void ModrinthPage::triggerSearch() { + listModel->searchWithTerm(ui->searchEdit->text(), + ui->sortByBox->currentIndex()); } -void ModrinthPage::triggerSearch() -{ - listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); -} +void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) { + ui->versionSelectionBox->clear(); -void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) -{ - ui->versionSelectionBox->clear(); + if (!first.isValid()) { + return; + } - if(!first.isValid()) - { - if(isOpened) - { - dialog->setSuggestedMod(); - } + current = listModel->data(first, Qt::UserRole).value(); + QString text = ""; + QString name = current.name; + + if (current.websiteUrl.isEmpty()) + text = name; + else + text = "" + name + ""; + text += "
" + tr(" by ") + "" + + current.author.name + "

"; + ui->packDescription->setHtml(text + current.description); + + if (!current.versionsLoaded) { + qDebug() << "Loading Modrinth mod versions"; + + ui->modSelectionButton->setText(tr("Loading versions...")); + ui->modSelectionButton->setEnabled(false); + + auto netJob = + new NetJob(QString("Modrinth::ModVersions(%1)").arg(current.name), + APPLICATION->network()); + auto response = new QByteArray(); + QString addonId = current.addonId; + netJob->addNetAction(Net::Download::makeByteArray( + QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId), + response)); + + QObject::connect(netJob, &NetJob::succeeded, this, [this, response] { + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth at " + << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; return; - } - - current = listModel->data(first, Qt::UserRole).value(); - QString text = ""; - QString name = current.name; - - if (current.websiteUrl.isEmpty()) - text = name; - else - text = "" + name + ""; - text += "
"+ tr(" by ") + ""+current.author.name+"

"; - ui->packDescription->setHtml(text + current.description); - - if (!current.versionsLoaded) - { - qDebug() << "Loading Modrinth mod versions"; - auto netJob = new NetJob(QString("Modrinth::ModVersions(%1)").arg(current.name), APPLICATION->network()); - auto response = new QByteArray(); - QString addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId), response)); - - QObject::connect(netJob, &NetJob::succeeded, this, [this, response] - { - QJsonParseError parse_error; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if(parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *response; - return; - } - QJsonArray arr = doc.array(); - try - { - Modrinth::loadIndexedPackVersions(current, arr, APPLICATION->network(), m_instance); - } - catch(const JSONValidationError &e) - { - qDebug() << *response; - qWarning() << "Error while reading Modrinth mod version: " << e.cause(); - } - auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile(); - QString mcVersion = packProfile->getComponentVersion("net.minecraft"); - QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge"; - for(int i = 0; i < current.versions.size(); i++) { - auto version = current.versions[i]; - if(!version.mcVersion.contains(mcVersion) || !version.loaders.contains(loaderString)){ - continue; - } - ui->versionSelectionBox->addItem(version.version, QVariant(i)); - } - if(ui->versionSelectionBox->count() == 0){ - ui->versionSelectionBox->addItem(tr("No Valid Version found !"), QVariant(-1)); - } - - suggestCurrent(); - }); - QObject::connect(netJob, &NetJob::finished, this, [response, netJob]{ - netJob->deleteLater(); - delete response; - }); - netJob->start(); - } - else - { - for(int i = 0; i < current.versions.size(); i++) { - ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i)); + } + QJsonArray arr = doc.array(); + try { + Modrinth::loadIndexedPackVersions(current, arr, APPLICATION->network(), + m_instance); + } catch (const JSONValidationError &e) { + qDebug() << *response; + qWarning() << "Error while reading Modrinth mod version: " << e.cause(); + } + auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile(); + QString mcVersion = packProfile->getComponentVersion("net.minecraft"); + QString loaderString = + (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) + ? "fabric" + : "forge"; + for (int i = 0; i < current.versions.size(); i++) { + auto version = current.versions[i]; + if (!version.mcVersion.contains(mcVersion) || + !version.loaders.contains(loaderString)) { + continue; } - if(ui->versionSelectionBox->count() == 0){ - ui->versionSelectionBox->addItem(tr("No Valid Version found !"), QVariant(-1)); - } - suggestCurrent(); - } -} + ui->versionSelectionBox->addItem(version.version, QVariant(i)); + } + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found !"), + QVariant(-1)); + } -void ModrinthPage::suggestCurrent() -{ - if(!isOpened) - { - return; + ui->modSelectionButton->setText(tr("Cannot select invalid version :(")); + updateSelectionButton(); + }); + + QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { + netJob->deleteLater(); + delete response; + }); + + netJob->start(); + } else { + for (int i = 0; i < current.versions.size(); i++) { + ui->versionSelectionBox->addItem(current.versions[i].version, + QVariant(i)); + } + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found !"), + QVariant(-1)); } - if (selectedVersion == -1) - { - dialog->setSuggestedMod(); - return; - } - auto version = current.versions[selectedVersion]; - dialog->setSuggestedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods)); + updateSelectionButton(); + } } -void ModrinthPage::onVersionSelectionChanged(QString data) -{ - if(data.isNull() || data.isEmpty()) - { - selectedVersion = -1; - return; - } - selectedVersion = ui->versionSelectionBox->currentData().toInt(); - suggestCurrent(); +void ModrinthPage::updateSelectionButton() { + if (!isOpened || selectedVersion < 0) { + ui->modSelectionButton->setEnabled(false); + return; + } + + ui->modSelectionButton->setEnabled(true); + auto &version = current.versions[selectedVersion]; + if (!dialog->isModSelected(current.name, version.fileName)) { + ui->modSelectionButton->setText(tr("Select mod for download")); + } else { + ui->modSelectionButton->setText(tr("Deselect mod for download")); + } +} + +void ModrinthPage::onVersionSelectionChanged(QString data) { + if (data.isNull() || data.isEmpty()) { + selectedVersion = -1; + return; + } + selectedVersion = ui->versionSelectionBox->currentData().toInt(); + updateSelectionButton(); +} + +void ModrinthPage::onModSelected() { + auto &version = current.versions[selectedVersion]; + if (dialog->isModSelected(current.name, version.fileName)) { + dialog->removeSelectedMod(current.name); + } else { + dialog->addSelectedMod(current.name, + new ModDownloadTask(version.downloadUrl, + version.fileName, dialog->mods)); + } + + updateSelectionButton(); } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index 3c517069d..52b538e35 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -50,12 +50,13 @@ public: BaseInstance *m_instance; private: - void suggestCurrent(); + void updateSelectionButton(); private slots: void triggerSearch(); void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); + void onModSelected(); private: Ui::ModrinthPage *ui = nullptr; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 6d183de50..d0a8b8f78 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -1,90 +1,97 @@ - ModrinthPage - - - - 0 - 0 - 837 - 685 - - - - - - - - - - 48 - 48 - - - - Qt::ScrollBarAlwaysOff - - - true - - - - - - - true - - - true - - - - - - - - - - - - - - Version selected: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - Search - - - - - - - Search and filter ... - - - + ModrinthPage + + + + 0 + 0 + 837 + 685 + + + + + + + + + true + + + true + + + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + - - - searchEdit - searchButton - packView - packDescription - sortByBox - versionSelectionBox - - - + + + + + Search + + + + + + + Search and filter ... + + + + + + + + + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Select mod for download + + + + + + + + + searchEdit + searchButton + packView + packDescription + sortByBox + versionSelectionBox + + +