diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index 6d23ce9a..f609e94c 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -140,6 +140,13 @@ VersionPtr VersionList::getVersion(const QString &version) return out; } +bool VersionList::hasVersion(QString version) const +{ + auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(), + [&](Meta::VersionPtr const& a){ return a->version() == version; }); + return (ver != m_versions.constEnd()); +} + void VersionList::setName(const QString &name) { m_name = name; diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index 378255df..a6db2fd7 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -66,6 +66,7 @@ public: QString humanReadable() const; VersionPtr getVersion(const QString &version); + bool hasVersion(QString version) const; QVector versions() const { diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index 7382d1cf..d740c8cb 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -127,9 +127,9 @@ QList ModDownloadDialog::getPages() { QList pages; - pages.append(new ModrinthModPage(this, m_instance)); + pages.append(ModrinthModPage::create(this, m_instance)); if (APPLICATION->capabilities() & Application::SupportsFlame) - pages.append(new FlameModPage(this, m_instance)); + pages.append(FlameModPage::create(this, m_instance)); return pages; } diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index a34a74db..986caa77 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -47,12 +47,12 @@ #include "ui/dialogs/ModDownloadDialog.h" #include "ui/widgets/ProjectItem.h" + ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api) : QWidget(dialog) , m_instance(instance) , ui(new Ui::ModPage) , dialog(dialog) - , filter_widget(static_cast(instance)->getPackProfile()->getComponentVersion("net.minecraft"), this) , m_fetch_progress(this, false) , api(api) { @@ -76,17 +76,6 @@ ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api) m_fetch_progress.progressFormat(""); ui->gridLayout_3->addWidget(&m_fetch_progress, 0, 0, 1, ui->gridLayout_3->columnCount()); - ui->gridLayout_3->addWidget(&filter_widget, 1, 0, 1, ui->gridLayout_3->columnCount()); - - filter_widget.setInstance(static_cast(m_instance)); - m_filter = filter_widget.getFilter(); - - connect(&filter_widget, &ModFilterWidget::filterChanged, this, [&]{ - ui->searchButton->setStyleSheet("text-decoration: underline"); - }); - connect(&filter_widget, &ModFilterWidget::filterUnchanged, this, [&]{ - ui->searchButton->setStyleSheet("text-decoration: none"); - }); ui->packView->setItemDelegate(new ProjectItemDelegate(this)); ui->packView->installEventFilter(this); @@ -97,6 +86,26 @@ ModPage::~ModPage() delete ui; } +void ModPage::setFilterWidget(unique_qobject_ptr& widget) +{ + if (m_filter_widget) + disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); + + m_filter_widget.swap(widget); + + ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, ui->gridLayout_3->columnCount()); + + m_filter_widget->setInstance(static_cast(m_instance)); + m_filter = m_filter_widget->getFilter(); + + connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, [&]{ + ui->searchButton->setStyleSheet("text-decoration: underline"); + }); + connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, [&]{ + ui->searchButton->setStyleSheet("text-decoration: none"); + }); +} + /******** Qt things ********/ @@ -141,13 +150,13 @@ auto ModPage::eventFilter(QObject* watched, QEvent* event) -> bool void ModPage::filterMods() { - filter_widget.setHidden(!filter_widget.isHidden()); + m_filter_widget->setHidden(!m_filter_widget->isHidden()); } void ModPage::triggerSearch() { - auto changed = filter_widget.changed(); - m_filter = filter_widget.getFilter(); + auto changed = m_filter_widget->changed(); + m_filter = m_filter_widget->getFilter(); if(changed){ ui->packView->clearSelection(); diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 09c38d8b..3f31651c 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -21,7 +21,17 @@ class ModPage : public QWidget, public BasePage { Q_OBJECT public: - explicit ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api); + template + static T* create(ModDownloadDialog* dialog, BaseInstance* instance) + { + auto page = new T(dialog, instance); + + auto filter_widget = ModFilterWidget::create(static_cast(instance)->getPackProfile()->getComponentVersion("net.minecraft"), page); + page->setFilterWidget(filter_widget); + + return page; + } + ~ModPage() override; /* Affects what the user sees */ @@ -51,6 +61,8 @@ class ModPage : public QWidget, public BasePage { /** Programatically set the term in the search bar. */ void setSearchTerm(QString); + void setFilterWidget(unique_qobject_ptr&); + auto getCurrent() -> ModPlatform::IndexedPack& { return current; } void updateModVersions(int prev_count = -1); @@ -60,6 +72,7 @@ class ModPage : public QWidget, public BasePage { BaseInstance* m_instance; protected: + ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api); void updateSelectionButton(); protected slots: @@ -73,7 +86,7 @@ class ModPage : public QWidget, public BasePage { Ui::ModPage* ui = nullptr; ModDownloadDialog* dialog = nullptr; - ModFilterWidget filter_widget; + unique_qobject_ptr m_filter_widget; std::shared_ptr m_filter; ProgressWidget m_fetch_progress; diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h index 445d0368..2cd484cb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.h +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.h @@ -44,7 +44,12 @@ class FlameModPage : public ModPage { Q_OBJECT public: - explicit FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance); + static FlameModPage* create(ModDownloadDialog* dialog, BaseInstance* instance) + { + return ModPage::create(dialog, instance); + } + + FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance); ~FlameModPage() override = default; inline auto displayName() const -> QString override { return "CurseForge"; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h index 94985f63..40d82e6f 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h @@ -44,7 +44,12 @@ class ModrinthModPage : public ModPage { Q_OBJECT public: - explicit ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instance); + static ModrinthModPage* create(ModDownloadDialog* dialog, BaseInstance* instance) + { + return ModPage::create(dialog, instance); + } + + ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instance); ~ModrinthModPage() override = default; inline auto displayName() const -> QString override { return "Modrinth"; } diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 4ab34375..ea052c41 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,6 +1,39 @@ #include "ModFilterWidget.h" #include "ui_ModFilterWidget.h" +#include "Application.h" + +unique_qobject_ptr ModFilterWidget::create(Version default_version, QWidget* parent) +{ + auto filter_widget = new ModFilterWidget(default_version, parent); + + if (!filter_widget->versionList()->isLoaded()) { + QEventLoop load_version_list_loop; + + QTimer time_limit_for_list_load; + time_limit_for_list_load.setTimerType(Qt::TimerType::CoarseTimer); + time_limit_for_list_load.setSingleShot(true); + 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(); + + connect(task.get(), &Task::failed, [filter_widget]{ + filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); + }); + connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); + + if (!task->isRunning()) + task->start(); + + load_version_list_loop.exec(); + if (time_limit_for_list_load.isActive()) + time_limit_for_list_load.stop(); + } + + return unique_qobject_ptr(filter_widget); +} + ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget) { @@ -16,6 +49,7 @@ ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) m_filter->versions.push_front(def); + m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); setHidden(true); } @@ -51,24 +85,30 @@ auto ModFilterWidget::getFilter() -> std::shared_ptr return m_filter; } -void ModFilterWidget::disableVersionButton(VersionButtonID id) +void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason) { + QAbstractButton* btn = nullptr; + switch(id){ case(VersionButtonID::Strict): - ui->strictVersionButton->setEnabled(false); + btn = ui->strictVersionButton; break; case(VersionButtonID::Major): - ui->majorVersionButton->setEnabled(false); + btn = ui->majorVersionButton; break; case(VersionButtonID::All): - ui->allVersionsButton->setEnabled(false); + btn = ui->allVersionsButton; break; case(VersionButtonID::Between): - // ui->betweenVersionsButton->setEnabled(false); - break; default: break; } + + if (btn) { + btn->setEnabled(false); + if (!reason.isEmpty()) + btn->setText(btn->text() + QString(" (%1)").arg(reason)); + } } void ModFilterWidget::onVersionFilterChanged(int id) @@ -76,7 +116,7 @@ void ModFilterWidget::onVersionFilterChanged(int id) //ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between); //ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between); - int index = 0; + int index = 1; auto cast_id = (VersionButtonID) id; if (cast_id != m_version_id) { @@ -93,10 +133,15 @@ void ModFilterWidget::onVersionFilterChanged(int id) break; case(VersionButtonID::Major): { auto versionSplit = mcVersionStr().split("."); - for(auto i = Version(QString("%1.%2").arg(versionSplit[0], versionSplit[1])); i <= mcVersion(); index++){ - m_filter->versions.push_front(i); - i = Version(QString("%1.%2.%3").arg(versionSplit[0], versionSplit[1], QString("%1").arg(index))); + + 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): diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 334fc672..958a1e2b 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -4,6 +4,10 @@ #include #include "Version.h" + +#include "meta/Index.h" +#include "meta/VersionList.h" + #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" @@ -34,18 +38,22 @@ public: std::shared_ptr m_filter; public: - explicit ModFilterWidget(Version def, QWidget* parent = nullptr); + 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); + void disableVersionButton(VersionButtonID, QString reason = {}); auto getFilter() -> std::shared_ptr; auto changed() const -> bool { return m_last_version_id != m_version_id; } + Meta::VersionListPtr versionList() { return m_version_list; } + private: + ModFilterWidget(Version def, QWidget* parent = nullptr); + inline auto mcVersionStr() const -> QString { return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; } inline auto mcVersion() const -> Version { return { mcVersionStr() }; } @@ -61,8 +69,12 @@ private: MinecraftInstance* m_instance = nullptr; + +/* Version stuff */ QButtonGroup m_mcVersion_buttons; + Meta::VersionListPtr 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;