Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into change_version

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2024-05-31 10:20:48 +03:00
commit 817ff1ecbd
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
21 changed files with 155 additions and 123 deletions

View File

@ -25,7 +25,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v2.5.0 uses: korthout/backport-action@v3.0.2
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

View File

@ -611,7 +611,7 @@ jobs:
flatpak: flatpak:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08 image: bilelmoussaoui/flatpak-github-actions:kde-6.7
options: --privileged options: --privileged
steps: steps:
- name: Checkout - name: Checkout

View File

@ -17,7 +17,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 - uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
- uses: DeterminateSystems/update-flake-lock@v21 - uses: DeterminateSystems/update-flake-lock@v21
with: with:

52
flake.lock generated
View File

@ -23,11 +23,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1714641030, "lastModified": 1715865404,
"narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=", "narHash": "sha256-/GJvTdTpuDjNn84j82cU6bXztE0MSkdnTWClUCRub78=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e", "rev": "8dc45382d5206bd292f9c2768b8058a8fd8311d9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -36,24 +36,6 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": { "gitignore": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -93,11 +75,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1715413075, "lastModified": 1716619601,
"narHash": "sha256-FCi3R1MeS5bVp0M0xTheveP6hhcCYfW/aghSTPebYL4=", "narHash": "sha256-9dUxZf8MOqJH3vjbhrz7LH4qTcnRsPSBU1Q50T7q/X8=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e4e7a43a9db7e22613accfeb1005cca1b2b1ee0d", "rev": "47e03a624662ce399e55c45a5f6da698fc72c797",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -112,7 +94,6 @@
"flake-compat": [ "flake-compat": [
"flake-compat" "flake-compat"
], ],
"flake-utils": "flake-utils",
"gitignore": "gitignore", "gitignore": "gitignore",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
@ -122,11 +103,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1714478972, "lastModified": 1716213921,
"narHash": "sha256-q//cgb52vv81uOuwz1LaXElp3XAe1TqrABXODAEF6Sk=", "narHash": "sha256-xrsYFST8ij4QWaV6HEokCUNIZLjjLP1bYC60K8XiBVA=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "2849da033884f54822af194400f8dff435ada242", "rev": "0e8fcc54b842ad8428c9e705cb5994eaf05c26a0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -143,21 +124,6 @@
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks" "pre-commit-hooks": "pre-commit-hooks"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform runtime: org.kde.Platform
runtime-version: 5.15-23.08 runtime-version: 6.7
sdk: org.kde.Sdk sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk21 - org.freedesktop.Sdk.Extension.openjdk21
@ -38,7 +38,6 @@ modules:
config-opts: config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak - -DLauncher_BUILD_PLATFORM=flatpak
- -DCMAKE_BUILD_TYPE=RelWithDebInfo - -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DLauncher_QT_VERSION_MAJOR=5
build-options: build-options:
env: env:
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17

View File

@ -378,6 +378,7 @@ enum class FilesystemType {
HFSX, HFSX,
FUSEBLK, FUSEBLK,
F2FS, F2FS,
BCACHEFS,
UNKNOWN UNKNOWN
}; };
@ -406,6 +407,7 @@ static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { Fil
{ FilesystemType::HFSX, { "HFSX" } }, { FilesystemType::HFSX, { "HFSX" } },
{ FilesystemType::FUSEBLK, { "FUSEBLK" } }, { FilesystemType::FUSEBLK, { "FUSEBLK" } },
{ FilesystemType::F2FS, { "F2FS" } }, { FilesystemType::F2FS, { "F2FS" } },
{ FilesystemType::BCACHEFS, { "BCACHEFS" } },
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } }; { FilesystemType::UNKNOWN, { "UNKNOWN" } } };
/** /**
@ -458,7 +460,7 @@ QString nearestExistentAncestor(const QString& path);
FilesystemInfo statFS(const QString& path); FilesystemInfo statFS(const QString& path);
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS, static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
FilesystemType::XFS, FilesystemType::REFS }; FilesystemType::XFS, FilesystemType::REFS, FilesystemType::BCACHEFS };
/** /**
* @brief if the Filesystem is reflink/clone capable * @brief if the Filesystem is reflink/clone capable

View File

@ -1000,7 +1000,7 @@ QString MinecraftInstance::getStatusbarDescription()
QString description; QString description;
description.append(tr("Minecraft %1").arg(mcVersion)); description.append(tr("Minecraft %1").arg(mcVersion));
if (m_settings->get("ShowGameTime").toBool()) { if (m_settings->get("ShowGameTime").toBool()) {
if (lastTimePlayed() > 0) { if (lastTimePlayed() > 0 && lastLaunch() > 0) {
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch()); QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
description.append( description.append(
tr(", last played on %1 for %2") tr(", last played on %1 for %2")

View File

@ -42,17 +42,28 @@ QString toHTML(QList<Mod*> mods, OptionalData extraData)
} }
if (extraData & Authors && !mod->authors().isEmpty()) if (extraData & Authors && !mod->authors().isEmpty())
line += " by " + mod->authors().join(", ").toHtmlEscaped(); line += " by " + mod->authors().join(", ").toHtmlEscaped();
if (extraData & FileName)
line += QString(" (%1)").arg(mod->fileinfo().fileName().toHtmlEscaped());
lines.append(QString("<li>%1</li>").arg(line)); lines.append(QString("<li>%1</li>").arg(line));
} }
return QString("<html><body><ul>\n\t%1\n</ul></body></html>").arg(lines.join("\n\t")); return QString("<html><body><ul>\n\t%1\n</ul></body></html>").arg(lines.join("\n\t"));
} }
QString toMarkdownEscaped(QString src)
{
for (auto ch : "\\`*_{}[]<>()#+-.!|")
src.replace(ch, QString("\\%1").arg(ch));
return src;
}
QString toMarkdown(QList<Mod*> mods, OptionalData extraData) QString toMarkdown(QList<Mod*> mods, OptionalData extraData)
{ {
QStringList lines; QStringList lines;
for (auto mod : mods) { for (auto mod : mods) {
auto meta = mod->metadata(); auto meta = mod->metadata();
auto modName = mod->name(); auto modName = toMarkdownEscaped(mod->name());
if (extraData & Url) { if (extraData & Url) {
auto url = mod->metaurl(); auto url = mod->metaurl();
if (!url.isEmpty()) if (!url.isEmpty())
@ -60,14 +71,16 @@ QString toMarkdown(QList<Mod*> mods, OptionalData extraData)
} }
auto line = modName; auto line = modName;
if (extraData & Version) { if (extraData & Version) {
auto ver = mod->version(); auto ver = toMarkdownEscaped(mod->version());
if (ver.isEmpty() && meta != nullptr) if (ver.isEmpty() && meta != nullptr)
ver = meta->version().toString(); ver = toMarkdownEscaped(meta->version().toString());
if (!ver.isEmpty()) if (!ver.isEmpty())
line += QString(" [%1]").arg(ver); line += QString(" [%1]").arg(ver);
} }
if (extraData & Authors && !mod->authors().isEmpty()) if (extraData & Authors && !mod->authors().isEmpty())
line += " by " + mod->authors().join(", "); line += " by " + toMarkdownEscaped(mod->authors().join(", "));
if (extraData & FileName)
line += QString(" (%1)").arg(toMarkdownEscaped(mod->fileinfo().fileName()));
lines << "- " + line; lines << "- " + line;
} }
return lines.join("\n"); return lines.join("\n");
@ -95,6 +108,8 @@ QString toPlainTXT(QList<Mod*> mods, OptionalData extraData)
} }
if (extraData & Authors && !mod->authors().isEmpty()) if (extraData & Authors && !mod->authors().isEmpty())
line += " by " + mod->authors().join(", "); line += " by " + mod->authors().join(", ");
if (extraData & FileName)
line += QString(" (%1)").arg(mod->fileinfo().fileName());
lines << line; lines << line;
} }
return lines.join("\n"); return lines.join("\n");
@ -122,6 +137,8 @@ QString toJSON(QList<Mod*> mods, OptionalData extraData)
} }
if (extraData & Authors && !mod->authors().isEmpty()) if (extraData & Authors && !mod->authors().isEmpty())
line["authors"] = QJsonArray::fromStringList(mod->authors()); line["authors"] = QJsonArray::fromStringList(mod->authors());
if (extraData & FileName)
line["filename"] = mod->fileinfo().fileName();
lines << line; lines << line;
} }
QJsonDocument doc; QJsonDocument doc;
@ -154,6 +171,8 @@ QString toCSV(QList<Mod*> mods, OptionalData extraData)
authors = QString("\"%1\"").arg(mod->authors().join(",")); authors = QString("\"%1\"").arg(mod->authors().join(","));
data << authors; data << authors;
} }
if (extraData & FileName)
data << mod->fileinfo().fileName();
lines << data.join(","); lines << data.join(",");
} }
return lines.join("\n"); return lines.join("\n");
@ -189,11 +208,13 @@ QString exportToModList(QList<Mod*> mods, QString lineTemplate)
if (ver.isEmpty() && meta != nullptr) if (ver.isEmpty() && meta != nullptr)
ver = meta->version().toString(); ver = meta->version().toString();
auto authors = mod->authors().join(", "); auto authors = mod->authors().join(", ");
auto filename = mod->fileinfo().fileName();
lines << QString(lineTemplate) lines << QString(lineTemplate)
.replace("{name}", modName) .replace("{name}", modName)
.replace("{url}", url) .replace("{url}", url)
.replace("{version}", ver) .replace("{version}", ver)
.replace("{authors}", authors); .replace("{authors}", authors)
.replace("{filename}", filename);
} }
return lines.join("\n"); return lines.join("\n");
} }

View File

@ -23,11 +23,7 @@
namespace ExportToModList { namespace ExportToModList {
enum Formats { HTML, MARKDOWN, PLAINTXT, JSON, CSV, CUSTOM }; enum Formats { HTML, MARKDOWN, PLAINTXT, JSON, CSV, CUSTOM };
enum OptionalData { enum OptionalData { Authors = 1 << 0, Url = 1 << 1, Version = 1 << 2, FileName = 1 << 3 };
Authors = 1 << 0,
Url = 1 << 1,
Version = 1 << 2,
};
QString exportToModList(QList<Mod*> mods, Formats format, OptionalData extraData); QString exportToModList(QList<Mod*> mods, Formats format, OptionalData extraData);
QString exportToModList(QList<Mod*> mods, QString lineTemplate); QString exportToModList(QList<Mod*> mods, QString lineTemplate);
} // namespace ExportToModList } // namespace ExportToModList

View File

@ -40,6 +40,7 @@
#include "tasks/ConcurrentTask.h" #include "tasks/ConcurrentTask.h"
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#include "ui/dialogs/CustomMessageBox.h"
#endif #endif
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) : ConcurrentTask(nullptr, job_name), m_network(network) NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) : ConcurrentTask(nullptr, job_name), m_network(network)
@ -63,8 +64,11 @@ void NetJob::executeNextSubTask()
// We're finished, check for failures and retry if we can (up to 3 times) // We're finished, check for failures and retry if we can (up to 3 times)
if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) { if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) {
m_try += 1; m_try += 1;
while (!m_failed.isEmpty()) while (!m_failed.isEmpty()) {
m_queue.enqueue(m_failed.take(*m_failed.keyBegin())); auto task = m_failed.take(*m_failed.keyBegin());
m_done.remove(task.get());
m_queue.enqueue(task);
}
} }
ConcurrentTask::executeNextSubTask(); ConcurrentTask::executeNextSubTask();
} }
@ -136,3 +140,25 @@ void NetJob::updateState()
setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)") setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
.arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize()))); .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
} }
void NetJob::emitFailed(QString reason)
{
#if defined(LAUNCHER_APPLICATION)
auto response = CustomMessageBox::selectable(nullptr, "Confirm retry",
"The tasks failed\n"
"Failed urls\n" +
getFailedFiles().join("\n\t") +
"\n"
"If this continues to happen please check the logs of the application"
"Do you want to retry?",
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response == QMessageBox::Yes) {
m_try = 0;
executeNextSubTask();
return;
}
#endif
ConcurrentTask::emitFailed(reason);
}

View File

@ -66,6 +66,7 @@ class NetJob : public ConcurrentTask {
public slots: public slots:
// Qt can't handle auto at the start for some reason? // Qt can't handle auto at the start for some reason?
bool abort() override; bool abort() override;
void emitFailed(QString reason) override;
protected slots: protected slots:
void executeNextSubTask() override; void executeNextSubTask() override;

View File

@ -96,7 +96,6 @@
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ExportInstanceDialog.h" #include "ui/dialogs/ExportInstanceDialog.h"
#include "ui/dialogs/ExportPackDialog.h" #include "ui/dialogs/ExportPackDialog.h"
#include "ui/dialogs/ExportToModListDialog.h"
#include "ui/dialogs/IconPickerDialog.h" #include "ui/dialogs/IconPickerDialog.h"
#include "ui/dialogs/ImportResourceDialog.h" #include "ui/dialogs/ImportResourceDialog.h"
#include "ui/dialogs/NewInstanceDialog.h" #include "ui/dialogs/NewInstanceDialog.h"
@ -209,7 +208,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
exportInstanceMenu->addAction(ui->actionExportInstanceZip); exportInstanceMenu->addAction(ui->actionExportInstanceZip);
exportInstanceMenu->addAction(ui->actionExportInstanceMrPack); exportInstanceMenu->addAction(ui->actionExportInstanceMrPack);
exportInstanceMenu->addAction(ui->actionExportInstanceFlamePack); exportInstanceMenu->addAction(ui->actionExportInstanceFlamePack);
exportInstanceMenu->addAction(ui->actionExportInstanceToModList);
ui->actionExportInstance->setMenu(exportInstanceMenu); ui->actionExportInstance->setMenu(exportInstanceMenu);
} }
@ -1416,14 +1414,6 @@ void MainWindow::on_actionExportInstanceMrPack_triggered()
} }
} }
void MainWindow::on_actionExportInstanceToModList_triggered()
{
if (m_selectedInstance) {
ExportToModListDialog dlg(m_selectedInstance, this);
dlg.exec();
}
}
void MainWindow::on_actionExportInstanceFlamePack_triggered() void MainWindow::on_actionExportInstanceFlamePack_triggered()
{ {
if (m_selectedInstance) { if (m_selectedInstance) {

View File

@ -158,7 +158,6 @@ class MainWindow : public QMainWindow {
void on_actionExportInstanceZip_triggered(); void on_actionExportInstanceZip_triggered();
void on_actionExportInstanceMrPack_triggered(); void on_actionExportInstanceMrPack_triggered();
void on_actionExportInstanceFlamePack_triggered(); void on_actionExportInstanceFlamePack_triggered();
void on_actionExportInstanceToModList_triggered();
void on_actionRenameInstance_triggered(); void on_actionRenameInstance_triggered();

View File

@ -491,15 +491,6 @@
<string>CurseForge (zip)</string> <string>CurseForge (zip)</string>
</property> </property>
</action> </action>
<action name="actionExportInstanceToModList">
<property name="icon">
<iconset theme="new">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Mod List</string>
</property>
</action>
<action name="actionCreateInstanceShortcut"> <action name="actionCreateInstanceShortcut">
<property name="icon"> <property name="icon">
<iconset theme="shortcut"> <iconset theme="shortcut">

View File

@ -22,8 +22,6 @@
#include <QTextEdit> #include <QTextEdit>
#include "FileSystem.h" #include "FileSystem.h"
#include "Markdown.h" #include "Markdown.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/mod/ModFolderModel.h"
#include "modplatform/helpers/ExportToModList.h" #include "modplatform/helpers/ExportToModList.h"
#include "ui_ExportToModListDialog.h" #include "ui_ExportToModListDialog.h"
@ -41,38 +39,31 @@ const QHash<ExportToModList::Formats, QString> ExportToModListDialog::exampleLin
{ ExportToModList::CSV, "{name},{url},{version},\"{authors}\"" }, { ExportToModList::CSV, "{name},{url},{version},\"{authors}\"" },
}; };
ExportToModListDialog::ExportToModListDialog(InstancePtr instance, QWidget* parent) ExportToModListDialog::ExportToModListDialog(QString name, QList<Mod*> mods, QWidget* parent)
: QDialog(parent), m_template_changed(false), name(instance->name()), ui(new Ui::ExportToModListDialog) : QDialog(parent), m_mods(mods), m_template_changed(false), m_name(name), ui(new Ui::ExportToModListDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
enableCustom(false); enableCustom(false);
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
if (mcInstance) {
mcInstance->loaderModList()->update();
connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, [this, mcInstance]() {
m_allMods = mcInstance->loaderModList()->allMods();
triggerImp();
});
}
connect(ui->formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExportToModListDialog::formatChanged); connect(ui->formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExportToModListDialog::formatChanged);
connect(ui->authorsCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->authorsCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
connect(ui->versionCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->versionCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
connect(ui->urlCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->urlCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
connect(ui->filenameCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
connect(ui->authorsButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Authors); }); connect(ui->authorsButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Authors); });
connect(ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); }); connect(ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); });
connect(ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); }); connect(ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); });
connect(ui->filenameButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::FileName); });
connect(ui->templateText, &QTextEdit::textChanged, this, [this] { connect(ui->templateText, &QTextEdit::textChanged, this, [this] {
if (ui->templateText->toPlainText() != exampleLines[format]) if (ui->templateText->toPlainText() != exampleLines[m_format])
ui->formatComboBox->setCurrentIndex(5); ui->formatComboBox->setCurrentIndex(5);
else triggerImp();
triggerImp();
}); });
connect(ui->copyButton, &QPushButton::clicked, this, [this](bool) { connect(ui->copyButton, &QPushButton::clicked, this, [this](bool) {
this->ui->finalText->selectAll(); this->ui->finalText->selectAll();
this->ui->finalText->copy(); this->ui->finalText->copy();
}); });
triggerImp();
} }
ExportToModListDialog::~ExportToModListDialog() ExportToModListDialog::~ExportToModListDialog()
@ -86,38 +77,38 @@ void ExportToModListDialog::formatChanged(int index)
case 0: { case 0: {
enableCustom(false); enableCustom(false);
ui->resultText->show(); ui->resultText->show();
format = ExportToModList::HTML; m_format = ExportToModList::HTML;
break; break;
} }
case 1: { case 1: {
enableCustom(false); enableCustom(false);
ui->resultText->show(); ui->resultText->show();
format = ExportToModList::MARKDOWN; m_format = ExportToModList::MARKDOWN;
break; break;
} }
case 2: { case 2: {
enableCustom(false); enableCustom(false);
ui->resultText->hide(); ui->resultText->hide();
format = ExportToModList::PLAINTXT; m_format = ExportToModList::PLAINTXT;
break; break;
} }
case 3: { case 3: {
enableCustom(false); enableCustom(false);
ui->resultText->hide(); ui->resultText->hide();
format = ExportToModList::JSON; m_format = ExportToModList::JSON;
break; break;
} }
case 4: { case 4: {
enableCustom(false); enableCustom(false);
ui->resultText->hide(); ui->resultText->hide();
format = ExportToModList::CSV; m_format = ExportToModList::CSV;
break; break;
} }
case 5: { case 5: {
m_template_changed = true; m_template_changed = true;
enableCustom(true); enableCustom(true);
ui->resultText->hide(); ui->resultText->hide();
format = ExportToModList::CUSTOM; m_format = ExportToModList::CUSTOM;
break; break;
} }
} }
@ -126,8 +117,8 @@ void ExportToModListDialog::formatChanged(int index)
void ExportToModListDialog::triggerImp() void ExportToModListDialog::triggerImp()
{ {
if (format == ExportToModList::CUSTOM) { if (m_format == ExportToModList::CUSTOM) {
ui->finalText->setPlainText(ExportToModList::exportToModList(m_allMods, ui->templateText->toPlainText())); ui->finalText->setPlainText(ExportToModList::exportToModList(m_mods, ui->templateText->toPlainText()));
return; return;
} }
auto opt = 0; auto opt = 0;
@ -137,9 +128,11 @@ void ExportToModListDialog::triggerImp()
opt |= ExportToModList::Version; opt |= ExportToModList::Version;
if (ui->urlCheckBox->isChecked()) if (ui->urlCheckBox->isChecked())
opt |= ExportToModList::Url; opt |= ExportToModList::Url;
auto txt = ExportToModList::exportToModList(m_allMods, format, static_cast<ExportToModList::OptionalData>(opt)); if (ui->filenameCheckBox->isChecked())
opt |= ExportToModList::FileName;
auto txt = ExportToModList::exportToModList(m_mods, m_format, static_cast<ExportToModList::OptionalData>(opt));
ui->finalText->setPlainText(txt); ui->finalText->setPlainText(txt);
switch (format) { switch (m_format) {
case ExportToModList::CUSTOM: case ExportToModList::CUSTOM:
return; return;
case ExportToModList::HTML: case ExportToModList::HTML:
@ -155,7 +148,7 @@ void ExportToModListDialog::triggerImp()
case ExportToModList::CSV: case ExportToModList::CSV:
break; break;
} }
auto exampleLine = exampleLines[format]; auto exampleLine = exampleLines[m_format];
if (!m_template_changed && ui->templateText->toPlainText() != exampleLine) if (!m_template_changed && ui->templateText->toPlainText() != exampleLine)
ui->templateText->setPlainText(exampleLine); ui->templateText->setPlainText(exampleLine);
} }
@ -163,9 +156,9 @@ void ExportToModListDialog::triggerImp()
void ExportToModListDialog::done(int result) void ExportToModListDialog::done(int result)
{ {
if (result == Accepted) { if (result == Accepted) {
const QString filename = FS::RemoveInvalidFilenameChars(name); const QString filename = FS::RemoveInvalidFilenameChars(m_name);
const QString output = const QString output =
QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + extension()), QFileDialog::getSaveFileName(this, tr("Export %1").arg(m_name), FS::PathCombine(QDir::homePath(), filename + extension()),
"File (*.txt *.html *.md *.json *.csv)", nullptr); "File (*.txt *.html *.md *.json *.csv)", nullptr);
if (output.isEmpty()) if (output.isEmpty())
@ -178,7 +171,7 @@ void ExportToModListDialog::done(int result)
QString ExportToModListDialog::extension() QString ExportToModListDialog::extension()
{ {
switch (format) { switch (m_format) {
case ExportToModList::HTML: case ExportToModList::HTML:
return ".html"; return ".html";
case ExportToModList::MARKDOWN: case ExportToModList::MARKDOWN:
@ -197,7 +190,7 @@ QString ExportToModListDialog::extension()
void ExportToModListDialog::addExtra(ExportToModList::OptionalData option) void ExportToModListDialog::addExtra(ExportToModList::OptionalData option)
{ {
if (format != ExportToModList::CUSTOM) if (m_format != ExportToModList::CUSTOM)
return; return;
switch (option) { switch (option) {
case ExportToModList::Authors: case ExportToModList::Authors:
@ -209,6 +202,9 @@ void ExportToModListDialog::addExtra(ExportToModList::OptionalData option)
case ExportToModList::Version: case ExportToModList::Version:
ui->templateText->insertPlainText("{version}"); ui->templateText->insertPlainText("{version}");
break; break;
case ExportToModList::FileName:
ui->templateText->insertPlainText("{filename}");
break;
} }
} }
void ExportToModListDialog::enableCustom(bool enabled) void ExportToModListDialog::enableCustom(bool enabled)
@ -221,4 +217,7 @@ void ExportToModListDialog::enableCustom(bool enabled)
ui->urlCheckBox->setHidden(enabled); ui->urlCheckBox->setHidden(enabled);
ui->urlButton->setHidden(!enabled); ui->urlButton->setHidden(!enabled);
ui->filenameCheckBox->setHidden(enabled);
ui->filenameButton->setHidden(!enabled);
} }

View File

@ -20,7 +20,6 @@
#include <QDialog> #include <QDialog>
#include <QList> #include <QList>
#include "BaseInstance.h"
#include "minecraft/mod/Mod.h" #include "minecraft/mod/Mod.h"
#include "modplatform/helpers/ExportToModList.h" #include "modplatform/helpers/ExportToModList.h"
@ -32,7 +31,7 @@ class ExportToModListDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ExportToModListDialog(InstancePtr instance, QWidget* parent = nullptr); explicit ExportToModListDialog(QString name, QList<Mod*> mods, QWidget* parent = nullptr);
~ExportToModListDialog(); ~ExportToModListDialog();
void done(int result) override; void done(int result) override;
@ -46,10 +45,11 @@ class ExportToModListDialog : public QDialog {
private: private:
QString extension(); QString extension();
void enableCustom(bool enabled); void enableCustom(bool enabled);
QList<Mod*> m_allMods;
QList<Mod*> m_mods;
bool m_template_changed; bool m_template_changed;
QString name; QString m_name;
ExportToModList::Formats format = ExportToModList::Formats::HTML; ExportToModList::Formats m_format = ExportToModList::Formats::HTML;
Ui::ExportToModListDialog* ui; Ui::ExportToModListDialog* ui;
static const QHash<ExportToModList::Formats, QString> exampleLines; static const QHash<ExportToModList::Formats, QString> exampleLines;
}; };

View File

@ -117,6 +117,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="filenameCheckBox">
<property name="text">
<string>Filename</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="versionButton"> <widget class="QPushButton" name="versionButton">
<property name="text"> <property name="text">
@ -138,6 +145,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="filenameButton">
<property name="text">
<string>Filename</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -70,15 +70,15 @@
</layout> </layout>
</widget> </widget>
<widget class="WideBar" name="actionsToolbar"> <widget class="WideBar" name="actionsToolbar">
<property name="useDefaultAction" stdset="0">
<bool>true</bool>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Actions</string> <string>Actions</string>
</property> </property>
<property name="toolButtonStyle"> <property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum> <enum>Qt::ToolButtonTextOnly</enum>
</property> </property>
<property name="useDefaultAction" stdset="0">
<bool>true</bool>
</property>
<attribute name="toolBarArea"> <attribute name="toolBarArea">
<enum>RightToolBarArea</enum> <enum>RightToolBarArea</enum>
</attribute> </attribute>
@ -171,6 +171,17 @@
<string>Try to check or update all selected resources (all resources if none are selected)</string> <string>Try to check or update all selected resources (all resources if none are selected)</string>
</property> </property>
</action> </action>
<action name="actionExportMetadata">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Export modlist</string>
</property>
<property name="toolTip">
<string>Export mod's metadata to text</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -37,6 +37,7 @@
*/ */
#include "ModFolderPage.h" #include "ModFolderPage.h"
#include "ui/dialogs/ExportToModListDialog.h"
#include "ui_ExternalResourcesPage.h" #include "ui_ExternalResourcesPage.h"
#include <QAbstractItemModel> #include <QAbstractItemModel>
@ -128,6 +129,9 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, changeVersion); ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, changeVersion);
connect(changeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion); connect(changeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion);
ui->actionsToolbar->insertActionAfter(ui->actionVisitItemPage, ui->actionExportMetadata);
connect(ui->actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata);
auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); };
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
@ -431,3 +435,15 @@ void ModFolderPage::changeModVersion()
m_model->update(); m_model->update();
} }
} }
void ModFolderPage::exportModMetadata()
{
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
auto selectedMods = m_model->selectedMods(selection);
if (selectedMods.length() == 0)
selectedMods = m_model->allMods();
std::sort(selectedMods.begin(), selectedMods.end(), [](const Mod* a, const Mod* b) { return a->name() < b->name(); });
ExportToModListDialog dlg(m_instance->name(), selectedMods, this);
dlg.exec();
}

View File

@ -62,6 +62,7 @@ class ModFolderPage : public ExternalResourcesPage {
private slots: private slots:
void removeItems(const QItemSelection& selection) override; void removeItems(const QItemSelection& selection) override;
void deleteModMetadata(); void deleteModMetadata();
void exportModMetadata();
void installMods(); void installMods();
void updateMods(bool includeDeps = false); void updateMods(bool includeDeps = false);

View File

@ -18,7 +18,7 @@
jdk21, jdk21,
gamemode, gamemode,
flite, flite,
mesa-demos, glxinfo,
udev, udev,
libusb1, libusb1,
msaClientID ? null, msaClientID ? null,
@ -81,7 +81,7 @@ in
runtimePrograms = runtimePrograms =
[ [
xorg.xrandr xorg.xrandr
mesa-demos # need glxinfo glxinfo
] ]
++ additionalPrograms; ++ additionalPrograms;
in in