diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index df8c690af..deec0db7c 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -23,6 +23,7 @@ #include #include "Json.h" #include "QObjectPtr.h" +#include "minecraft/PackProfile.h" #include "minecraft/mod/MetadataHandler.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 2cd7710e3..d2113320a 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -94,10 +94,9 @@ auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString t { QCryptographicHash::Algorithm algo = QCryptographicHash::Sha1; switch (p) { - case ResourceProvider::MODRINTH: { + case ResourceProvider::MODRINTH: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Sha512; break; - } case ResourceProvider::FLAME: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Md5; break; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index dc7cfff06..f21502365 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -27,6 +27,7 @@ #include "Application.h" #include "ResourceDownloadTask.h" +#include "minecraft/PackProfile.h" #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index c628f74ac..3b53d1348 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -25,15 +25,18 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() Q_ASSERT(m_filter); std::optional> versions{}; + auto loaders = profile->getSupportedModLoaders(); { // Version filter if (!m_filter->versions.empty()) versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; } auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) @@ -45,10 +48,13 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en Q_ASSERT(m_filter); std::optional> versions{}; + auto loaders = profile->getSupportedModLoaders(); if (!m_filter->versions.empty()) versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; - return { pack, versions, profile->getSupportedModLoaders() }; + return { pack, versions, loaders }; } ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index d6cc1fdcc..838cf21c8 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -71,7 +71,6 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount()); - m_filter_widget->setInstance(&static_cast(m_base_instance)); m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, @@ -89,13 +88,14 @@ void ModPage::filterMods() void ModPage::triggerSearch() { + auto changed = m_filter_widget->changed(); m_filter = m_filter_widget->getFilter(); m_ui->packView->clearSelection(); m_ui->packDescription->clear(); m_ui->versionSelectionBox->clear(); updateSelectionButton(); - static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), m_filter_widget->changed()); + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), changed); m_fetch_progress.watch(m_model->activeSearchJob().get()); } @@ -116,6 +116,9 @@ void ModPage::updateVersionList() auto packProfile = (dynamic_cast(m_base_instance)).getPackProfile(); QString mcVersion = packProfile->getComponentVersion("net.minecraft"); + auto loaders = packProfile->getSupportedModLoaders(); + if (m_filter->loaders) + loaders = m_filter->loaders; auto current_pack = getCurrentPack(); if (!current_pack) @@ -124,7 +127,7 @@ void ModPage::updateVersionList() auto version = current_pack->versions[i]; bool valid = false; for (auto& mcVer : m_filter->versions) { - if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) { + if (validateVersion(version, mcVer.toString(), loaders)) { valid = true; break; } diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 5a43e49a6..6efb318ea 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -31,8 +31,7 @@ class ModPage : public ResourcePage { auto page = new T(dialog, instance); auto model = static_cast(page->getModel()); - auto filter_widget = - ModFilterWidget::create(static_cast(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); + auto filter_widget = ModFilterWidget::create(&static_cast(instance), page); page->setFilterWidget(filter_widget); model->setFilter(page->getFilter()); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 7d18e72a6..39a2a0d6a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -6,6 +6,7 @@ #include "Json.h" +#include "minecraft/PackProfile.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index c2c099eeb..62a8eb154 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,13 +1,70 @@ #include "ModFilterWidget.h" +#include +#include +#include "BaseVersionList.h" +#include "meta/Index.h" +#include "modplatform/ModIndex.h" #include "ui_ModFilterWidget.h" #include "Application.h" +#include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(Version default_version, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, QWidget* parent) { - auto filter_widget = new ModFilterWidget(default_version, parent); + return unique_qobject_ptr(new ModFilterWidget(instance, parent)); +} - if (!filter_widget->versionList()->isLoaded()) { +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) + : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) +{ + ui->setupUi(this); + + m_versions_proxy = new VersionProxyModel(this); + + ui->versionsCb->setModel(m_versions_proxy); + + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + + ui->versionsCb->setStyleSheet("combobox-popup: 0;"); + connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); + connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + + connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabricCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quiltCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->liteLoaderCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->cauldronCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + + ui->liteLoaderCb->hide(); + ui->cauldronCb->hide(); + + connect(ui->serverEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + connect(ui->clientEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + + connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); + + setHidden(true); + loadVersionList(); + prepareBasicFilter(); +} + +auto ModFilterWidget::getFilter() -> std::shared_ptr +{ + m_filter_changed = false; + emit filterUnchanged(); + return m_filter; +} + +ModFilterWidget::~ModFilterWidget() +{ + delete ui; +} + +void ModFilterWidget::loadVersionList() +{ + m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); + if (!m_version_list->isLoaded()) { QEventLoop load_version_list_loop; QTimer time_limit_for_list_load; @@ -16,10 +73,12 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit); time_limit_for_list_load.start(4000); - auto task = filter_widget->versionList()->getLoadTask(); + auto task = m_version_list->getLoadTask(); - connect(task.get(), &Task::failed, - [filter_widget] { filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); }); + connect(task.get(), &Task::failed, [this] { + ui->versionsCb->setEnabled(false); + ui->snapshotsCb->setEnabled(false); + }); connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); if (!task->isRunning()) @@ -29,128 +88,93 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers if (time_limit_for_list_load.isActive()) time_limit_for_list_load.stop(); } - - return unique_qobject_ptr(filter_widget); + m_versions_proxy->setSourceModel(m_version_list.get()); } -ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget) +void ModFilterWidget::prepareBasicFilter() { - ui->setupUi(this); - - m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict); - ui->strictVersionButton->click(); - m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major); - m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All); - // m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between); - - connect(&m_mcVersion_buttons, SIGNAL(idClicked(int)), this, SLOT(onVersionFilterChanged(int))); - - m_filter->versions.push_front(def); - - m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); - setHidden(true); + m_filter->hideInstalled = false; + m_filter->side = ""; // or "both"t + auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + ui->neoForgeCb->setChecked(loaders & ModPlatform::NeoForge); + ui->forgeCb->setChecked(loaders & ModPlatform::Forge); + ui->fabricCb->setChecked(loaders & ModPlatform::Fabric); + ui->quiltCb->setChecked(loaders & ModPlatform::Quilt); + ui->liteLoaderCb->setChecked(loaders & ModPlatform::LiteLoader); + ui->cauldronCb->setChecked(loaders & ModPlatform::Cauldron); + m_filter->loaders = loaders; + auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); + m_filter->versions.push_front(Version{ def }); + m_versions_proxy->setCurrentVersion(def); + ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row()); } -void ModFilterWidget::setInstance(MinecraftInstance* instance) +void ModFilterWidget::onIncludeSnapshotsChanged() { - m_instance = instance; - - ui->strictVersionButton->setText(tr("Strict match (= %1)").arg(mcVersionStr())); - - // we can't do this for snapshots sadly - if (mcVersionStr().contains('.')) { - auto mcVersionSplit = mcVersionStr().split("."); - ui->majorVersionButton->setText(tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1])); - } else { - ui->majorVersionButton->setText(tr("Major version match (unsupported)")); - disableVersionButton(Major); - } - ui->allVersionsButton->setText(tr("Any version")); - // ui->betweenVersionsButton->setText( - // tr("Between two versions")); + QString filter = "(release)"; + if (ui->snapshotsCb->isChecked()) + filter += "|(snapshot)"; + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); } -auto ModFilterWidget::getFilter() -> std::shared_ptr +void ModFilterWidget::onVersionFilterChanged() { - m_last_version_id = m_version_id; - emit filterUnchanged(); - return m_filter; -} - -void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason) -{ - QAbstractButton* btn = nullptr; - - switch (id) { - case (VersionButtonID::Strict): - btn = ui->strictVersionButton; - break; - case (VersionButtonID::Major): - btn = ui->majorVersionButton; - break; - case (VersionButtonID::All): - btn = ui->allVersionsButton; - break; - case (VersionButtonID::Between): - default: - break; - } - - if (btn) { - btn->setEnabled(false); - if (!reason.isEmpty()) - btn->setText(btn->text() + QString(" (%1)").arg(reason)); - } -} - -void ModFilterWidget::onVersionFilterChanged(int id) -{ - // ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between); - // ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between); - - int index = 1; - - auto cast_id = (VersionButtonID)id; - if (cast_id != m_version_id) { - m_version_id = cast_id; - } else { - return; - } - + auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); m_filter->versions.clear(); + m_filter->versions.push_front(version); + m_filter_changed = true; + emit filterChanged(); +} - switch (cast_id) { - case (VersionButtonID::Strict): - m_filter->versions.push_front(mcVersion()); - break; - case (VersionButtonID::Major): { - auto versionSplit = mcVersionStr().split("."); - - auto major_version = QString("%1.%2").arg(versionSplit[0], versionSplit[1]); - QString version_str = major_version; - - while (m_version_list->hasVersion(version_str)) { - m_filter->versions.emplace_back(version_str); - version_str = QString("%1.%2").arg(major_version, QString::number(index++)); - } - - break; - } - case (VersionButtonID::All): - // Empty list to avoid enumerating all versions :P - break; - case (VersionButtonID::Between): - // TODO - break; - } - - if (changed()) +void ModFilterWidget::onLoadersFilterChanged() +{ + ModPlatform::ModLoaderTypes loaders; + if (ui->neoForgeCb->isChecked()) + loaders |= ModPlatform::NeoForge; + if (ui->forgeCb->isChecked()) + loaders |= ModPlatform::Forge; + if (ui->fabricCb->isChecked()) + loaders |= ModPlatform::Fabric; + if (ui->quiltCb->isChecked()) + loaders |= ModPlatform::Quilt; + if (ui->cauldronCb->isChecked()) + loaders |= ModPlatform::Cauldron; + if (ui->liteLoaderCb->isChecked()) + loaders |= ModPlatform::LiteLoader; + m_filter_changed = loaders != m_filter->loaders; + m_filter->loaders = loaders; + if (m_filter_changed) emit filterChanged(); else emit filterUnchanged(); } -ModFilterWidget::~ModFilterWidget() +void ModFilterWidget::onSideFilterChanged() { - delete ui; + QString side; + if (ui->serverEnv->isChecked()) + side = "server"; + if (ui->clientEnv->isChecked()) { + if (side.isEmpty()) + side = "client"; + else + side = ""; // or both + } + m_filter_changed = side != m_filter->side; + m_filter->side = side; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); } + +void ModFilterWidget::onHideInstalledFilterChanged() +{ + auto hide = ui->hide_installed->isChecked(); + m_filter_changed = hide != m_filter->hideInstalled; + m_filter->hideInstalled = hide; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); +} \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index ed6cd0ea7..b92437a4f 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -5,11 +5,11 @@ #include "Version.h" -#include "meta/Index.h" +#include "VersionProxyModel.h" #include "meta/VersionList.h" #include "minecraft/MinecraftInstance.h" -#include "minecraft/PackProfile.h" +#include "modplatform/ModIndex.h" class MinecraftInstance; @@ -20,42 +20,37 @@ class ModFilterWidget; class ModFilterWidget : public QTabWidget { Q_OBJECT public: - enum VersionButtonID { Strict = 0, Major = 1, All = 2, Between = 3 }; - struct Filter { std::list versions; + ModPlatform::ModLoaderTypes loaders; + QString side; + bool hideInstalled; - bool operator==(const Filter& other) const { return versions == other.versions; } + bool operator==(const Filter& other) const + { + return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions; + } bool operator!=(const Filter& other) const { return !(*this == other); } }; - std::shared_ptr m_filter; - - public: - static unique_qobject_ptr create(Version default_version, QWidget* parent = nullptr); - ~ModFilterWidget(); - - void setInstance(MinecraftInstance* instance); - - /// By default all buttons are enabled - void disableVersionButton(VersionButtonID, QString reason = {}); + static unique_qobject_ptr create(MinecraftInstance* instance, QWidget* parent = nullptr); + virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; - auto changed() const -> bool { return m_last_version_id != m_version_id; } - - Meta::VersionList::Ptr versionList() { return m_version_list; } + auto changed() const -> bool { return m_filter_changed; } private: - ModFilterWidget(Version def, QWidget* parent = nullptr); + ModFilterWidget(MinecraftInstance* instance, QWidget* parent = nullptr); - inline auto mcVersionStr() const -> QString - { - return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; - } - inline auto mcVersion() const -> Version { return { mcVersionStr() }; } + void loadVersionList(); + void prepareBasicFilter(); private slots: - void onVersionFilterChanged(int id); + void onVersionFilterChanged(); + void onLoadersFilterChanged(); + void onSideFilterChanged(); + void onHideInstalledFilterChanged(); + void onIncludeSnapshotsChanged(); public: signals: @@ -66,13 +61,9 @@ class ModFilterWidget : public QTabWidget { Ui::ModFilterWidget* ui; MinecraftInstance* m_instance = nullptr; - - /* Version stuff */ - QButtonGroup m_mcVersion_buttons; + std::shared_ptr m_filter; + bool m_filter_changed = false; Meta::VersionList::Ptr m_version_list; - - /* Used to tell if the filter was changed since the last getFilter() call */ - VersionButtonID m_last_version_id = VersionButtonID::Strict; - VersionButtonID m_version_id = VersionButtonID::Strict; + VersionProxyModel* m_versions_proxy = nullptr; }; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index ebe5d2be1..74e27e5f7 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -7,7 +7,7 @@ 0 0 400 - 300 + 127 @@ -16,35 +16,143 @@ 0 + + 0 + Minecraft versions + + + + + 0 + 0 + + + + 5 + + + QComboBox::AdjustToContentsOnFirstShow + + + + + + + Include Snapshots + + + + + + + + Loaders + + + + QLayout::SetMinimumSize + + + + + false + + + LiteLoader + + + + + + + false + + + Cauldron + + + - - - - - allVersions - - - - - - - strictVersion - - - - - - - majorVersion - - - - + + + NeoForge + + + + + + + Forge + + + + + + + Quilt + + + + + + + Fabric + + + + + + + + Others + + + + + + Environments + + + + QLayout::SetDefaultConstraint + + + + + Client + + + + + + + Server + + + + + + + + + + Instaled status + + + + + + Hide already installed + + + + +