diff options
Diffstat (limited to 'launcher/ui')
35 files changed, 818 insertions, 233 deletions
diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index ad8db5ff..5af24b1b 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -37,15 +37,21 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider) : QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider) { + Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME); + ui->setupUi(this); - ui->name->setText(instance->name()); + ui->name->setPlaceholderText(instance->name()); + ui->name->setText(instance->settings()->get("ExportName").toString()); + ui->version->setText(instance->settings()->get("ExportVersion").toString()); + ui->optionalFiles->setChecked(instance->settings()->get("ExportOptionalFiles").toBool()); + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { - ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]); - setWindowTitle("Export Modrinth Pack"); + setWindowTitle(tr("Export Modrinth Pack")); + ui->summary->setText(instance->settings()->get("ExportSummary").toString()); } else { - setWindowTitle("Export CurseForge Pack"); - ui->version->setText(""); - ui->summaryLabel->setText("Author"); + setWindowTitle(tr("Export CurseForge Pack")); + ui->summaryLabel->setText(tr("&Author")); + ui->summary->setText(instance->settings()->get("ExportAuthor").toString()); } // ensure a valid pack is generated @@ -75,20 +81,19 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get()); if (mcInstance) { - mcInstance->loaderModList()->update(); const QDir index = mcInstance->loaderModList()->indexDir(); if (index.exists()) - proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath())); + proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath())); } - ui->treeView->setModel(proxy); - ui->treeView->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); - ui->treeView->sortByColumn(0, Qt::AscendingOrder); + ui->files->setModel(proxy); + ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); + ui->files->sortByColumn(0, Qt::AscendingOrder); model->setFilter(filter); model->setRootPath(instance->gameRoot()); - QHeaderView* headerView = ui->treeView->header(); + QHeaderView* headerView = ui->files->header(); headerView->setSectionResizeMode(QHeaderView::ResizeToContents); headerView->setSectionResizeMode(0, QHeaderView::Stretch); } @@ -100,26 +105,41 @@ ExportPackDialog::~ExportPackDialog() void ExportPackDialog::done(int result) { + auto settings = instance->settings(); + settings->set("ExportName", ui->name->text()); + settings->set("ExportVersion", ui->version->text()); + settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text()); + settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked()); + if (result == Accepted) { - const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text()); + const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text(); + const QString filename = FS::RemoveInvalidFilenameChars(name); + QString output; - if (m_provider == ModPlatform::ResourceProvider::MODRINTH) - output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()), - FS::PathCombine(QDir::homePath(), filename + ".mrpack"), "Modrinth pack (*.mrpack *.zip)", - nullptr); - else - output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()), - FS::PathCombine(QDir::homePath(), filename + ".zip"), "CurseForge pack (*.zip)", nullptr); - - if (output.isEmpty()) - return; + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { + output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".mrpack"), + "Modrinth pack (*.mrpack *.zip)", nullptr); + if (output.isEmpty()) + return; + if (!(output.endsWith(".zip") || output.endsWith(".mrpack"))) + output.append(".mrpack"); + } else { + output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".zip"), + "CurseForge pack (*.zip)", nullptr); + if (output.isEmpty()) + return; + if (!output.endsWith(".zip")) + output.append(".zip"); + } + Task* task; - if (m_provider == ModPlatform::ResourceProvider::MODRINTH) - task = new ModrinthPackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output, - std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); - else - task = new FlamePackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output, + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { + task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, + output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); + } else { + task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); + } connect(task, &Task::failed, [this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); @@ -140,7 +160,6 @@ void ExportPackDialog::done(int result) void ExportPackDialog::validate() { - const bool invalid = - ui->name->text().isEmpty() || ((m_provider == ModPlatform::ResourceProvider::MODRINTH) && ui->version->text().isEmpty()); - ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid); + ui->buttonBox->button(QDialogButtonBox::Ok) + ->setDisabled(m_provider == ModPlatform::ResourceProvider::MODRINTH && ui->version->text().isEmpty()); } diff --git a/launcher/ui/dialogs/ExportPackDialog.ui b/launcher/ui/dialogs/ExportPackDialog.ui index 3976e28f..09dea72a 100644 --- a/launcher/ui/dialogs/ExportPackDialog.ui +++ b/launcher/ui/dialogs/ExportPackDialog.ui @@ -7,12 +7,9 @@ <x>0</x> <y>0</y> <width>650</width> - <height>413</height> + <height>510</height> </rect> </property> - <property name="windowTitle"> - <string>Export Pack</string> - </property> <property name="sizeGripEnabled"> <bool>true</bool> </property> @@ -20,13 +17,16 @@ <item> <widget class="QGroupBox" name="information"> <property name="title"> - <string>Information</string> + <string>&Description</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="3" column="0"> <widget class="QLabel" name="summaryLabel"> <property name="text"> - <string>Summary</string> + <string>&Summary</string> + </property> + <property name="buddy"> + <cstring>summary</cstring> </property> </widget> </item> @@ -36,14 +36,20 @@ <item row="0" column="0"> <widget class="QLabel" name="nameLabel"> <property name="text"> - <string>Name</string> + <string>&Name</string> + </property> + <property name="buddy"> + <cstring>name</cstring> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="versionLabel"> <property name="text"> - <string>Version</string> + <string>&Version</string> + </property> + <property name="buddy"> + <cstring>version</cstring> </property> </widget> </item> @@ -57,31 +63,52 @@ </property> </widget> </item> - </layout> </widget> </item> <item> - <widget class="QLabel" name="filesLabel"> - <property name="text"> - <string>Files</string> - </property> - </widget> - </item> - <item> - <widget class="QTreeView" name="treeView"> - <property name="alternatingRowColors"> - <bool>true</bool> - </property> - <property name="selectionMode"> - <enum>QAbstractItemView::ExtendedSelection</enum> - </property> - <property name="sortingEnabled"> - <bool>true</bool> + <widget class="QGroupBox" name="options"> + <property name="title"> + <string>&Options</string> </property> - <attribute name="headerStretchLastSection"> - <bool>false</bool> - </attribute> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="filesLabel"> + <property name="text"> + <string>&Files</string> + </property> + <property name="buddy"> + <cstring>files</cstring> + </property> + </widget> + </item> + <item> + <widget class="QTreeView" name="files"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <attribute name="headerStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QCheckBox" name="optionalFiles"> + <property name="text"> + <string>&Mark disabled files as optional</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </widget> </item> <item> @@ -97,7 +124,8 @@ <tabstop>name</tabstop> <tabstop>version</tabstop> <tabstop>summary</tabstop> - <tabstop>treeView</tabstop> + <tabstop>files</tabstop> + <tabstop>optionalFiles</tabstop> </tabstops> <resources/> <connections> diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 0a7edb7b..cb8f1920 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -132,6 +132,32 @@ void ResourceModel::search() if (hasActiveSearchJob()) return; + if (m_search_term.startsWith("#")) { + auto projectId = m_search_term.mid(1); + if (!projectId.isEmpty()) { + ResourceAPI::ProjectInfoCallbacks callbacks; + + callbacks.on_fail = [this](QString reason) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestFailed(reason, -1); + }; + callbacks.on_abort = [this] { + if (!s_running_models.constFind(this).value()) + return; + searchRequestAborted(); + }; + + callbacks.on_succeed = [this](auto& doc, auto& pack) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestForOneSucceeded(doc); + }; + if (auto job = m_api->getProjectInfo({ projectId }, std::move(callbacks)); job) + runSearchJob(job); + return; + } + } auto args{ createSearchArguments() }; auto callbacks{ createSearchCallbacks() }; @@ -189,11 +215,18 @@ void ResourceModel::loadEntry(QModelIndex& entry) // Use default if no callbacks are set if (!callbacks.on_succeed) - callbacks.on_succeed = [this, entry](auto& doc, auto pack) { + callbacks.on_succeed = [this, entry](auto& doc, auto& newpack) { if (!s_running_models.constFind(this).value()) return; + auto pack = newpack; infoRequestSucceeded(doc, pack, entry); }; + if (!callbacks.on_fail) + callbacks.on_fail = [this](QString reason) { + if (!s_running_models.constFind(this).value()) + return; + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason)); + }; if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) runInfoJob(job); @@ -372,6 +405,27 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) endInsertRows(); } +void ResourceModel::searchRequestForOneSucceeded(QJsonDocument& doc) +{ + ModPlatform::IndexedPack::Ptr pack = std::make_shared<ModPlatform::IndexedPack>(); + + try { + auto obj = Json::requireObject(doc); + if (obj.contains("data")) + obj = Json::requireObject(obj, "data"); + loadIndexedPack(*pack, obj); + } catch (const JSONValidationError& e) { + qDebug() << doc; + qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause(); + } + + m_search_state = SearchState::Finished; + + beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + 1); + m_packs.append(pack); + endInsertRows(); +} + void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int network_error_code) { switch (network_error_code) { diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index cc813d6e..ecf4f8f7 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -149,6 +149,7 @@ class ResourceModel : public QAbstractListModel { private: /* Default search request callbacks */ void searchRequestSucceeded(QJsonDocument&); + void searchRequestForOneSucceeded(QJsonDocument&); void searchRequestFailed(QString reason, int network_error_code); void searchRequestAborted(); diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index c087e2be..fc7d64a4 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -44,9 +44,6 @@ #include <QKeyEvent> #include "Markdown.h" -#include "ResourceDownloadTask.h" - -#include "minecraft/MinecraftInstance.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ResourceModel.h" diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp index 9cd5eed5..dee3784e 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp @@ -67,9 +67,10 @@ bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParen if (searchTerm.isEmpty()) { return true; } - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); ATLauncher::IndexedPack pack = sourceModel()->data(index, Qt::UserRole).value<ATLauncher::IndexedPack>(); + if (searchTerm.startsWith("#")) + return QString::number(pack.id) == searchTerm.mid(1); return pack.name.contains(searchTerm, Qt::CaseInsensitive); } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 39f4f346..b6fb7153 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -21,6 +21,7 @@ #include <Json.h> #include "net/ApiDownload.h" +#include "ui/widgets/ProjectItem.h" namespace Atl { @@ -46,27 +47,50 @@ QVariant ListModel::data(const QModelIndex& index, int role) const } ATLauncher::IndexedPack pack = modpacks.at(pos); - if (role == Qt::DisplayRole) { - return pack.name; - } else if (role == Qt::ToolTipRole) { - return pack.name; - } else if (role == Qt::DecorationRole) { - if (m_logoMap.contains(pack.safeName)) { - return (m_logoMap.value(pack.safeName)); + switch (role) { + case Qt::ToolTipRole: { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; } - auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder"); + case Qt::DecorationRole: { + if (m_logoMap.contains(pack.safeName)) { + return (m_logoMap.value(pack.safeName)); + } + auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder"); - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); - ((ListModel*)this)->requestLogo(pack.safeName, url); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); + ((ListModel*)this)->requestLogo(pack.safeName, url); - return icon; - } else if (role == Qt::UserRole) { - QVariant v; - v.setValue(pack); - return v; + return icon; + } + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; + } + case Qt::DisplayRole: + return pack.name; + case Qt::SizeHintRole: + return QSize(0, 58); + // Custom data + case UserDataTypes::TITLE: + return pack.name; + case UserDataTypes::DESCRIPTION: + return pack.description; + case UserDataTypes::SELECTED: + return false; + case UserDataTypes::INSTALLED: + return false; + default: + break; } - return QVariant(); + return {}; } void ListModel::request() diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 5e3b9ecf..c7e80027 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -35,11 +35,11 @@ */ #include "AtlPage.h" +#include "ui/widgets/ProjectItem.h" #include "ui_AtlPage.h" #include "BuildConfig.h" -#include "AtlOptionalModDialog.h" #include "AtlUserInteractionSupportImpl.h" #include "modplatform/atlauncher/ATLPackInstallTask.h" #include "ui/dialogs/NewInstanceDialog.h" @@ -71,6 +71,8 @@ AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &AtlPage::onSortingSelectionChanged); connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &AtlPage::onSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &AtlPage::onVersionSelectionChanged); + + ui->packView->setItemDelegate(new ProjectItemDelegate(this)); } AtlPage::~AtlPage() diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui index 746aa6d1..8b674733 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui @@ -11,21 +11,28 @@ </rect> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="1" column="0"> - <widget class="QTreeView" name="packView"> - <property name="alternatingRowColors"> - <bool>true</bool> + <item row="3" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0"> + <item row="0" column="2"> + <widget class="QComboBox" name="versionSelectionBox"/> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Version selected:</string> </property> - <property name="iconSize"> - <size> - <width>96</width> - <height>48</height> - </size> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> + <item row="0" column="0"> + <widget class="QComboBox" name="sortByBox"/> + </item> + </layout> + </item> + <item row="2" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_3"> <item row="1" column="1"> <widget class="QTextBrowser" name="packDescription"> <property name="openExternalLinks"> @@ -36,39 +43,22 @@ </property> </widget> </item> - <item row="0" column="0" colspan="2"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Warning: This is still a work in progress. If you run into issues with the imported modpack, it may be a bug.</string> - </property> - <property name="wordWrap"> + <item row="1" column="0"> + <widget class="QTreeView" name="packView"> + <property name="alternatingRowColors"> <bool>true</bool> </property> - </widget> - </item> - </layout> - </item> - <item row="2" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0"> - <item row="0" column="2"> - <widget class="QComboBox" name="versionSelectionBox"/> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Version selected:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + <property name="iconSize"> + <size> + <width>96</width> + <height>48</height> + </size> </property> </widget> </item> - <item row="0" column="0"> - <widget class="QComboBox" name="sortByBox"/> - </item> </layout> </item> - <item row="0" column="0"> + <item row="1" column="0"> <widget class="QLineEdit" name="searchEdit"> <property name="placeholderText"> <string>Search and filter...</string> @@ -78,6 +68,31 @@ </property> </widget> </item> + <item row="1" column="1"> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>Search</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_2"> + <property name="font"> + <font> + <italic>true< |
