Merge pull request #384 from jamierocks/technic-improvements
This commit is contained in:
commit
99d569ed0e
@ -140,6 +140,12 @@ public:
|
|||||||
|
|
||||||
QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
|
QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
|
||||||
|
|
||||||
|
QString TECHNIC_API_BASE_URL = "https://api.technicpack.net/";
|
||||||
|
/**
|
||||||
|
* The build that is reported to the Technic API.
|
||||||
|
*/
|
||||||
|
QString TECHNIC_API_BUILD = "multimc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Converts the Version to a string.
|
* \brief Converts the Version to a string.
|
||||||
* \return The version number in string format (major.minor.revision.build).
|
* \return The version number in string format (major.minor.revision.build).
|
||||||
|
@ -539,6 +539,8 @@ set(TECHNIC_SOURCES
|
|||||||
modplatform/technic/SingleZipPackInstallTask.cpp
|
modplatform/technic/SingleZipPackInstallTask.cpp
|
||||||
modplatform/technic/SolderPackInstallTask.h
|
modplatform/technic/SolderPackInstallTask.h
|
||||||
modplatform/technic/SolderPackInstallTask.cpp
|
modplatform/technic/SolderPackInstallTask.cpp
|
||||||
|
modplatform/technic/SolderPackManifest.h
|
||||||
|
modplatform/technic/SolderPackManifest.cpp
|
||||||
modplatform/technic/TechnicPackProcessor.h
|
modplatform/technic/TechnicPackProcessor.h
|
||||||
modplatform/technic/TechnicPackProcessor.cpp
|
modplatform/technic/TechnicPackProcessor.cpp
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -19,16 +39,23 @@
|
|||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include <MMCZip.h>
|
#include <MMCZip.h>
|
||||||
|
|
||||||
#include "TechnicPackProcessor.h"
|
#include "TechnicPackProcessor.h"
|
||||||
|
#include "SolderPackManifest.h"
|
||||||
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
Technic::SolderPackInstallTask::SolderPackInstallTask(
|
Technic::SolderPackInstallTask::SolderPackInstallTask(
|
||||||
shared_qobject_ptr<QNetworkAccessManager> network,
|
shared_qobject_ptr<QNetworkAccessManager> network,
|
||||||
const QUrl &sourceUrl,
|
const QUrl &solderUrl,
|
||||||
|
const QString &pack,
|
||||||
|
const QString &version,
|
||||||
const QString &minecraftVersion
|
const QString &minecraftVersion
|
||||||
) {
|
) {
|
||||||
m_sourceUrl = sourceUrl;
|
m_solderUrl = solderUrl;
|
||||||
m_minecraftVersion = minecraftVersion;
|
m_pack = pack;
|
||||||
|
m_version = version;
|
||||||
m_network = network;
|
m_network = network;
|
||||||
|
m_minecraftVersion = minecraftVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Technic::SolderPackInstallTask::abort() {
|
bool Technic::SolderPackInstallTask::abort() {
|
||||||
@ -41,34 +68,12 @@ bool Technic::SolderPackInstallTask::abort() {
|
|||||||
|
|
||||||
void Technic::SolderPackInstallTask::executeTask()
|
void Technic::SolderPackInstallTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Resolving modpack files"));
|
||||||
m_filesNetJob = new NetJob(tr("Finding recommended version"), m_network);
|
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
|
|
||||||
auto job = m_filesNetJob.get();
|
|
||||||
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded);
|
|
||||||
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
|
|
||||||
m_filesNetJob->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Technic::SolderPackInstallTask::versionSucceeded()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
QJsonDocument doc = Json::requireDocument(m_response);
|
|
||||||
QJsonObject obj = Json::requireObject(doc);
|
|
||||||
QString version = Json::requireString(obj, "recommended", "__placeholder__");
|
|
||||||
m_sourceUrl = m_sourceUrl.toString() + '/' + version;
|
|
||||||
}
|
|
||||||
catch (const JSONValidationError &e)
|
|
||||||
{
|
|
||||||
emitFailed(e.cause());
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatus(tr("Resolving modpack files:\n%1").arg(m_sourceUrl.toString()));
|
|
||||||
m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network);
|
m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network);
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
|
auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
|
||||||
|
m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response));
|
||||||
|
|
||||||
auto job = m_filesNetJob.get();
|
auto job = m_filesNetJob.get();
|
||||||
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
|
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
|
||||||
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
|
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
|
||||||
@ -77,38 +82,47 @@ void Technic::SolderPackInstallTask::versionSucceeded()
|
|||||||
|
|
||||||
void Technic::SolderPackInstallTask::fileListSucceeded()
|
void Technic::SolderPackInstallTask::fileListSucceeded()
|
||||||
{
|
{
|
||||||
setStatus(tr("Downloading modpack:"));
|
setStatus(tr("Downloading modpack"));
|
||||||
QStringList modUrls;
|
|
||||||
try
|
QJsonParseError parse_error {};
|
||||||
{
|
QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
|
||||||
QJsonDocument doc = Json::requireDocument(m_response);
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
QJsonObject obj = Json::requireObject(doc);
|
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||||
QString minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
|
qWarning() << m_response;
|
||||||
if (!minecraftVersion.isEmpty())
|
return;
|
||||||
m_minecraftVersion = minecraftVersion;
|
|
||||||
QJsonArray mods = Json::requireArray(obj, "mods", "'mods'");
|
|
||||||
for (auto mod: mods)
|
|
||||||
{
|
|
||||||
QJsonObject modObject = Json::requireObject(mod);
|
|
||||||
modUrls.append(Json::requireString(modObject, "url", "'url'"));
|
|
||||||
}
|
}
|
||||||
|
auto obj = doc.object();
|
||||||
|
|
||||||
|
TechnicSolder::PackBuild build;
|
||||||
|
try {
|
||||||
|
TechnicSolder::loadPackBuild(build, obj);
|
||||||
}
|
}
|
||||||
catch (const JSONValidationError &e)
|
catch (const JSONValidationError& e) {
|
||||||
{
|
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||||
emitFailed(e.cause());
|
|
||||||
m_filesNetJob.reset();
|
m_filesNetJob.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!build.minecraft.isEmpty())
|
||||||
|
m_minecraftVersion = build.minecraft;
|
||||||
|
|
||||||
m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network);
|
m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto &modUrl: modUrls)
|
for (const auto &mod : build.mods) {
|
||||||
{
|
|
||||||
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
|
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, path));
|
|
||||||
|
auto dl = Net::Download::makeFile(mod.url, path);
|
||||||
|
if (!mod.md5.isEmpty()) {
|
||||||
|
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||||
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||||
|
}
|
||||||
|
m_filesNetJob->addNetAction(dl);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_modCount = modUrls.size();
|
m_modCount = build.mods.size();
|
||||||
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
|
||||||
@ -206,6 +220,5 @@ void Technic::SolderPackInstallTask::extractFinished()
|
|||||||
void Technic::SolderPackInstallTask::extractAborted()
|
void Technic::SolderPackInstallTask::extractAborted()
|
||||||
{
|
{
|
||||||
emitFailed(tr("Instance import has been aborted."));
|
emitFailed(tr("Instance import has been aborted."));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -27,7 +47,7 @@ namespace Technic
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &sourceUrl, const QString &minecraftVersion);
|
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &solderUrl, const QString& pack, const QString& version, const QString &minecraftVersion);
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
@ -37,7 +57,6 @@ namespace Technic
|
|||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void versionSucceeded();
|
|
||||||
void fileListSucceeded();
|
void fileListSucceeded();
|
||||||
void downloadSucceeded();
|
void downloadSucceeded();
|
||||||
void downloadFailed(QString reason);
|
void downloadFailed(QString reason);
|
||||||
@ -51,7 +70,9 @@ namespace Technic
|
|||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
|
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
QUrl m_sourceUrl;
|
QUrl m_solderUrl;
|
||||||
|
QString m_pack;
|
||||||
|
QString m_version;
|
||||||
QString m_minecraftVersion;
|
QString m_minecraftVersion;
|
||||||
QByteArray m_response;
|
QByteArray m_response;
|
||||||
QTemporaryDir m_outputDir;
|
QTemporaryDir m_outputDir;
|
||||||
|
58
launcher/modplatform/technic/SolderPackManifest.cpp
Normal file
58
launcher/modplatform/technic/SolderPackManifest.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (c) 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SolderPackManifest.h"
|
||||||
|
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
|
namespace TechnicSolder {
|
||||||
|
|
||||||
|
void loadPack(Pack& v, QJsonObject& obj)
|
||||||
|
{
|
||||||
|
v.recommended = Json::requireString(obj, "recommended");
|
||||||
|
v.latest = Json::requireString(obj, "latest");
|
||||||
|
|
||||||
|
auto builds = Json::requireArray(obj, "builds");
|
||||||
|
for (const auto buildRaw : builds) {
|
||||||
|
auto build = Json::requireString(buildRaw);
|
||||||
|
v.builds.append(build);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj)
|
||||||
|
{
|
||||||
|
b.name = Json::requireString(obj, "name");
|
||||||
|
b.version = Json::requireString(obj, "version");
|
||||||
|
b.md5 = Json::requireString(obj, "md5");
|
||||||
|
b.url = Json::requireString(obj, "url");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadPackBuild(PackBuild& v, QJsonObject& obj)
|
||||||
|
{
|
||||||
|
v.minecraft = Json::requireString(obj, "minecraft");
|
||||||
|
|
||||||
|
auto mods = Json::requireArray(obj, "mods");
|
||||||
|
for (const auto modRaw : mods) {
|
||||||
|
auto modObj = Json::requireObject(modRaw);
|
||||||
|
PackBuildMod mod;
|
||||||
|
loadPackBuildMod(mod, modObj);
|
||||||
|
v.mods.append(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
launcher/modplatform/technic/SolderPackManifest.h
Normal file
49
launcher/modplatform/technic/SolderPackManifest.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (c) 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
namespace TechnicSolder {
|
||||||
|
|
||||||
|
struct Pack {
|
||||||
|
QString recommended;
|
||||||
|
QString latest;
|
||||||
|
QVector<QString> builds;
|
||||||
|
};
|
||||||
|
|
||||||
|
void loadPack(Pack& v, QJsonObject& obj);
|
||||||
|
|
||||||
|
struct PackBuildMod {
|
||||||
|
QString name;
|
||||||
|
QString version;
|
||||||
|
QString md5;
|
||||||
|
QString url;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PackBuild {
|
||||||
|
QString minecraft;
|
||||||
|
QVector<PackBuildMod> mods;
|
||||||
|
};
|
||||||
|
|
||||||
|
void loadPackBuild(PackBuild& v, QJsonObject& obj);
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2020-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,6 +37,7 @@
|
|||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
namespace Technic {
|
namespace Technic {
|
||||||
struct Modpack {
|
struct Modpack {
|
||||||
@ -36,6 +57,11 @@ struct Modpack {
|
|||||||
QString websiteUrl;
|
QString websiteUrl;
|
||||||
QString author;
|
QString author;
|
||||||
QString description;
|
QString description;
|
||||||
|
QString currentVersion;
|
||||||
|
|
||||||
|
bool versionsLoaded = false;
|
||||||
|
QString recommended;
|
||||||
|
QVector<QString> versions;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2020-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (c) 2021 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,6 +35,7 @@
|
|||||||
|
|
||||||
#include "TechnicModel.h"
|
#include "TechnicModel.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
@ -94,13 +115,24 @@ void Technic::ListModel::performSearch()
|
|||||||
NetJob *netJob = new NetJob("Technic::Search", APPLICATION->network());
|
NetJob *netJob = new NetJob("Technic::Search", APPLICATION->network());
|
||||||
QString searchUrl = "";
|
QString searchUrl = "";
|
||||||
if (currentSearchTerm.isEmpty()) {
|
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(
|
searchUrl = QString(
|
||||||
"https://api.technicpack.net/search?build=multimc&q=%1"
|
"%1search?build=%2&q=%3"
|
||||||
).arg(currentSearchTerm);
|
).arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
|
||||||
|
searchMode = List;
|
||||||
}
|
}
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
||||||
jobPtr = netJob;
|
jobPtr = netJob;
|
||||||
@ -125,6 +157,9 @@ void Technic::ListModel::searchRequestFinished()
|
|||||||
QList<Modpack> newList;
|
QList<Modpack> newList;
|
||||||
try {
|
try {
|
||||||
auto root = Json::requireObject(doc);
|
auto root = Json::requireObject(doc);
|
||||||
|
|
||||||
|
switch (searchMode) {
|
||||||
|
case List: {
|
||||||
auto objs = Json::requireArray(root, "modpacks");
|
auto objs = Json::requireArray(root, "modpacks");
|
||||||
for (auto technicPack: objs) {
|
for (auto technicPack: objs) {
|
||||||
Modpack pack;
|
Modpack pack;
|
||||||
@ -146,6 +181,35 @@ void Technic::ListModel::searchRequestFinished()
|
|||||||
pack.broken = false;
|
pack.broken = false;
|
||||||
newList.append(pack);
|
newList.append(pack);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const JSONValidationError &err)
|
catch (const JSONValidationError &err)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2020-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (c) 2021 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -63,6 +83,10 @@ private:
|
|||||||
ResetRequested,
|
ResetRequested,
|
||||||
Finished
|
Finished
|
||||||
} searchState = None;
|
} searchState = None;
|
||||||
|
enum SearchMode {
|
||||||
|
List,
|
||||||
|
Single,
|
||||||
|
} searchMode = List;
|
||||||
NetJob::Ptr jobPtr;
|
NetJob::Ptr jobPtr;
|
||||||
QByteArray response;
|
QByteArray response;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -40,12 +40,14 @@
|
|||||||
|
|
||||||
#include "ui/dialogs/NewInstanceDialog.h"
|
#include "ui/dialogs/NewInstanceDialog.h"
|
||||||
|
|
||||||
|
#include "BuildConfig.h"
|
||||||
#include "TechnicModel.h"
|
#include "TechnicModel.h"
|
||||||
#include "modplatform/technic/SingleZipPackInstallTask.h"
|
#include "modplatform/technic/SingleZipPackInstallTask.h"
|
||||||
#include "modplatform/technic/SolderPackInstallTask.h"
|
#include "modplatform/technic/SolderPackInstallTask.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "modplatform/technic/SolderPackManifest.h"
|
||||||
|
|
||||||
TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
|
TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
|
||||||
: QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
|
: QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
|
||||||
@ -55,7 +57,9 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
|
|||||||
ui->searchEdit->installEventFilter(this);
|
ui->searchEdit->installEventFilter(this);
|
||||||
model = new Technic::ListModel(this);
|
model = new Technic::ListModel(this);
|
||||||
ui->packView->setModel(model);
|
ui->packView->setModel(model);
|
||||||
|
|
||||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged);
|
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged);
|
||||||
|
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TechnicPage::eventFilter(QObject* watched, QEvent* event)
|
bool TechnicPage::eventFilter(QObject* watched, QEvent* event)
|
||||||
@ -98,13 +102,14 @@ void TechnicPage::triggerSearch() {
|
|||||||
|
|
||||||
void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
||||||
{
|
{
|
||||||
|
ui->versionSelectionBox->clear();
|
||||||
|
|
||||||
if(!first.isValid())
|
if(!first.isValid())
|
||||||
{
|
{
|
||||||
if(isOpened)
|
if(isOpened)
|
||||||
{
|
{
|
||||||
dialog->setSuggestedPack();
|
dialog->setSuggestedPack();
|
||||||
}
|
}
|
||||||
//ui->frame->clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,17 +142,19 @@ void TechnicPage::suggestCurrent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
NetJob *netJob = new NetJob(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network());
|
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;
|
QString slug = current.slug;
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.technicpack.net/modpack/%1?build=multimc").arg(slug), response.get()));
|
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, response, slug]
|
QObject::connect(netJob, &NetJob::succeeded, this, [this, slug]
|
||||||
{
|
{
|
||||||
|
jobPtr.reset();
|
||||||
|
|
||||||
if (current.slug != slug)
|
if (current.slug != slug)
|
||||||
{
|
{
|
||||||
return;
|
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();
|
QJsonObject obj = doc.object();
|
||||||
if(parse_error.error != QJsonParseError::NoError)
|
if(parse_error.error != QJsonParseError::NoError)
|
||||||
{
|
{
|
||||||
@ -189,10 +196,14 @@ void TechnicPage::suggestCurrent()
|
|||||||
current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__");
|
current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__");
|
||||||
current.author = Json::ensureString(obj, "user", QString(), "__placeholder__");
|
current.author = Json::ensureString(obj, "user", QString(), "__placeholder__");
|
||||||
current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
|
current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
|
||||||
|
current.currentVersion = Json::ensureString(obj, "version", QString(), "__placeholder__");
|
||||||
current.metadataLoaded = true;
|
current.metadataLoaded = true;
|
||||||
|
|
||||||
metadataLoaded();
|
metadataLoaded();
|
||||||
});
|
});
|
||||||
netJob->start();
|
|
||||||
|
jobPtr = netJob;
|
||||||
|
jobPtr->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// expects current.metadataLoaded to be true
|
// expects current.metadataLoaded to be true
|
||||||
@ -202,25 +213,119 @@ void TechnicPage::metadataLoaded()
|
|||||||
QString name = current.name;
|
QString name = current.name;
|
||||||
|
|
||||||
if (current.websiteUrl.isEmpty())
|
if (current.websiteUrl.isEmpty())
|
||||||
// This allows injecting HTML here.
|
text = name.toHtmlEscaped();
|
||||||
text = name;
|
|
||||||
else
|
else
|
||||||
// URL not properly escaped for inclusion in HTML. The name allows for injecting HTML.
|
text = "<a href=\"" + current.websiteUrl.toHtmlEscaped() + "\">" + name.toHtmlEscaped() + "</a>";
|
||||||
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
|
|
||||||
if (!current.author.isEmpty()) {
|
if (!current.author.isEmpty()) {
|
||||||
// This allows injecting HTML here
|
text += "<br>" + tr(" by ") + current.author.toHtmlEscaped();
|
||||||
text += tr(" by ") + current.author;
|
}
|
||||||
|
|
||||||
|
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)
|
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
|
else
|
||||||
{
|
{
|
||||||
while (current.url.endsWith('/')) current.url.chop(1);
|
dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url, current.slug, selectedVersion, current.minecraftVersion));
|
||||||
dialog->setSuggestedPack(current.name, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url + "/modpack/" + current.slug, 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();
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#include "ui/pages/BasePage.h"
|
#include "ui/pages/BasePage.h"
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
|
#include "net/NetJob.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
#include "TechnicData.h"
|
#include "TechnicData.h"
|
||||||
|
|
||||||
@ -86,14 +87,22 @@ public:
|
|||||||
private:
|
private:
|
||||||
void suggestCurrent();
|
void suggestCurrent();
|
||||||
void metadataLoaded();
|
void metadataLoaded();
|
||||||
|
void selectVersion();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void triggerSearch();
|
void triggerSearch();
|
||||||
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
||||||
|
void onSolderLoaded();
|
||||||
|
void onVersionSelectionChanged(QString data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::TechnicPage *ui = nullptr;
|
Ui::TechnicPage *ui = nullptr;
|
||||||
NewInstanceDialog* dialog = nullptr;
|
NewInstanceDialog* dialog = nullptr;
|
||||||
Technic::ListModel* model = nullptr;
|
Technic::ListModel* model = nullptr;
|
||||||
|
|
||||||
Technic::Modpack current;
|
Technic::Modpack current;
|
||||||
|
QString selectedVersion;
|
||||||
|
|
||||||
|
NetJob::Ptr jobPtr;
|
||||||
|
QByteArray response;
|
||||||
};
|
};
|
||||||
|
@ -10,44 +10,44 @@
|
|||||||
<height>405</height>
|
<height>405</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item>
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QWidget" name="widget" native="true">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<item row="0" column="2">
|
||||||
<property name="leftMargin">
|
<widget class="QComboBox" name="versionSelectionBox"/>
|
||||||
<number>0</number>
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Version selected:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="alignment">
|
||||||
<number>0</number>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</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>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QPushButton" name="searchButton">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="text">
|
<property name="orientation">
|
||||||
<string>Search</string>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Preferred</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>1</width>
|
||||||
|
<height>1</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="2" column="0" colspan="2">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
<widget class="QListView" name="packView">
|
<widget class="QListView" name="packView">
|
||||||
<property name="horizontalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
<property name="alternatingRowColors">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -59,37 +59,27 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="1">
|
||||||
<widget class="MCModInfoFrame" name="frame">
|
<widget class="QTextBrowser" name="packDescription"/>
|
||||||
<property name="sizePolicy">
|
</item>
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
</layout>
|
||||||
<horstretch>0</horstretch>
|
</item>
|
||||||
<verstretch>0</verstretch>
|
<item row="1" column="0">
|
||||||
</sizepolicy>
|
<widget class="QLineEdit" name="searchEdit">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Search and filter...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShape">
|
</widget>
|
||||||
<enum>QFrame::StyledPanel</enum>
|
</item>
|
||||||
</property>
|
<item row="1" column="1">
|
||||||
<property name="frameShadow">
|
<widget class="QPushButton" name="searchButton">
|
||||||
<enum>QFrame::Raised</enum>
|
<property name="text">
|
||||||
|
<string>Search</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</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/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
Loading…
Reference in New Issue
Block a user