aboutsummaryrefslogtreecommitdiff
path: root/launcher/ui
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/ui')
-rw-r--r--launcher/ui/dialogs/AboutDialog.ui2
-rw-r--r--launcher/ui/dialogs/ModDownloadDialog.cpp6
-rw-r--r--launcher/ui/dialogs/ProgressDialog.cpp22
-rw-r--r--launcher/ui/dialogs/ProgressDialog.h5
-rw-r--r--launcher/ui/dialogs/ProgressDialog.ui45
-rw-r--r--launcher/ui/dialogs/UpdateDialog.ui2
-rw-r--r--launcher/ui/pages/global/MinecraftPage.ui8
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.cpp1
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.cpp44
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp32
-rw-r--r--launcher/ui/pages/instance/VersionPage.h1
-rw-r--r--launcher/ui/pages/instance/VersionPage.ui9
-rw-r--r--launcher/ui/pages/modplatform/ModModel.cpp40
-rw-r--r--launcher/ui/pages/modplatform/ModModel.h7
-rw-r--r--launcher/ui/pages/modplatform/ModPage.cpp65
-rw-r--r--launcher/ui/pages/modplatform/ModPage.h9
-rw-r--r--launcher/ui/pages/modplatform/ModPage.ui24
-rw-r--r--launcher/ui/pages/modplatform/VanillaPage.cpp90
-rw-r--r--launcher/ui/pages/modplatform/VanillaPage.h7
-rw-r--r--launcher/ui/pages/modplatform/VanillaPage.ui307
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.h2
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.h2
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicData.h46
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicModel.cpp130
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicModel.h44
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicPage.cpp143
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicPage.h11
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicPage.ui132
-rw-r--r--launcher/ui/widgets/CustomCommands.ui2
-rw-r--r--launcher/ui/widgets/ModFilterWidget.cpp109
-rw-r--r--launcher/ui/widgets/ModFilterWidget.h69
-rw-r--r--launcher/ui/widgets/ModFilterWidget.ui54
-rw-r--r--launcher/ui/widgets/VersionSelectWidget.cpp6
-rw-r--r--launcher/ui/widgets/VersionSelectWidget.h2
34 files changed, 1145 insertions, 333 deletions
diff --git a/launcher/ui/dialogs/AboutDialog.ui b/launcher/ui/dialogs/AboutDialog.ui
index f9665c30..70c5009d 100644
--- a/launcher/ui/dialogs/AboutDialog.ui
+++ b/launcher/ui/dialogs/AboutDialog.ui
@@ -80,7 +80,7 @@
</font>
</property>
<property name="text">
- <string notr="true">PolyMC</string>
+ <string notr="true">Launcher</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp
index a53f93e8..d02ea476 100644
--- a/launcher/ui/dialogs/ModDownloadDialog.cpp
+++ b/launcher/ui/dialogs/ModDownloadDialog.cpp
@@ -22,7 +22,9 @@ ModDownloadDialog::ModDownloadDialog(const std::shared_ptr<ModFolderModel> &mods
: QDialog(parent), mods(mods), m_instance(instance)
{
setObjectName(QStringLiteral("ModDownloadDialog"));
- resize(400, 347);
+
+ resize(std::max(0.5*parent->width(), 400.0), std::max(0.75*parent->height(), 400.0));
+
m_verticalLayout = new QVBoxLayout(this);
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
@@ -80,7 +82,7 @@ void ModDownloadDialog::confirm()
tr("Confirm mods to download")
);
- for(auto task : keys){
+ for(auto& task : keys){
confirm_dialog->appendMod(task, modTask.find(task).value()->getFilename());
}
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index 4b092859..648bd88b 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -81,6 +81,12 @@ int ProgressDialog::execWithTask(Task *task)
connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &)));
connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64)));
+ m_is_multi_step = task->isMultiStep();
+ if(!m_is_multi_step){
+ ui->globalStatusLabel->setHidden(true);
+ ui->globalProgressBar->setHidden(true);
+ }
+
// if this didn't connect to an already running task, invoke start
if(!task->isRunning())
{
@@ -152,14 +158,24 @@ void ProgressDialog::onTaskSucceeded()
void ProgressDialog::changeStatus(const QString &status)
{
- ui->statusLabel->setText(status);
+ ui->statusLabel->setText(task->getStepStatus());
+ ui->globalStatusLabel->setText(status);
updateSize();
}
void ProgressDialog::changeProgress(qint64 current, qint64 total)
{
- ui->taskProgressBar->setMaximum(total);
- ui->taskProgressBar->setValue(current);
+ ui->globalProgressBar->setMaximum(total);
+ ui->globalProgressBar->setValue(current);
+
+ if(!m_is_multi_step){
+ ui->taskProgressBar->setMaximum(total);
+ ui->taskProgressBar->setValue(current);
+ }
+ else{
+ ui->taskProgressBar->setMaximum(task->getStepProgress());
+ ui->taskProgressBar->setValue(task->getStepTotalProgress());
+ }
}
void ProgressDialog::keyPressEvent(QKeyEvent *e)
diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h
index b28ad4fa..0b4b78a4 100644
--- a/launcher/ui/dialogs/ProgressDialog.h
+++ b/launcher/ui/dialogs/ProgressDialog.h
@@ -19,6 +19,7 @@
#include <memory>
class Task;
+class SequentialTask;
namespace Ui
{
@@ -35,7 +36,7 @@ public:
void updateSize();
- int execWithTask(Task *task);
+ int execWithTask(Task* task);
int execWithTask(std::unique_ptr<Task> &&task);
int execWithTask(std::unique_ptr<Task> &task);
@@ -68,4 +69,6 @@ private:
Ui::ProgressDialog *ui;
Task *task;
+
+ bool m_is_multi_step = false;
};
diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui
index 04b8fef3..bf119a78 100644
--- a/launcher/ui/dialogs/ProgressDialog.ui
+++ b/launcher/ui/dialogs/ProgressDialog.ui
@@ -2,14 +2,6 @@
<ui version="4.0">
<class>ProgressDialog</class>
<widget class="QDialog" name="ProgressDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>400</width>
- <height>100</height>
- </rect>
- </property>
<property name="minimumSize">
<size>
<width>400</width>
@@ -26,7 +18,27 @@
<string>Please wait...</string>
</property>
<layout class="QGridLayout" name="gridLayout">
+ <item row="4" column="0">
+ <widget class="QPushButton" name="skipButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Skip</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="0">
+ <widget class="QLabel" name="globalStatusLabel">
+ <property name="text">
+ <string>Global Task Status...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>Task Status...</string>
@@ -36,7 +48,7 @@
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="3" column="0">
<widget class="QProgressBar" name="taskProgressBar">
<property name="value">
<number>24</number>
@@ -46,16 +58,13 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QPushButton" name="skipButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="1" column="0">
+ <widget class="QProgressBar" name="globalProgressBar">
+ <property name="enabled">
+ <bool>true</bool>
</property>
- <property name="text">
- <string>Skip</string>
+ <property name="value">
+ <number>24</number>
</property>
</widget>
</item>
diff --git a/launcher/ui/dialogs/UpdateDialog.ui b/launcher/ui/dialogs/UpdateDialog.ui
index bd94a554..5eb9d88a 100644
--- a/launcher/ui/dialogs/UpdateDialog.ui
+++ b/launcher/ui/dialogs/UpdateDialog.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>PolyMC Update</string>
+ <string>Launcher Update</string>
</property>
<property name="windowIcon">
<iconset>
diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui
index decc9b8b..c18ab34b 100644
--- a/launcher/ui/pages/global/MinecraftPage.ui
+++ b/launcher/ui/pages/global/MinecraftPage.ui
@@ -173,20 +173,20 @@
<item>
<widget class="QCheckBox" name="closeAfterLaunchCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;PolyMC will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Close PolyMC after game window opens</string>
+ <string>Close the launcher after game window opens</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="quitAfterGameStopCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;PolyMC will automatically exit if the game crashes or exists.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically quit after the game exits or crashes.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Quit PolyMC after game window stops</string>
+ <string>Quit the launcher after game window closes</string>
</property>
</widget>
</item>
diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp
index a5985741..a48c4d69 100644
--- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp
+++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp
@@ -179,7 +179,6 @@ void InstanceSettingsPage::applySettings()
if(javaArgs)
{
m_settings->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " "));
- JavaCommon::checkJVMArgs(m_settings->get("JvmArgs").toString(), this->parentWidget());
}
else
{
diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp
index 599f0e11..8113fe85 100644
--- a/launcher/ui/pages/instance/ModFolderPage.cpp
+++ b/launcher/ui/pages/instance/ModFolderPage.cpp
@@ -56,8 +56,11 @@
#include "minecraft/VersionFilterData.h"
#include "minecraft/PackProfile.h"
+#include "modplatform/ModAPI.h"
+
#include "Version.h"
#include "ui/dialogs/ProgressDialog.h"
+#include "tasks/SequentialTask.h"
namespace {
// FIXME: wasteful
@@ -387,32 +390,31 @@ void ModFolderPage::on_actionInstall_mods_triggered()
if(m_inst->typeName() != "Minecraft"){
return; //this is a null instance or a legacy instance
}
- bool hasFabric = !((MinecraftInstance *)m_inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
- bool hasForge = !((MinecraftInstance *)m_inst)->getPackProfile()->getComponentVersion("net.minecraftforge").isEmpty();
- if (!hasFabric && !hasForge) {
+ auto profile = ((MinecraftInstance *)m_inst)->getPackProfile();
+ if (profile->getModLoader() == ModAPI::Unspecified) {
QMessageBox::critical(this,tr("Error"),tr("Please install a mod loader first!"));
return;
}
ModDownloadDialog mdownload(m_mods, this, m_inst);
- if(mdownload.exec()) {
- for(auto task : mdownload.getTasks()){
- connect(task, &Task::failed, [this, task](QString reason) {
- task->deleteLater();
- CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
- });
- connect(task, &Task::succeeded, [this, task]() {
- QStringList warnings = task->warnings();
- if (warnings.count()) {
- CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'),
- QMessageBox::Warning)->show();
- }
- task->deleteLater();
- });
- ProgressDialog loadDialog(this);
- loadDialog.setSkipButton(true, tr("Abort"));
- loadDialog.execWithTask(task);
- m_mods->update();
+ if (mdownload.exec()) {
+ SequentialTask* tasks = new SequentialTask(this);
+ connect(tasks, &Task::failed, [this, tasks](QString reason) {
+ CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
+ tasks->deleteLater();
+ });
+ connect(tasks, &Task::succeeded, [this, tasks]() {
+ QStringList warnings = tasks->warnings();
+ if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); }
+ tasks->deleteLater();
+ });
+
+ for (auto task : mdownload.getTasks()) {
+ tasks->addTask(task);
}
+ ProgressDialog loadDialog(this);
+ loadDialog.setSkipButton(true, tr("Abort"));
+ loadDialog.execWithTask(tasks);
+ m_mods->update();
}
}
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index ed37dd1a..23e2367b 100644
--- a/launcher/ui/pages/instance/VersionPage.cpp
+++ b/launcher/ui/pages/instance/VersionPage.cpp
@@ -243,6 +243,9 @@ void VersionPage::updateVersionControls()
bool supportsFabric = minecraftVersion >= Version("1.14");
ui->actionInstall_Fabric->setEnabled(controlsEnabled && supportsFabric);
+ bool supportsQuilt = minecraftVersion >= Version("1.14");
+ ui->actionInstall_Quilt->setEnabled(controlsEnabled && supportsQuilt);
+
bool supportsLiteLoader = minecraftVersion <= Version("1.12.2");
ui->actionInstall_LiteLoader->setEnabled(controlsEnabled && supportsLiteLoader);
@@ -392,7 +395,7 @@ void VersionPage::on_actionChange_version_triggered()
return;
}
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
- if (uid == "net.fabricmc.intermediary")
+ if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed")
{
vselect.setEmptyString(tr("No intermediary mappings versions are currently available."));
vselect.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!"));
@@ -498,6 +501,33 @@ void VersionPage::on_actionInstall_Fabric_triggered()
}
}
+void VersionPage::on_actionInstall_Quilt_triggered()
+{
+ auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader");
+ if(!vlist)
+ {
+ return;
+ }
+ VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this);
+ vselect.setEmptyString(tr("No Quilt Loader versions are currently available."));
+ vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!"));
+
+ auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader");
+ if(!currentVersion.isEmpty())
+ {
+ vselect.setCurrentVersion(currentVersion);
+ }
+
+ if (vselect.exec() && vselect.selectedVersion())
+ {
+ auto vsn = vselect.selectedVersion();
+ m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor());
+ m_profile->resolve(Net::Mode::Online);
+ preselect(m_profile->rowCount(QModelIndex())-1);
+ m_container->refreshContainer();
+ }
+}
+
void VersionPage::on_actionAdd_Empty_triggered()
{
NewComponentDialog compdialog(QString(), QString(), this);
diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h
index 2d37af43..979311fc 100644
--- a/launcher/ui/pages/instance/VersionPage.h
+++ b/launcher/ui/pages/instance/VersionPage.h
@@ -73,6 +73,7 @@ private slots:
void on_actionChange_version_triggered();
void on_actionInstall_Forge_triggered();
void on_actionInstall_Fabric_triggered();
+ void on_actionInstall_Quilt_triggered();
void on_actionAdd_Empty_triggered();
void on_actionInstall_LiteLoader_triggered();
void on_actionReload_triggered();
diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui
index a4990ff3..489f7218 100644
--- a/launcher/ui/pages/instance/VersionPage.ui
+++ b/launcher/ui/pages/instance/VersionPage.ui
@@ -107,6 +107,7 @@
<addaction name="separator"/>
<addaction name="actionInstall_Forge"/>
<addaction name="actionInstall_Fabric"/>
+ <addaction name="actionInstall_Quilt"/>
<addaction name="actionInstall_LiteLoader"/>
<addaction name="actionInstall_mods"/>
<addaction name="separator"/>
@@ -192,6 +193,14 @@
<string>Install the Fabric Loader package.</string>
</property>
</action>
+ <action name="actionInstall_Quilt">
+ <property name="text">
+ <string>Install Quilt</string>
+ </property>
+ <property name="toolTip">
+ <string>Install the Quilt Loader package.</string>
+ </property>
+ </action>
<action name="actionInstall_LiteLoader">
<property name="text">
<string>Install LiteLoader</string>
diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp
index 01b5d247..da0331b5 100644
--- a/launcher/ui/pages/modplatform/ModModel.cpp
+++ b/launcher/ui/pages/modplatform/ModModel.cpp
@@ -61,21 +61,22 @@ auto ListModel::data(const QModelIndex& index, int role) const -> QVariant
void ListModel::requestModVersions(ModPlatform::IndexedPack const& current)
{
+ auto profile = (dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance))->getPackProfile();
+
m_parent->apiProvider()->getVersions(this,
- { current.addonId.toString(), getMineVersions(), hasFabric() ? ModAPI::ModLoaderType::Fabric : ModAPI::ModLoaderType::Forge });
+ { current.addonId.toString(), getMineVersions(), profile->getModLoader() });
}
void ListModel::performPaginatedSearch()
{
+ auto profile = (dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance))->getPackProfile();
+
m_parent->apiProvider()->searchMods(this,
- { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], hasFabric() ? ModAPI::Fabric : ModAPI::Forge, getMineVersions().at(0) });
+ { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], profile->getModLoader(), getMineVersions() });
}
-void ListModel::searchWithTerm(const QString& term, const int sort)
+void ListModel::refresh()
{
- if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) { return; }
- currentSearchTerm = term;
- currentSort = sort;
if (jobPtr) {
jobPtr->abort();
searchState = ResetRequested;
@@ -90,6 +91,20 @@ void ListModel::searchWithTerm(const QString& term, const int sort)
performPaginatedSearch();
}
+void ListModel::searchWithTerm(const QString& term, const int sort, const bool filter_changed)
+{
+ if (currentSearchTerm == term
+ && currentSearchTerm.isNull() == term.isNull()
+ && currentSort == sort
+ && !filter_changed)
+ { return; }
+
+ currentSearchTerm = term;
+ currentSort = sort;
+
+ refresh();
+}
+
void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback)
{
if (m_logoMap.contains(logo)) {
@@ -218,17 +233,8 @@ void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId)
} // namespace ModPlatform
/******** Helpers ********/
-auto ModPlatform::ListModel::hasFabric() const -> bool
-{
- return !(dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance))
- ->getPackProfile()
- ->getComponentVersion("net.fabricmc.fabric-loader")
- .isEmpty();
-}
-auto ModPlatform::ListModel::getMineVersions() const -> QList<QString>
+auto ModPlatform::ListModel::getMineVersions() const -> std::list<Version>
{
- return { (dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance))
- ->getPackProfile()
- ->getComponentVersion("net.minecraft") };
+ return m_parent->getFilter()->versions;
}
diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h
index 64cfa71e..d460cff2 100644
--- a/launcher/ui/pages/modplatform/ModModel.h
+++ b/launcher/ui/pages/modplatform/ModModel.h
@@ -7,6 +7,7 @@
#include "net/NetJob.h"
class ModPage;
+class Version;
namespace ModPlatform {
@@ -33,7 +34,8 @@ class ListModel : public QAbstractListModel {
/* Ask the API for more information */
void fetchMore(const QModelIndex& parent) override;
- void searchWithTerm(const QString& term, const int sort);
+ void refresh();
+ void searchWithTerm(const QString& term, const int sort, const bool filter_changed);
void requestModVersions(const ModPlatform::IndexedPack& current);
virtual void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0;
@@ -62,8 +64,7 @@ class ListModel : public QAbstractListModel {
void requestLogo(QString file, QString url);
- inline auto hasFabric() const -> bool;
- inline auto getMineVersions() const -> QList<QString>;
+ inline auto getMineVersions() const -> std::list<Version>;
protected:
ModPage* m_parent;
diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp
index 3a116d3c..29f6b601 100644
--- a/launcher/ui/pages/modplatform/ModPage.cpp
+++ b/launcher/ui/pages/modplatform/ModPage.cpp
@@ -2,21 +2,39 @@
#include "ui_ModPage.h"
#include <QKeyEvent>
+#include <memory>
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "ui/dialogs/ModDownloadDialog.h"
ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api)
- : QWidget(dialog), m_instance(instance), ui(new Ui::ModPage), dialog(dialog), api(api)
+ : QWidget(dialog)
+ , m_instance(instance)
+ , ui(new Ui::ModPage)
+ , dialog(dialog)
+ , filter_widget(static_cast<MinecraftInstance*>(instance)->getPackProfile()->getComponentVersion("net.minecraft"), this)
+ , api(api)
{
ui->setupUi(this);
connect(ui->searchButton, &QPushButton::clicked, this, &ModPage::triggerSearch);
+ connect(ui->modFilterButton, &QPushButton::clicked, this, &ModPage::filterMods);
ui->searchEdit->installEventFilter(this);
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
+ ui->gridLayout_3->addWidget(&filter_widget, 0, 0, 1, ui->gridLayout_3->columnCount());
+
+ filter_widget.setInstance(static_cast<MinecraftInstance*>(m_instance));
+ m_filter = filter_widget.getFilter();
+
+ connect(&filter_widget, &ModFilterWidget::filterChanged, this, [&]{
+ ui->searchButton->setStyleSheet("text-decoration: underline");
+ });
+ connect(&filter_widget, &ModFilterWidget::filterUnchanged, this, [&]{
+ ui->searchButton->setStyleSheet("text-decoration: none");
+ });
}
ModPage::~ModPage()
@@ -49,9 +67,24 @@ auto ModPage::eventFilter(QObject* watched, QEvent* event) -> bool
/******** Callbacks to events in the UI (set up in the derived classes) ********/
+void ModPage::filterMods()
+{
+ filter_widget.setHidden(!filter_widget.isHidden());
+}
+
void ModPage::triggerSearch()
{
- listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
+ auto changed = filter_widget.changed();
+ m_filter = filter_widget.getFilter();
+
+ if(changed){
+ ui->packView->clearSelection();
+ ui->packDescription->clear();
+ ui->versionSelectionBox->clear();
+ updateSelectionButton();
+ }
+
+ listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex(), changed);
}
void ModPage::onSelectionChanged(QModelIndex first, QModelIndex second)
@@ -68,7 +101,7 @@ void ModPage::onSelectionChanged(QModelIndex first, QModelIndex second)
text = name;
else
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
-
+
if (!current.authors.empty()) {
auto authorToStr = [](ModPlatform::ModpackAuthor& author) -> QString {
if (author.url.isEmpty()) { return author.name; }
@@ -128,27 +161,35 @@ void ModPage::onModSelected()
void ModPage::retranslate()
{
- ui->retranslateUi(this);
+ ui->retranslateUi(this);
}
-void ModPage::updateModVersions()
+void ModPage::updateModVersions(int prev_count)
{
auto packProfile = (dynamic_cast<MinecraftInstance*>(m_instance))->getPackProfile();
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
- QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge";
+
+ QString loaderString = ModAPI::getModLoaderString(packProfile->getModLoader());
for (int i = 0; i < current.versions.size(); i++) {
auto version = current.versions[i];
- //NOTE: Flame doesn't care about loaderString, so passing it changes nothing.
- if (!validateVersion(version, mcVersion, loaderString)) {
- continue;
+ bool valid = false;
+ for(auto& mcVer : m_filter->versions){
+ //NOTE: Flame doesn't care about loaderString, so passing it changes nothing.
+ if (validateVersion(version, mcVer.toString(), loaderString)) {
+ valid = true;
+ break;
+ }
}
- ui->versionSelectionBox->addItem(version.version, QVariant(i));
+ if(valid || m_filter->versions.size() == 0)
+ ui->versionSelectionBox->addItem(version.version, QVariant(i));
+ }
+ if (ui->versionSelectionBox->count() == 0 && prev_count != 0) {
+ ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1));
+ ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
}
- if (ui->versionSelectionBox->count() == 0) { ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); }
- ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
updateSelectionButton();
}
diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h
index 0cd13f37..85aaede9 100644
--- a/launcher/ui/pages/modplatform/ModPage.h
+++ b/launcher/ui/pages/modplatform/ModPage.h
@@ -7,6 +7,7 @@
#include "modplatform/ModIndex.h"
#include "ui/pages/BasePage.h"
#include "ui/pages/modplatform/ModModel.h"
+#include "ui/widgets/ModFilterWidget.h"
class ModDownloadDialog;
@@ -39,9 +40,10 @@ class ModPage : public QWidget, public BasePage {
virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, QString loaderVer = "") const -> bool = 0;
auto apiProvider() const -> const ModAPI* { return api.get(); };
+ auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }
auto getCurrent() -> ModPlatform::IndexedPack& { return current; }
- void updateModVersions();
+ void updateModVersions(int prev_count = -1);
void openedImpl() override;
auto eventFilter(QObject* watched, QEvent* event) -> bool override;
@@ -52,6 +54,7 @@ class ModPage : public QWidget, public BasePage {
void updateSelectionButton();
protected slots:
+ virtual void filterMods();
void triggerSearch();
void onSelectionChanged(QModelIndex first, QModelIndex second);
void onVersionSelectionChanged(QString data);
@@ -60,6 +63,10 @@ class ModPage : public QWidget, public BasePage {
protected:
Ui::ModPage* ui = nullptr;
ModDownloadDialog* dialog = nullptr;
+
+ ModFilterWidget filter_widget;
+ std::shared_ptr<ModFilterWidget::Filter> m_filter;
+
ModPlatform::ListModel* listModel = nullptr;
ModPlatform::IndexedPack current;
diff --git a/launcher/ui/pages/modplatform/ModPage.ui b/launcher/ui/pages/modplatform/ModPage.ui
index 508f1bac..afcd9bb7 100644
--- a/launcher/ui/pages/modplatform/ModPage.ui
+++ b/launcher/ui/pages/modplatform/ModPage.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="1" column="0" colspan="2">
+ <item row="1" column="0" colspan="4">
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="2">
<widget class="QTextBrowser" name="packDescription">
@@ -41,7 +41,7 @@
</item>
</layout>
</item>
- <item row="0" column="1">
+ <item row="0" column="3">
<widget class="QPushButton" name="searchButton">
<property name="text">
<string>Search</string>
@@ -51,12 +51,12 @@
<item row="0" column="0">
<widget class="QLineEdit" name="searchEdit">
<property name="placeholderText">
- <string>Search and filter...</string>
+ <string>Search for mods...</string>
</property>
</widget>
</item>
- <item row="2" column="0" colspan="2">
- <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0,0">
+ <item row="2" column="0" colspan="4">
+ <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0">
<item row="0" column="2">
<widget class="QComboBox" name="versionSelectionBox"/>
</item>
@@ -82,6 +82,20 @@
</item>
</layout>
</item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="modFilterButton">
+ <property name="text">
+ <string>Filter options</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<tabstops>
diff --git a/launcher/ui/pages/modplatform/VanillaPage.cpp b/launcher/ui/pages/modplatform/VanillaPage.cpp
index c691128f..207d0130 100644
--- a/launcher/ui/pages/modplatform/VanillaPage.cpp
+++ b/launcher/ui/pages/modplatform/VanillaPage.cpp
@@ -44,6 +44,7 @@
#include "ui/dialogs/NewInstanceDialog.h"
#include "Filter.h"
#include "InstanceCreationTask.h"
+#include "Version.h"
VanillaPage::VanillaPage(NewInstanceDialog *dialog, QWidget *parent)
: QWidget(parent), dialog(dialog), ui(new Ui::VanillaPage)
@@ -59,6 +60,15 @@ VanillaPage::VanillaPage(NewInstanceDialog *dialog, QWidget *parent)
connect(ui->releaseFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
connect(ui->experimentsFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
connect(ui->refreshBtn, &QPushButton::clicked, this, &VanillaPage::refresh);
+
+ connect(ui->loaderVersionList, &VersionSelectWidget::selectedVersionChanged, this, &VanillaPage::setSelectedLoaderVersion);
+ connect(ui->noneFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
+ connect(ui->forgeFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
+ connect(ui->fabricFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
+ connect(ui->quiltFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
+ connect(ui->liteLoaderFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
+ connect(ui->loaderRefreshBtn, &QPushButton::clicked, this, &VanillaPage::loaderRefresh);
+
}
void VanillaPage::openedImpl()
@@ -80,6 +90,13 @@ void VanillaPage::refresh()
ui->versionList->loadList();
}
+void VanillaPage::loaderRefresh()
+{
+ if(ui->noneFilter->isChecked())
+ return;
+ ui->loaderVersionList->loadList();
+}
+
void VanillaPage::filterChanged()
{
QStringList out;
@@ -99,6 +116,52 @@ void VanillaPage::filterChanged()
ui->versionList->setFilter(BaseVersionList::TypeRole, new RegexpFilter(regexp, false));
}
+void VanillaPage::loaderFilterChanged()
+{
+ auto minecraftVersion = m_selectedVersion->descriptor();
+ if(ui->noneFilter->isChecked())
+ {
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list
+ ui->loaderVersionList->setEmptyString(tr("No mod loader is selected."));
+ ui->loaderVersionList->setEmptyMode(VersionListView::String);
+ return;
+ }
+ else if(ui->forgeFilter->isChecked())
+ {
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
+ m_selectedLoader = "net.minecraftforge";
+ }
+ else if(ui->fabricFilter->isChecked())
+ {
+ // FIXME: dirty hack because the launcher is unaware of Fabric's dependencies
+ if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "");
+ else // Fabric/Quilt unsupported
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list
+ m_selectedLoader = "net.fabricmc.fabric-loader";
+ }
+ else if(ui->quiltFilter->isChecked())
+ {
+ // FIXME: dirty hack because the launcher is unaware of Quilt's dependencies (same as Fabric)
+ if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "");
+ else // Fabric/Quilt unsupported
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list
+ m_selectedLoader = "org.quiltmc.quilt-loader";
+ }
+ else if(ui->liteLoaderFilter->isChecked())
+ {
+ ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
+ m_selectedLoader = "com.mumfrey.liteloader";
+ }
+
+ auto vlist = APPLICATION->metadataIndex()->get(m_selectedLoader);
+ ui->loaderVersionList->initialize(vlist.get());
+ ui->loaderVersionList->selectRecommended();
+ suggestCurrent();
+ ui->loaderVersionList->setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion));
+}
+
VanillaPage::~VanillaPage()
{
delete ui;
@@ -119,6 +182,16 @@ BaseVersionPtr VanillaPage::selectedVersion() const
return m_selectedVersion;
}
+BaseVersionPtr VanillaPage::selectedLoaderVersion() const
+{
+ return m_selectedLoaderVersion;
+}
+
+QString VanillaPage::selectedLoader() const
+{
+ return m_selectedLoader;
+}
+
void VanillaPage::suggestCurrent()
{
if (!isOpened)
@@ -132,7 +205,15 @@ void VanillaPage::suggestCurrent()
return;
}
- dialog->setSuggestedPack(m_selectedVersion->descriptor(), new InstanceCreationTask(m_selectedVersion));
+ // List is empty if either no mod loader is selected, or no versions are available
+ if(!ui->loaderVersionList->hasVersions())
+ dialog->setSuggestedPack(m_selectedVersion->descriptor(), new InstanceCreationTask(m_selectedVersion));
+ else
+ {
+ dialog->setSuggestedPack(m_selectedVersion->descriptor(),
+ new InstanceCreationTask(m_selectedVersion, m_selectedLoader,
+ m_selectedLoaderVersion));
+ }
dialog->setSuggestedIcon("default");
}
@@ -140,4 +221,11 @@ void VanillaPage::setSelectedVersion(BaseVersionPtr version)
{
m_selectedVersion = version;
suggestCurrent();
+ loaderFilterChanged();
+}
+
+void VanillaPage::setSelectedLoaderVersion(BaseVersionPtr version)
+{
+ m_selectedLoaderVersion = version;
+ suggestCurrent();
}
diff --git a/launcher/ui/pages/modplatform/VanillaPage.h b/launcher/ui/pages/modplatform/VanillaPage.h
index 4e7479df..7193597d 100644
--- a/launcher/ui/pages/modplatform/VanillaPage.h
+++ b/launcher/ui/pages/modplatform/VanillaPage.h
@@ -77,15 +77,20 @@ public:
void openedImpl() override;
BaseVersionPtr selectedVersion() const;
+ BaseVersionPtr selectedLoaderVersion() const;
+ QString selectedLoader() const;
public slots:
void setSelectedVersion(BaseVersionPtr version);
+ void setSelectedLoaderVersion(BaseVersionPtr version);
private slots:
void filterChanged();
+ void loaderFilterChanged();
private:
void refresh();
+ void loaderRefresh();
void suggestCurrent();
private:
@@ -94,4 +99,6 @@ private:
Ui::VanillaPage *ui = nullptr;
bool m_versionSetByUser = false;
BaseVersionPtr m_selectedVersion;
+ BaseVersionPtr m_selectedLoaderVersion;
+ QString m_selectedLoader;
};
diff --git a/launcher/ui/pages/modplatform/VanillaPage.ui b/launcher/ui/pages/modplatform/VanillaPage.ui
index 870ff161..43110927 100644
--- a/launcher/ui/pages/modplatform/VanillaPage.ui
+++ b/launcher/ui/pages/modplatform/VanillaPage.ui
@@ -33,113 +33,231 @@
<string notr="true"/>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Filter</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="releaseFilter">
- <property name="text">
- <string>Releases</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="snapshotFilter">
- <property name="text">
- <string>Snapshots</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="oldSnapshotFilter">
- <property name="text">
- <string>Old Snapshots</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- </widget>
- </item>
+ <item row="2" column="0">
+ <widget class="Line" name="line">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="minecraftLayout">
<item>
- <widget class="QCheckBox" name="betaFilter">
- <property name="text">
- <string>Betas</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
+ <widget class="VersionSelectWidget" name="versionList" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="alphaFilter">
- <property name="text">
- <string>Alphas</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- </widget>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Filter</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="releaseFilter">
+ <property name="text">
+ <string>Releases</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="snapshotFilter">
+ <property name="text">
+ <string>Snapshots</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="oldSnapshotFilter">
+ <property name="text">
+ <string>Old Snapshots</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="betaFilter">
+ <property name="text">
+ <string>Betas</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="alphaFilter">
+ <property name="text">
+ <string>Alphas</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="experimentsFilter">
+ <property name="text">
+ <string>Experiments</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="refreshBtn">
+ <property name="text">
+ <string>Refresh</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
+ </layout>
+ </item>
+ <item row="4" column="0">
+ <layout class="QHBoxLayout" name="loaderLayout">
<item>
- <widget class="QCheckBox" name="experimentsFilter">
- <property name="text">
- <string>Experiments</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
+ <widget class="VersionSelectWidget" name="loaderVersionList" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
</widget>
</item>
<item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="refreshBtn">
- <property name="text">
- <string>Refresh</string>
- </property>
- </widget>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="loaderLabel">
+ <property name="text">
+ <string>Mod Loader</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="noneFilter">
+ <property name="text">
+ <string>None</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">loaderBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="forgeFilter">
+ <property name="text">
+ <string>Forge</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">loaderBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="fabricFilter">
+ <property name="text">
+ <string>Fabric</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">loaderBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="quiltFilter">
+ <property name="text">
+ <string>Quilt</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">loaderBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="liteLoaderFilter">
+ <property name="text">
+ <string>LiteLoader</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">loaderBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="loaderRefreshBtn">
+ <property name="text">
+ <string>Refresh</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</item>
- <item row="0" column="0">
- <widget class="VersionSelectWidget" name="versionList" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
</layout>
</widget>
</widget>
@@ -166,4 +284,7 @@
</tabstops>
<resources/>
<connections/>
+ <buttongroups>
+ <buttongroup name="loaderBtnGroup"/>
+ </buttongroups>
</ui>
diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h
index dc58fd7f..d96a0720 100644
--- a/launcher/ui/pages/modplatform/flame/FlameModPage.h
+++ b/launcher/ui/pages/modplatform/flame/FlameModPage.h
@@ -49,7 +49,7 @@ class FlameModPage : public ModPage {
inline auto displayName() const -> QString override { return "CurseForge"; }
inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("flame"); }
inline auto id() const -> QString override { return "curseforge"; }
- inline auto helpPage() const -> QString override { return "Flame-platform"; }
+ inline auto helpPage() const -> QString override { return "Mod-platform"; }
inline auto debugName() const -> QString override { return "Flame"; }
inline auto metaEntryBase() const -> QString override { return "FlameMods"; };
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h
index aa5ed793..0bde43eb 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h
@@ -49,7 +49,7 @@ class ModrinthPage : public ModPage {
inline auto displayName() const -> QString override { return "Modrinth"; }
inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("modrinth"); }
inline auto id() const -> QString override { return "modrinth"; }
- inline auto helpPage() const -> QString override { return "Modrinth-platform"; }
+ inline auto helpPage() const -> QString override { return "Mod-platform"; }
inline auto debugName() const -> QString override { return "Modrinth"; }
inline auto metaEntryBase() const -> QString override { return "ModrinthPacks"; };
diff --git a/launcher/ui/pages/modplatform/technic/TechnicData.h b/launcher/ui/pages/modplatform/technic/TechnicData.h
index 50fd75e8..cd2ea8e1 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicData.h
+++ b/launcher/ui/pages/modplatform/technic/TechnicData.h
@@ -1,22 +1,43 @@
-/* Copyright 2020-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-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 <QList>
#include <QString>
+#include <QVector>
namespace Technic {
struct Modpack {
@@ -36,6 +57,11 @@ struct Modpack {
QString websiteUrl;
QString author;
QString description;
+ QString currentVersion;
+
+ bool versionsLoaded = false;
+ QString recommended;
+ QVector<QString> versions;
};
}
diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
index 0167f746..9c9d1e75 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
@@ -1,20 +1,41 @@
-/* Copyright 2020-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
#include "TechnicModel.h"
#include "Application.h"
+#include "BuildConfig.h"
#include "Json.h"
#include <QIcon>
@@ -94,13 +115,24 @@ void Technic::ListModel::performSearch()
NetJob *netJob = new NetJob("Technic::Search", APPLICATION->network());
QString searchUrl = "";
if (currentSearchTerm.isEmpty()) {
- searchUrl = "https://api.technicpack.net/trending?build=multimc";
+ searchUrl = QString("%1trending?build=%2")
+ .arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD);
+ searchMode = List;
}
- else
- {
+ else if (currentSearchTerm.startsWith("http://api.technicpack.net/modpack/")) {
+ searchUrl = QString("https://%1?build=%2")
+ .arg(currentSearchTerm.mid(7), BuildConfig.TECHNIC_API_BUILD);
+ searchMode = Single;
+ }
+ else if (currentSearchTerm.startsWith("https://api.technicpack.net/modpack/")) {
+ searchUrl = QString("%1?build=%2").arg(currentSearchTerm, BuildConfig.TECHNIC_API_BUILD);
+ searchMode = Single;
+ }
+ else {
searchUrl = QString(
- "https://api.technicpack.net/search?build=multimc&q=%1"
- ).arg(currentSearchTerm);
+ "%1search?build=%2&q=%3"
+ ).arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
+ searchMode = List;
}
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
jobPtr = netJob;
@@ -125,26 +157,58 @@ void Technic::ListModel::searchRequestFinished()
QList<Modpack> newList;
try {
auto root = Json::requireObject(doc);
- auto objs = Json::requireArray(root, "modpacks");
- for (auto technicPack: objs) {
- Modpack pack;
- auto technicPackObject = Json::requireObject(technicPack);
- pack.name = Json::requireString(technicPackObject, "name");
- pack.slug = Json::requireString(technicPackObject, "slug");
- if (pack.slug == "vanilla")
- continue;
-
- auto rawURL = Json::ensureString(technicPackObject, "iconUrl", "null");
- if(rawURL == "null") {
- pack.logoUrl = "null";
- pack.logoName = "null";
+
+ switch (searchMode) {
+ case List: {
+ auto objs = Json::requireArray(root, "modpacks");
+ for (auto technicPack: objs) {
+ Modpack pack;
+ auto technicPackObject = Json::requireObject(technicPack);
+ pack.name = Json::requireString(technicPackObject, "name");
+ pack.slug = Json::requireString(technicPackObject, "slug");
+ if (pack.slug == "vanilla")
+ continue;
+
+ auto rawURL = Json::ensureString(technicPackObject, "iconUrl", "null");
+ if(rawURL == "null") {
+ pack.logoUrl = "null";
+ pack.logoName = "null";
+ }
+ else {
+ pack.logoUrl = rawURL;
+ pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
+ }
+ pack.broken = false;
+ newList.append(pack);
+ }
+ break;
}
- else {
- pack.logoUrl = rawURL;
- pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
+ case Single: {
+ if (root.contains("error")) {
+ // Invalid API url
+ break;
+ }
+
+ Modpack pack;
+ pack.name = Json::requireString(root, "displayName");
+ pack.slug = Json::requireString(root, "name");
+
+ if (root.contains("icon")) {
+ auto iconObj = Json::requireObject(root, "icon");
+ auto iconUrl = Json::requireString(iconObj, "url");
+
+ pack.logoUrl = iconUrl;
+ pack.logoName = iconUrl.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
+ }
+ else {
+ pack.logoUrl = "null";
+ pack.logoName = "null";
+ }
+
+ pack.broken = false;
+ newList.append(pack);
+ break;
}
- pack.broken = false;
- newList.append(pack);
}
}
catch (const JSONValidationError &err)
diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.h b/launcher/ui/pages/modplatform/technic/TechnicModel.h
index e80e6e7c..5eea124c 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicModel.h
+++ b/launcher/ui/pages/modplatform/technic/TechnicModel.h
@@ -1,16 +1,36 @@
-/* Copyright 2020-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-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
@@ -63,6 +83,10 @@ private:
ResetRequested,
Finished
} searchState = None;
+ enum SearchMode {
+ List,
+ Single,
+ } searchMode = List;
NetJob::Ptr jobPtr;
QByteArray response;
};
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
index c3807269..b8c1e00a 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,12 +40,14 @@
#include "ui/dialogs/NewInstanceDialog.h"
+#include "BuildConfig.h"
#include "TechnicModel.h"
#include "modplatform/technic/SingleZipPackInstallTask.h"
#include "modplatform/technic/SolderPackInstallTask.h"
#include "Json.h"
#include "Application.h"
+#include "modplatform/technic/SolderPackManifest.h"
TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
@@ -55,7 +57,9 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
ui->searchEdit->installEventFilter(this);
model = new Technic::ListModel(this);
ui->packView->setModel(model);
+
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged);
+ connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged);
}
bool TechnicPage::eventFilter(QObject* watched, QEvent* event)
@@ -98,13 +102,14 @@ void TechnicPage::triggerSearch() {
void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second)
{
+ ui->versionSelectionBox->clear();
+
if(!first.isValid())
{
if(isOpened)
{
dialog->setSuggestedPack();
}
- //ui->frame->clear();
return;
}
@@ -137,17 +142,19 @@ void TechnicPage::suggestCurrent()
}
NetJob *netJob = new NetJob(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network());
- std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
QString slug = current.slug;
- netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.technicpack.net/modpack/%1?build=multimc").arg(slug), response.get()));
- QObject::connect(netJob, &NetJob::succeeded, this, [this, response, slug]
+ netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response));
+ QObject::connect(netJob, &NetJob::succeeded, this, [this, slug]
{
+ jobPtr.reset();
+
if (current.slug != slug)
{
return;
}
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+
+ QJsonParseError parse_error {};
+ QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
QJsonObject obj = doc.object();
if(parse_error.error != QJsonParseError::NoError)
{
@@ -189,10 +196,14 @@ void TechnicPage::suggestCurrent()
current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__");
current.author = Json::ensureString(obj, "user", QString(), "__placeholder__");
current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
+ current.currentVersion = Json::ensureString(obj, "version", QString(), "__placeholder__");
current.metadataLoaded = true;
+
metadataLoaded();
});
- netJob->start();
+
+ jobPtr = netJob;
+ jobPtr->start();
}
// expects current.metadataLoaded to be true
@@ -202,25 +213,119 @@ void TechnicPage::metadataLoaded()
QString name = current.name;
if (current.websiteUrl.isEmpty())
- // This allows injecting HTML here.
- text = name;
+ text = name.toHtmlEscaped();
else
- // URL not properly escaped for inclusion in HTML. The name allows for injecting HTML.
- text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
+ text = "<a href=\"" + current.websiteUrl.toHtmlEscaped() + "\">" + name.toHtmlEscaped() + "</a>";
+
if (!current.author.isEmpty()) {
- // This allows injecting HTML here
- text += tr(" by ") + current.author;
+ text += "<br>" + tr(" by ") + current.author.toHtmlEscaped();
+ }
+
+ text += "<br><br>";
+
+ ui->packDescription->setHtml(text + current.description);
+
+ // Strip trailing forward-slashes from Solder URL's
+ if (current.isSolder) {
+ while (current.url.endsWith('/')) current.url.chop(1);
+ }
+
+ // Display versions from Solder
+ if (!current.isSolder) {
+ // If the pack isn't a Solder pack, it only has the single version
+ ui->versionSelectionBox->addItem(current.currentVersion);
+ }
+ else if (current.versionsLoaded) {
+ // reverse foreach, so that the newest versions are first
+ for (auto i = current.versions.size(); i--;) {
+ ui->versionSelectionBox->addItem(current.versions.at(i));
+ }
+ ui->versionSelectionBox->setCurrentText(current.recommended);
+ }
+ else {
+ // For now, until the versions are pulled from the Solder instance, display the current
+ // version so we can display something quicker
+ ui->versionSelectionBox->addItem(current.currentVersion);
+
+ auto* netJob = new NetJob(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network());
+ auto url = QString("%1/modpack/%2").arg(current.url, current.slug);
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
+
+ QObject::connect(netJob, &NetJob::succeeded, this, &TechnicPage::onSolderLoaded);
+
+ jobPtr = netJob;
+ jobPtr->start();
+ }
+
+ selectVersion();
+}
+
+void TechnicPage::selectVersion() {
+ if (!isOpened) {
+ return;
+ }
+ if (current.broken) {
+ dialog->setSuggestedPack();
+ return;
}
- ui->frame->setModText(text);
- ui->frame->setModDescription(current.description);
if (!current.isSolder)
{
- dialog->setSuggestedPack(current.name, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));
+ dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));
}
else
{
- while (current.url.endsWith('/')) current.url.chop(1);
- dialog->setSuggestedPack(current.name, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url + "/modpack/" + current.slug, current.minecraftVersion));
+ dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url, current.slug, selectedVersion, current.minecraftVersion));
+ }
+}
+
+void TechnicPage::onSolderLoaded() {
+ jobPtr.reset();
+
+ auto fallback = [this]() {
+ current.versionsLoaded = true;
+
+ current.versions.clear();
+ current.versions.append(current.currentVersion);
+ };
+
+ current.versions.clear();
+
+ QJsonParseError parse_error {};
+ auto doc = QJsonDocument::fromJson(response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << response;
+ fallback();
+ return;
+ }
+ auto obj = doc.object();
+
+ TechnicSolder::Pack pack;
+ try {
+ TechnicSolder::loadPack(pack, obj);
+ }
+ catch (const JSONValidationError& err) {
+ qCritical() << "Couldn't parse Solder pack metadata:" << err.cause();
+ fallback();
+ return;
+ }
+
+ current.versionsLoaded = true;
+ current.recommended = pack.recommended;
+ current.versions.append(pack.builds);
+
+ // Finally, let's reload :)
+ ui->versionSelectionBox->clear();
+ metadataLoaded();
+}
+
+void TechnicPage::onVersionSelectionChanged(QString data) {
+ if (data.isNull() || data.isEmpty()) {
+ selectedVersion = "";
+ return;
}
+
+ selectedVersion = data;
+ selectVersion();
}
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.h b/launcher/ui/pages/modplatform/technic/TechnicPage.h
index bf4baa58..f4a3b61d 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.h
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@
#include "ui/pages/BasePage.h"
#include <Application.h>
+#include "net/NetJob.h"
#include "tasks/Task.h"
#include "TechnicData.h"
@@ -86,14 +87,22 @@ public:
private:
void suggestCurrent();
void metadataLoaded();
+ void selectVersion();
private slots:
void triggerSearch();
void onSelectionChanged(QModelIndex first, QModelIndex second);
+ void onSolderLoaded();
+ void onVersionSelectionChanged(QString data);
private:
Ui::TechnicPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
Technic::ListModel* model = nullptr;
+
Technic::Modpack current;
+ QString selectedVersion;
+
+ NetJob::Ptr jobPtr;
+ QByteArray response;
};
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.ui b/launcher/ui/pages/modplatform/technic/TechnicPage.ui
index 62ab6154..ca6a9b7e 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.ui
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.ui
@@ -10,86 +10,76 @@
<height>405</height>
</rect>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QWidget" name="widget" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <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="QLineEdit" name="searchEdit">
- <property name="placeholderText">
- <string>Search and filter...</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="searchButton">
- <property name="text">
- <string>Search</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="3" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <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">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
</item>
- <item>
- <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>
+ <item row="2" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QListView" name="packView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QTextBrowser" name="packDescription"/>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLineEdit" name="searchEdit">
+ <property name="placeholderText">
+ <string>Search and filter...</string>
</property>
</widget>
</item>
- <item>
- <widget class="MCModInfoFrame" name="frame">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="searchButton">
+ <property name="text">
+ <string>Search</string>
</property>
</widget>
</item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>MCModInfoFrame</class>
- <extends>QFrame</extends>
- <header>ui/widgets/MCModInfoFrame.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <tabstops>
- <tabstop>searchEdit</tabstop>
- <tabstop>searchButton</tabstop>
- <tabstop>packView</tabstop>
- </tabstops>
<resources/>
<connections/>
</ui>
diff --git a/launcher/ui/widgets/CustomCommands.ui b/launcher/ui/widgets/CustomCommands.ui
index dbd54431..650a9cc1 100644
--- a/launcher/ui/widgets/CustomCommands.ui
+++ b/launcher/ui/widgets/CustomCommands.ui
@@ -74,7 +74,7 @@
<item>
<widget class="QLabel" name="labelCustomCmdsDescription">
<property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Pre-launch command runs before the instance launches and post-exit command runs after it exits.&lt;/p&gt;&lt;p&gt;Both will be run in the launcher's working folder with extra environment variables:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;$INST_NAME - Name of the instance&lt;/li&gt;&lt;li&gt;$INST_ID - ID of the instance (its folder name)&lt;/li&gt;&lt;li&gt;$INST_DIR - absolute path of the instance&lt;/li&gt;&lt;li&gt;$INST_MC_DIR - absolute path of Minecraft&lt;/li&gt;&lt;li&gt;$INST_JAVA - Java binary used for launch&lt;/li&gt;&lt;li&gt;$INST_JAVA_ARGS - command-line parameters used for launch&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Wrapper command allows launching using an extra wrapper program (like 'optirun' on Linux)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Pre-launch command runs before the instance launches and post-exit command runs after it exits.&lt;/p&gt;&lt;p&gt;Both will be run in the launcher's working folder with extra environment variables:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;$INST_NAME - Name of the instance&lt;/li&gt;&lt;li&gt;$INST_ID - ID of the instance (its folder name)&lt;/li&gt;&lt;li&gt;$INST_DIR - absolute path of the instance&lt;/li&gt;&lt;li&gt;$INST_MC_DIR - absolute path of Minecraft&lt;/li&gt;&lt;li&gt;$INST_JAVA - Java binary used for launch&lt;/li&gt;&lt;li&gt;$INST_JAVA_ARGS - command-line parameters used for launch (warning: will not work correctly if arguments contain spaces)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Wrapper command allows launching using an extra wrapper program (like 'optirun' on Linux)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp
new file mode 100644
index 00000000..ffc8d05d
--- /dev/null
+++ b/launcher/ui/widgets/ModFilterWidget.cpp
@@ -0,0 +1,109 @@
+#include "ModFilterWidget.h"
+#include "ui_ModFilterWidget.h"
+
+ModFilterWidget::ModFilterWidget(Version def, QWidget* parent)
+ : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget)
+{
+ ui->setupUi(this);
+
+ m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict);
+ ui->strictVersionButton->click();
+ m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major);
+ m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All);
+ //m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between);
+
+ connect(&m_mcVersion_buttons, SIGNAL(idClicked(int)), this, SLOT(onVersionFilterChanged(int)));
+
+ m_filter->versions.push_front(def);
+
+ setHidden(true);
+}
+
+void ModFilterWidget::setInstance(MinecraftInstance* instance)
+{
+ m_instance = instance;
+
+ auto mcVersionSplit = mcVersionStr().split(".");
+
+ ui->strictVersionButton->setText(
+ tr("Strict match (= %1)").arg(mcVersionStr()));
+ ui->majorVersionButton->setText(
+ tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1]));
+ ui->allVersionsButton->setText(
+ tr("Any version"));
+ //ui->betweenVersionsButton->setText(
+ // tr("Between two versions"));
+}
+
+auto ModFilterWidget::getFilter() -> std::shared_ptr<Filter>
+{
+ m_last_version_id = m_version_id;
+ emit filterUnchanged();
+ return m_filter;
+}
+
+void ModFilterWidget::disableVersionButton(VersionButtonID id)
+{
+ switch(id){
+ case(VersionButtonID::Strict):
+ ui->strictVersionButton->setEnabled(false);
+ break;
+ case(VersionButtonID::Major):
+ ui->majorVersionButton->setEnabled(false);
+ break;
+ case(VersionButtonID::All):
+ ui->allVersionsButton->setEnabled(false);
+ break;
+ case(VersionButtonID::Between):
+ // ui->betweenVersionsButton->setEnabled(false);
+ break;
+ default:
+ break;
+ }
+}
+
+void ModFilterWidget::onVersionFilterChanged(int id)
+{
+ //ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between);
+ //ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between);
+
+ auto versionSplit = mcVersionStr().split(".");
+ int index = 0;
+
+ auto cast_id = (VersionButtonID) id;
+ if (cast_id != m_version_id) {
+ m_version_id = cast_id;
+ } else {
+ return;
+ }
+
+ m_filter->versions.clear();
+
+ switch(cast_id){
+ case(VersionButtonID::Strict):
+ m_filter->versions.push_front(mcVersion());
+ break;
+ case(VersionButtonID::Major):
+ for(auto i = Version(QString("%1.%2").arg(versionSplit[0], versionSplit[1])); i <= mcVersion(); index++){
+ m_filter->versions.push_front(i);
+ i = Version(QString("%1.%2.%3").arg(versionSplit[0], versionSplit[1], QString("%1").arg(index)));
+ }
+ break;
+ case(VersionButtonID::All):
+ // Empty list to avoid enumerating all versions :P
+ break;
+ case(VersionButtonID::Between):
+ // TODO
+ break;
+ }
+
+ if(changed())
+ emit filterChanged();
+ else
+ emit filterUnchanged();
+}
+
+ModFilterWidget::~ModFilterWidget()
+{
+ delete ui;
+}
diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h
new file mode 100644
index 00000000..334fc672
--- /dev/null
+++ b/launcher/ui/widgets/ModFilterWidget.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <QTabWidget>
+#include <QButtonGroup>
+
+#include "Version.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+class MinecraftInstance;
+
+namespace Ui {
+class ModFilterWidget;
+}
+
+class ModFilterWidget : public QTabWidget
+{
+ Q_OBJECT
+public:
+ enum VersionButtonID {
+ Strict = 0,
+ Major = 1,
+ All = 2,
+ Between = 3
+ };
+
+ struct Filter {
+ std::list<Version> versions;
+
+ bool operator==(const Filter& other) const { return versions == other.versions; }
+ bool operator!=(const Filter& other) const { return !(*this == other); }
+ };
+
+ std::shared_ptr<Filter> m_filter;
+
+public:
+ explicit ModFilterWidget(Version def, QWidget* parent = nullptr);
+ ~ModFilterWidget();
+
+ void setInstance(MinecraftInstance* instance);
+
+ /// By default all buttons are enabled
+ void disableVersionButton(VersionButtonID);
+
+ auto getFilter() -> std::shared_ptr<Filter>;
+ auto changed() const -> bool { return m_last_version_id != m_version_id; }
+
+private:
+ inline auto mcVersionStr() const -> QString { return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; }
+ inline auto mcVersion() const -> Version { return { mcVersionStr() }; }
+
+private slots:
+ void onVersionFilterChanged(int id);
+
+public: signals:
+ void filterChanged();
+ void filterUnchanged();
+
+private:
+ Ui::ModFilterWidget* ui;
+
+ MinecraftInstance* m_instance = nullptr;
+
+ QButtonGroup m_mcVersion_buttons;
+
+ /* Used to tell if the filter was changed since the last getFilter() call */
+ VersionButtonID m_last_version_id = VersionButtonID::Strict;
+ VersionButtonID m_version_id = VersionButtonID::Strict;
+};
diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui
new file mode 100644
index 00000000..ad1090e2
--- /dev/null
+++ b/launcher/ui/widgets/ModFilterWidget.ui
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ModFilterWidget</class>
+ <widget class="QTabWidget" name="ModFilterWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget" name="VersionPage">
+ <attribute name="title">
+ <string>Minecraft versions</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <layout class="QFormLayout" name="formLayout">
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="allVersionsButton">
+ <property name="text">
+ <string>allVersions</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="strictVersionButton">
+ <property name="text">
+ <string>strictVersion</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QRadioButton" name="majorVersionButton">
+ <property name="text">
+ <string>majorVersion</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp
index 1209f118..cc4fc6a2 100644
--- a/launcher/ui/widgets/VersionSelectWidget.cpp
+++ b/launcher/ui/widgets/VersionSelectWidget.cpp
@@ -4,7 +4,6 @@
#include <QVBoxLayout>
#include <QHeaderView>
-#include "VersionListView.h"
#include "VersionProxyModel.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -57,6 +56,11 @@ void VersionSelectWidget::setEmptyErrorString(QString emptyErrorString)
listView->setEmptyErrorString(emptyErrorString);
}
+void VersionSelectWidget::setEmptyMode(VersionListView::EmptyMode mode)
+{
+ listView->setEmptyMode(mode);
+}
+
VersionSelectWidget::~VersionSelectWidget()
{
}
diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h
index 0a649408..f56daa8a 100644
--- a/launcher/ui/widgets/VersionSelectWidget.h
+++ b/launcher/ui/widgets/VersionSelectWidget.h
@@ -18,6 +18,7 @@
#include <QWidget>
#include <QSortFilterProxyModel>
#include "BaseVersionList.h"
+#include "VersionListView.h"
class VersionProxyModel;
class VersionListView;
@@ -49,6 +50,7 @@ public:
void setFilter(BaseVersionList::ModelRoles role, Filter *filter);
void setEmptyString(QString emptyString);
void setEmptyErrorString(QString emptyErrorString);
+ void setEmptyMode(VersionListView::EmptyMode mode);
void setResizeOn(int column);
signals: