diff options
Diffstat (limited to 'launcher/ui')
23 files changed, 811 insertions, 688 deletions
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index a3da64e4..7b758e05 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -233,6 +233,7 @@ public: TranslatedToolButton helpMenuButton; TranslatedAction actionReportBug; TranslatedAction actionDISCORD; + TranslatedAction actionMATRIX; TranslatedAction actionREDDIT; TranslatedAction actionAbout; @@ -341,13 +342,23 @@ public: all_actions.append(&actionReportBug); helpMenu->addAction(actionReportBug); } + + if(!BuildConfig.MATRIX_URL.isEmpty()) { + actionMATRIX = TranslatedAction(MainWindow); + actionMATRIX->setObjectName(QStringLiteral("actionMATRIX")); + actionMATRIX->setIcon(APPLICATION->getThemedIcon("matrix")); + actionMATRIX.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Matrix")); + actionMATRIX.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Matrix space")); + all_actions.append(&actionMATRIX); + helpMenu->addAction(actionMATRIX); + } if (!BuildConfig.DISCORD_URL.isEmpty()) { actionDISCORD = TranslatedAction(MainWindow); actionDISCORD->setObjectName(QStringLiteral("actionDISCORD")); actionDISCORD->setIcon(APPLICATION->getThemedIcon("discord")); actionDISCORD.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Discord")); - actionDISCORD.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 discord voice chat.")); + actionDISCORD.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Discord guild.")); all_actions.append(&actionDISCORD); helpMenu->addAction(actionDISCORD); } @@ -1469,6 +1480,11 @@ void MainWindow::on_actionDISCORD_triggered() DesktopServices::openUrl(QUrl(BuildConfig.DISCORD_URL)); } +void MainWindow::on_actionMATRIX_triggered() +{ + DesktopServices::openUrl(QUrl(BuildConfig.MATRIX_URL)); +} + void MainWindow::on_actionChangeInstIcon_triggered() { if (!m_selectedInstance) diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index fd65620e..f2852d78 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -72,6 +72,8 @@ private slots: void on_actionREDDIT_triggered(); + void on_actionMATRIX_triggered(); + void on_actionDISCORD_triggered(); void on_actionCopyInstance_triggered(); diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index 6b807b8c..a53f93e8 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -5,6 +5,7 @@ #include <InstanceList.h> #include "ProgressDialog.h" +#include "ReviewMessageBox.h" #include <QLayout> #include <QPushButton> @@ -39,9 +40,10 @@ ModDownloadDialog::ModDownloadDialog(const std::shared_ptr<ModFolderModel> &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<ModFolderModel> &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,25 @@ void ModDownloadDialog::reject() QDialog::reject(); } +void ModDownloadDialog::confirm() +{ + auto keys = modTask.keys(); + keys.sort(Qt::CaseInsensitive); + + auto confirm_dialog = ReviewMessageBox::create( + this, + tr("Confirm mods to download") + ); + + for(auto task : keys){ + confirm_dialog->appendMod(task, modTask.find(task).value()->getFilename()); + } + + connect(confirm_dialog, &QDialog::accepted, this, &ModDownloadDialog::accept); + + confirm_dialog->open(); +} + void ModDownloadDialog::accept() { QDialog::accept(); @@ -83,16 +105,35 @@ QList<BasePage *> ModDownloadDialog::getPages() }; } -void ModDownloadDialog::setSuggestedMod(const QString& name, ModDownloadTask* task) +void ModDownloadDialog::addSelectedMod(const QString& name, ModDownloadTask* 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 { - modTask.reset(task); - m_buttons->button(QDialogButtonBox::Ok)->setEnabled(task); + // 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<ModDownloadTask*> ModDownloadDialog::getTasks() { + return modTask.values(); } diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index ece8e328..309d89d0 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -29,12 +29,15 @@ public: QString dialogTitle() override; QList<BasePage *> 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<ModDownloadTask*> getTasks(); const std::shared_ptr<ModFolderModel> &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<ModDownloadTask> modTask; + QHash<QString, ModDownloadTask*> modTask; BaseInstance *m_instance; }; diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp new file mode 100644 index 00000000..2bfd02e0 --- /dev/null +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -0,0 +1,31 @@ +#include "ReviewMessageBox.h" +#include "ui_ReviewMessageBox.h" + +ReviewMessageBox::ReviewMessageBox(QWidget* parent, QString const& title, QString const& icon) + : QDialog(parent), ui(new Ui::ReviewMessageBox) +{ + ui->setupUi(this); +} + +ReviewMessageBox::~ReviewMessageBox() +{ + delete ui; +} + +auto ReviewMessageBox::create(QWidget* parent, QString&& title, QString&& icon) -> ReviewMessageBox* +{ + return new ReviewMessageBox(parent, title, icon); +} + +void ReviewMessageBox::appendMod(const QString& name, const QString& filename) +{ + auto itemTop = new QTreeWidgetItem(ui->modTreeWidget); + itemTop->setText(0, name); + + auto filenameItem = new QTreeWidgetItem(itemTop); + filenameItem->setText(0, tr("Filename: %1").arg(filename)); + + itemTop->insertChildren(0, { filenameItem }); + + ui->modTreeWidget->addTopLevelItem(itemTop); +} diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h new file mode 100644 index 00000000..48742cd9 --- /dev/null +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -0,0 +1,23 @@ +#pragma once + +#include <QDialog> + +namespace Ui { +class ReviewMessageBox; +} + +class ReviewMessageBox final : public QDialog { + Q_OBJECT + + public: + static auto create(QWidget* parent, QString&& title, QString&& icon = "") -> ReviewMessageBox*; + + void appendMod(const QString& name, const QString& filename); + + ~ReviewMessageBox(); + + private: + 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 new file mode 100644 index 00000000..d04f3b3f --- /dev/null +++ b/launcher/ui/dialogs/ReviewMessageBox.ui @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ReviewMessageBox</class> + <widget class="QDialog" name="ReviewMessageBox"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Confirm mod selection</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>You're about to download the following mods:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QTreeWidget" name="modTreeWidget"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::NoSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectItems</enum> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string/> + </property> + </column> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ReviewMessageBox</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>200</x> + <y>265</y> + </hint> + <hint type="destinationlabel"> + <x>199</x> + <y>149</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ReviewMessageBox</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>200</x> + <y>265</y> + </hint> + <hint type="destinationlabel"> + <x>199</x> + <y>149</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 9e1d761b..ee68cd08 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -242,16 +242,16 @@ void LauncherPage::applySettings() switch (ui->themeComboBox->currentIndex()) { case 0: - s->set("IconTheme", "pe_dark"); + s->set("IconTheme", "pe_colored"); break; case 1: s->set("IconTheme", "pe_light"); break; case 2: - s->set("IconTheme", "pe_blue"); + s->set("IconTheme", "pe_dark"); break; case 3: - s->set("IconTheme", "pe_colored"); + s->set("IconTheme", "pe_blue"); break; case 4: s->set("IconTheme", "OSX"); @@ -263,10 +263,10 @@ void LauncherPage::applySettings() s->set("IconTheme", "flat"); break; case 7: - s->set("IconTheme", "custom"); + s->set("IconTheme", "multimc"); break; case 8: - s->set("IconTheme", "multimc"); + s->set("IconTheme", "custom"); break; } @@ -319,7 +319,7 @@ void LauncherPage::loadSettings() m_currentUpdateChannel = s->get("UpdateChannel").toString(); //FIXME: make generic auto theme = s->get("IconTheme").toString(); - if (theme == "pe_dark") + if (theme == "pe_colored") { ui->themeComboBox->setCurrentIndex(0); } @@ -327,11 +327,11 @@ void LauncherPage::loadSettings() { ui->themeComboBox->setCurrentIndex(1); } - else if (theme == "pe_blue") + else if (theme == "pe_dark") { ui->themeComboBox->setCurrentIndex(2); } - else if (theme == "pe_colored") + else if (theme == "pe_blue") { ui->themeComboBox->setCurrentIndex(3); } diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 6f68d0dd..c110dd09 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -245,7 +245,7 @@ </property> <item> <property name="text"> - <string>Simple (Dark Icons)</string> + <string>Simple (Colored Icons)</string> </property> </item> <item> @@ -255,12 +255,12 @@ </item> <item> <property name="text"> - <string>Simple (Blue Icons)</string> + <string>Simple (Dark Icons)</string> </property> </item> <item> <property name="text"> - <string>Simple (Colored Icons)</string> + <string>Simple (Blue Icons)</string> </property> </item> <item> @@ -275,17 +275,17 @@ </item> <item> <property name="text"> - <string notr="true">Flat</string> + <string>Flat</string> </property> </item> <item> <property name="text"> - <string>Custom</string> + <string>Legacy</string> </property> </item> <item> <property name="text"> - <string>MultiMC</string> + <string>Custom</string> </property> </item> </widget> diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.cpp b/launcher/ui/pages/instance/LegacyUpgradePage.cpp deleted file mode 100644 index cb78af02..00000000 --- a/launcher/ui/pages/instance/LegacyUpgradePage.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "LegacyUpgradePage.h" -#include "ui_LegacyUpgradePage.h" - -#include "InstanceList.h" -#include "minecraft/legacy/LegacyInstance.h" -#include "minecraft/legacy/LegacyUpgradeTask.h" -#include "Application.h" - -#include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ProgressDialog.h" - -LegacyUpgradePage::LegacyUpgradePage(InstancePtr inst, QWidget *parent) - : QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst) -{ - ui->setupUi(this); -} - -LegacyUpgradePage::~LegacyUpgradePage() -{ - delete ui; -} - -void LegacyUpgradePage::runModalTask(Task *task) -{ - connect(task, &Task::failed, [this](QString reason) - { - CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Warning)->show(); - }); - ProgressDialog loadDialog(this); - loadDialog.setSkipButton(true, tr("Abort")); - if(loadDialog.execWithTask(task) == QDialog::Accepted) - { - m_container->requestClose(); - } -} - -void LegacyUpgradePage::on_upgradeButton_clicked() -{ - QString newName = tr("%1 (Migrated)").arg(m_inst->name()); - auto upgradeTask = new LegacyUpgradeTask(m_inst); - upgradeTask->setName(newName); - upgradeTask->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); - upgradeTask->setIcon(m_inst->iconKey()); - unique_qobject_ptr<Task> task(APPLICATION->instances()->wrapInstanceTask(upgradeTask)); - runModalTask(task.get()); -} - -bool LegacyUpgradePage::shouldDisplay() const -{ - return !m_inst->isRunning(); -} diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.h b/launcher/ui/pages/instance/LegacyUpgradePage.h deleted file mode 100644 index 7c51956b..00000000 --- a/launcher/ui/pages/instance/LegacyUpgradePage.h +++ /dev/null @@ -1,64 +0,0 @@ -/* 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 <QWidget> - -#include "minecraft/legacy/LegacyInstance.h" -#include "ui/pages/BasePage.h" -#include <Application.h> -#include "tasks/Task.h" - -namespace Ui -{ -class LegacyUpgradePage; -} - -class LegacyUpgradePage : public QWidget, public BasePage -{ - Q_OBJECT - -public: - explicit LegacyUpgradePage(InstancePtr inst, QWidget *parent = 0); - virtual ~LegacyUpgradePage(); - virtual QString displayName() const override - { - return tr("Upgrade"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("checkupdate"); - } - virtual QString id() const override - { - return "upgrade"; - } - virtual QString helpPage() const override - { - return "Legacy-upgrade"; - } - virtual bool shouldDisplay() const override; - -private slots: - void on_upgradeButton_clicked(); - -private: - void runModalTask(Task *task); - -private: - Ui::LegacyUpgradePage *ui; - InstancePtr m_inst; -}; diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.ui b/launcher/ui/pages/instance/LegacyUpgradePage.ui deleted file mode 100644 index b22c03e5..00000000 --- a/launcher/ui/pages/instance/LegacyUpgradePage.ui +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>LegacyUpgradePage</class> - <widget class="QWidget" name="LegacyUpgradePage"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>546</width> - <height>405</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QTextBrowser" name="textBrowser"> - <property name="html"> - <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:11pt; font-weight:400; font-style:normal;"> -<h1 style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Upgrade is required</span></h1> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PolyMC now supports old Minecraft versions and all the required features in the new (OneSix) instance format. As a consequence, the old (Legacy) format has been entirely disabled and old instances need to be upgraded.</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process.</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please report any issues on our <a href="https://github.com/PolyMC/PolyMC/issues"><span style=" text-decoration: underline; color:#3584e4;">github issues page</span></a>.</p></body></html></string> - </property> - <property name="openExternalLinks"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCommandLinkButton" name="upgradeButton"> - <property name="text"> - <string>Upgrade the instance</string> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 494d32f0..ffb87bbe 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -143,11 +143,19 @@ ModFolderPage::ModFolderPage( ui(new Ui::ModFolderPage) { ui->setupUi(this); + + // This is structured like that so that these changes + // do not affect the Resouce pack and Shader pack tabs if(id == "mods") { - auto act = new QAction(tr("Install Mods"), this); - ui->actionsToolbar->insertActionBefore(ui->actionView_configs,act); + auto act = new QAction(tr("Download mods"), this); + act->setToolTip(tr("Download mods from online mod platforms")); + ui->actionsToolbar->insertActionBefore(ui->actionAdd, act); connect(act, &QAction::triggered, this, &ModFolderPage::on_actionInstall_mods_triggered); + + ui->actionAdd->setText(tr("Add .jar")); + ui->actionAdd->setToolTip(tr("Add mods via local file")); } + ui->actionsToolbar->insertSpacer(ui->actionView_configs); m_inst = inst; @@ -365,8 +373,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/instance/ModFolderPage.ui b/launcher/ui/pages/instance/ModFolderPage.ui index 0fb51e84..ab59b0df 100644 --- a/launcher/ui/pages/instance/ModFolderPage.ui +++ b/launcher/ui/pages/instance/ModFolderPage.ui @@ -96,7 +96,7 @@ <string>&Add</string> </property> <property name="toolTip"> - <string>Add mods</string> + <string>Add</string> </property> </action> <action name="actionRemove"> diff --git a/launcher/ui/pages/modplatform/flame/FlameModModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModModel.cpp index 2cf83261..e8afba5a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModModel.cpp @@ -175,13 +175,13 @@ void ListModel::performPaginatedSearch() "pageSize=25&" "searchFilter=%2&" "sort=%3&" - "%4" + "modLoaderType=%4&" "gameVersion=%5" ) .arg(nextSearchOffset) .arg(currentSearchTerm) .arg(sorts[currentSort]) - .arg(hasFabric ? "modLoaderType=4&" : "") + .arg(hasFabric ? 4 : 1) // Enum: https://docs.curseforge.com/?http#tocS_ModLoaderType .arg(mcVersion); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp index a816c681..114ac907 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp @@ -4,193 +4,214 @@ #include <QKeyEvent> #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); - - 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")); - - 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); + : 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); + + // 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->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<QKeyEvent*>(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<QKeyEvent *>(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() -{ - suggestCurrent(); - triggerSearch(); +void FlameModPage::openedImpl() { + updateSelectionButton(); + 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(); - - if(!first.isValid()) - { - if(isOpened) - { - dialog->setSuggestedMod(); +void FlameModPage::onSelectionChanged(QModelIndex first, QModelIndex second) { + ui->versionSelectionBox->clear(); + + if (!first.isValid()) { + return; + } + + current = listModel->data(first, Qt::UserRole).value<FlameMod::IndexedPack>(); + QString text = ""; + QString name = current.name; + + if (current.websiteUrl.isEmpty()) + text = name; + else + text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; + if (!current.authors.empty()) { + auto authorToStr = [](FlameMod::ModpackAuthor &author) { + if (author.url.isEmpty()) { + return author.name; + } + return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name); + }; + QStringList authorStrs; + for (auto &author : current.authors) { + authorStrs.push_back(authorToStr(author)); + } + text += "<br>" + tr(" by ") + authorStrs.join(", "); + } + text += "<br><br>"; + + 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, addonId] { + if(addonId != current.addonId){ + return; //wrong request } + 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<FlameMod::IndexedPack>(); - QString text = ""; - QString name = current.name; - - if (current.websiteUrl.isEmpty()) - text = name; - else - text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; - if (!current.authors.empty()) { - auto authorToStr = [](FlameMod::ModpackAuthor & author) { - if(author.url.isEmpty()) { - return author.name; - } - return QString("<a href=\"%1\">%2</a>").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 += "<br>" + tr(" by ") + authorStrs.join(", "); + ui->versionSelectionBox->addItem(version.version, QVariant(i)); + } + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found!"), + QVariant(-1)); + } + + 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)); } - text += "<br><br>"; - - 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()); - std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>(); - int addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId), response.get())); - - QObject::connect(netJob, &NetJob::succeeded, this, [this, response, netJob] - { - netJob->deleteLater(); - 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(); - }); - netJob->start(); + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found!"), + QVariant(-1)); } - 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(); - } -} -void FlameModPage::suggestCurrent() -{ - if(!isOpened) - { - return; - } + updateSelectionButton(); + } +} - if (selectedVersion == -1) - { - dialog->setSuggestedMod(); - return; - } +void FlameModPage::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")); + } +} - auto version = current.versions[selectedVersion]; - dialog->setSuggestedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods)); +void FlameModPage::onVersionSelectionChanged(QString data) { + if (data.isNull() || data.isEmpty()) { + selectedVersion = -1; + return; + } + selectedVersion = ui->versionSelectionBox->currentData().toInt(); + updateSelectionButton(); } -void FlameModPage::onVersionSelectionChanged(QString data) -{ - if(data.isNull() || data.isEmpty()) - { - selectedVersion = -1; - return; - } - selectedVersion = ui->versionSelectionBox->currentData().toInt(); - suggestCurrent(); +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 8fa3248a..b5b19a4f 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 7da0bb4a..36df7e8a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.ui +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.ui @@ -1,90 +1,97 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>FlameModPage</class> - <widget class="QWidget" name="FlameModPage"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>837</width> - <height>685</height> - </rect> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="1" column="0"> - <widget class="QListView" name="packView"> - <property name="iconSize"> - <size> - <width>48</width> - <height>48</height> - </size> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="alternatingRowColors"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QTextBrowser" name="packDescription"> - <property name="openExternalLinks"> - <bool>true</bool> - </property> - <property name="openLinks"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - <item row="2" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0"> - <item row="0" column="2"> - <widget class="QComboBox" name="versionSelectionBox"/> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Version selected:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QComboBox" name="sortByBox"/> - </item> - </layout> - </item> - <item row="0" column="1"> - <widget class="QPushButton" name="searchButton"> - <property name="text"> - <string>Search</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLineEdit" name="searchEdit"> - <property name="placeholderText"> - <string>Search and filter ...</string> - </property> - </widget> - </item> + <class>FlameModPage</class> + <widget class="QWidget" name="FlameModPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>837</width> + <height>685</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0,0,0" columnminimumwidth="0,0,0"> + <item row="1" column="2"> + <widget class="QComboBox" name="versionSelectionBox"/> + </item> + <item row="1" column="0"> + <widget class="QComboBox" name="sortByBox"/> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Version selected:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="modSelectionButton"> + <property name="text"> + <string>Select mod for download</string> + </property> + </widget> + </item> </layout> - </widget> - <tabstops> - <tabstop>searchEdit</tabstop> - <tabstop>searchButton</tabstop> - <tabstop>packView</tabstop> - <tabstop>packDescription</tabstop> - <tabstop>sortByBox</tabstop> - <tabstop>versionSelectionBox</tabstop> - </tabstops> - <resources/> - <connections/> + </item> + <item row="0" column="0"> + <widget class="QLineEdit" name="searchEdit"> + <property name="placeholderText"> + <string>Search and filter ...</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="0"> + <widget class="QListView" name="packView"> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="iconSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QTextBrowser" name="packDescription"> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="openLinks"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="searchButton"> + <property name="text"> + <string>Search</string> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>searchEdit</tabstop> + <tabstop>searchButton</tabstop> + <tabstop>packView</tabstop> + <tabstop>packDescription</tabstop> + <tabstop>sortByBox</tabstop> + <tabstop>versionSelectionBox</tabstop> + </tabstops> + <resources/> + <connections/> </ui> diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 1138a298..7e6ac2fd 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -109,13 +109,16 @@ void FlamePage::onSelectionChanged(QModelIndex first, QModelIndex second) if (current.versionsLoaded == false) { qDebug() << "Loading flame modpack versions"; - NetJob *netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network()); - std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>(); + auto netJob = new NetJob(QString("Flame::PackVersions(%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.get())); + 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] + QObject::connect(netJob, &NetJob::succeeded, this, [this, response, addonId] { + if(addonId != current.addonId){ + return; //wrong request + } QJsonParseError parse_error; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if(parse_error.error != QJsonParseError::NoError) { @@ -140,6 +143,11 @@ void FlamePage::onSelectionChanged(QModelIndex first, QModelIndex second) suggestCurrent(); }); + QObject::connect(netJob, &NetJob::finished, this, [response, netJob] + { + netJob->deleteLater(); + delete response; + }); netJob->start(); } else diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index c5a54c29..35cd743a 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -4,177 +4,202 @@ #include <QKeyEvent> #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); - - 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")); - - 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); + : 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); + + // 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->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<QKeyEvent*>(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<QKeyEvent *>(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() -{ - suggestCurrent(); - triggerSearch(); +void ModrinthPage::openedImpl() { + updateSelectionButton(); + 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(); - - if(!first.isValid()) - { - if(isOpened) - { - dialog->setSuggestedMod(); +void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) { + ui->versionSelectionBox->clear(); + + if (!first.isValid()) { + return; + } + + current = listModel->data(first, Qt::UserRole).value<Modrinth::IndexedPack>(); + QString text = ""; + QString name = current.name; + + if (current.websiteUrl.isEmpty()) + text = name; + else + text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; + text += "<br>" + tr(" by ") + "<a href=\"" + current.author.url + "\">" + + current.author.name + "</a><br><br>"; + 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, addonId] { + if(addonId != current.addonId){ + return; } + 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<Modrinth::IndexedPack>(); - QString text = ""; - QString name = current.name; - - if (current.websiteUrl.isEmpty()) - text = name; - else - text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; - text += "<br>"+ tr(" by ") + "<a href=\""+current.author.url+"\">"+current.author.name+"</a><br><br>"; - 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()); - std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>(); - QString addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId), response.get())); - - QObject::connect(netJob, &NetJob::succeeded, this, [this, response, netJob] - { - netJob->deleteLater(); - 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(); - }); - 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)); + } + 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; } - suggestCurrent(); + ui->versionSelectionBox->addItem(version.version, QVariant(i)); + } + if (ui->versionSelectionBox->count() == 0) { + ui->versionSelectionBox->addItem(tr("No Valid Version found !"), + QVariant(-1)); + } + + 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)); } + + updateSelectionButton(); + } } -void ModrinthPage::suggestCurrent() -{ - if(!isOpened) - { - return; - } +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")); + } +} - if (selectedVersion == -1) - { - dialog->setSuggestedMod(); - return; - } - auto version = current.versions[selectedVersion]; - dialog->setSuggestedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods)); +void ModrinthPage::onVersionSelectionChanged(QString data) { + if (data.isNull() || data.isEmpty()) { + selectedVersion = -1; + return; + } + selectedVersion = ui->versionSelectionBox->currentData().toInt(); + updateSelectionButton(); } -void ModrinthPage::onVersionSelectionChanged(QString data) -{ - if(data.isNull() || data.isEmpty()) - { - selectedVersion = -1; - return; - } - selectedVersion = ui->versionSelectionBox->currentData().toInt(); - suggestCurrent(); +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 3c517069..52b538e3 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 6d183de5..d0a8b8f7 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -1,90 +1,97 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ModrinthPage</class> - <widget class="QWidget" name="ModrinthPage"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>837</width> - <height>685</height> - </rect> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="1" column="0"> - <widget class="QListView" name="packView"> - <property name="iconSize"> - <size> - <width>48</width> - <height>48</height> - </size> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="alternatingRowColors"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QTextBrowser" name="packDescription"> - <property name="openExternalLinks"> - <bool>true</bool> - </property> - <property name="openLinks"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - <item row="2" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0"> - <item row="0" column="2"> - <widget class="QComboBox" name="versionSelectionBox"/> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Version selected:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QComboBox" name="sortByBox"/> - </item> - </layout> - </item> - <item row="0" column="1"> - <widget class="QPushButton" name="searchButton"> - <property name="text"> - <string>Search</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLineEdit" name="searchEdit"> - <property name="placeholderText"> - <string>Search and filter ...</string> - </property> - </widget> - </item> + <class>ModrinthPage</class> + <widget class="QWidget" name="ModrinthPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>837</width> + <height>685</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="2"> + <widget class="QTextBrowser" name="packDescription"> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="openLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QListView" name="packView"> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="iconSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + </widget> + </item> </layout> - </widget> - <tabstops> - <tabstop>searchEdit</tabstop> - <tabstop>searchButton</tabstop> - <tabstop>packView</tabstop> - <tabstop>packDescription</tabstop> - <tabstop>sortByBox</tabstop> - <tabstop>versionSelectionBox</tabstop> - </tabstops> - <resources/> - <connections/> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="searchButton"> + <property name="text"> + <string>Search</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLineEdit" name="searchEdit"> + <property name="placeholderText"> + <string>Search and filter ...</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0,0"> + <item row="0" column="2"> + <widget class="QComboBox" name="versionSelectionBox"/> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Version selected:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QComboBox" name="sortByBox"/> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="modSelectionButton"> + <property name="text"> + <string>Select mod for download</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>searchEdit</tabstop> + <tabstop>searchButton</tabstop> + <tabstop>packView</tabstop> + <tabstop>packDescription</tabstop> + <tabstop>sortByBox</tabstop> + <tabstop>versionSelectionBox</tabstop> + </tabstops> + <resources/> + <connections/> </ui> diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index 6de49467..558a98fb 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -14,6 +14,7 @@ */ #include "PageContainer.h" +#include "BuildConfig.h" #include "PageContainer_p.h" #include <QStackedLayout> @@ -207,7 +208,7 @@ void PageContainer::help() QString pageId = m_currentPage->helpPage(); if (pageId.isEmpty()) return; - DesktopServices::openUrl(QUrl("https://github.com/PolyMC/PolyMC/wiki/" + pageId)); + DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg(pageId))); } } |