From 54e976363106a0760e9f31611d229b211ea413cd Mon Sep 17 00:00:00 2001 From: HJfod <60038575+HJfod@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:17:20 +0200 Subject: [PATCH] make mod list state updating use events instead of poor man's delegates --- loader/src/ui/mods/ModsLayer.cpp | 5 ++- loader/src/ui/mods/ModsLayer.hpp | 2 + loader/src/ui/mods/UpdateModListState.cpp | 20 ++++++++++ loader/src/ui/mods/UpdateModListState.hpp | 39 +++++++++++++++++++ loader/src/ui/mods/list/ModItem.cpp | 23 ++++------- loader/src/ui/mods/list/ModItem.hpp | 7 +--- loader/src/ui/mods/list/ModList.cpp | 11 +----- loader/src/ui/mods/list/ModList.hpp | 4 +- .../ui/mods/popups/ConfirmUninstallPopup.cpp | 8 +--- .../ui/mods/popups/ConfirmUninstallPopup.hpp | 6 +-- loader/src/ui/mods/popups/ModPopup.cpp | 22 +++-------- loader/src/ui/mods/popups/ModPopup.hpp | 5 +-- loader/src/ui/mods/sources/ModSource.cpp | 11 ++++++ loader/src/ui/mods/sources/ModSource.hpp | 1 + 14 files changed, 100 insertions(+), 64 deletions(-) create mode 100644 loader/src/ui/mods/UpdateModListState.cpp create mode 100644 loader/src/ui/mods/UpdateModListState.hpp diff --git a/loader/src/ui/mods/ModsLayer.cpp b/loader/src/ui/mods/ModsLayer.cpp index e3fd6396..8a13f157 100644 --- a/loader/src/ui/mods/ModsLayer.cpp +++ b/loader/src/ui/mods/ModsLayer.cpp @@ -167,6 +167,10 @@ bool ModsLayer::init() { this->updateState(); + // The overall mods layer only cares about page number updates + m_updateStateListener.setFilter(UpdateModListStateFilter(UpdatePageNumberState())); + m_updateStateListener.bind([this](auto) { this->updateState(); }); + return true; } @@ -190,7 +194,6 @@ void ModsLayer::gotoTab(ModListSourceType type) { // Lazily create new list and add it to UI if (!m_lists.contains(src)) { auto list = ModList::create(src, m_frame->getContentSize() - ccp(30, 0)); - list->onUpdateParentState(std::bind(&ModsLayer::updateState, this)); list->setPosition(m_frame->getPosition()); this->addChild(list); m_lists.emplace(src, list); diff --git a/loader/src/ui/mods/ModsLayer.hpp b/loader/src/ui/mods/ModsLayer.hpp index 71cc00f5..e885c203 100644 --- a/loader/src/ui/mods/ModsLayer.hpp +++ b/loader/src/ui/mods/ModsLayer.hpp @@ -6,6 +6,7 @@ #include "list/ModItem.hpp" #include "list/ModList.hpp" #include "sources/ModListSource.hpp" +#include "UpdateModListState.hpp" using namespace geode::prelude; @@ -19,6 +20,7 @@ protected: CCLabelBMFont* m_pageLabel; CCMenuItemSpriteExtra* m_goToPageBtn; CCMenuItemSpriteExtra* m_restartBtn; + EventListener m_updateStateListener; bool m_showSearch = false; bool m_bigView = false; diff --git a/loader/src/ui/mods/UpdateModListState.cpp b/loader/src/ui/mods/UpdateModListState.cpp new file mode 100644 index 00000000..4e18fcd0 --- /dev/null +++ b/loader/src/ui/mods/UpdateModListState.cpp @@ -0,0 +1,20 @@ +#include "UpdateModListState.hpp" + +UpdateModListStateEvent::UpdateModListStateEvent(UpdateState&& target) : target(target) {} + +ListenerResult UpdateModListStateFilter::handle(MiniFunction fn, UpdateModListStateEvent* event) { + if ( + // If the listener wants to hear all state updates then let it + std::holds_alternative(m_target) || + // If the event is update everything then update everything + std::holds_alternative(event->target) || + // Otherwise only run if the event is what is asked for + m_target == event->target + ) { + fn(event); + } + return ListenerResult::Propagate; +} + +UpdateModListStateFilter::UpdateModListStateFilter() : m_target(UpdateWholeState()) {} +UpdateModListStateFilter::UpdateModListStateFilter(UpdateState&& target) : m_target(target) {} diff --git a/loader/src/ui/mods/UpdateModListState.hpp b/loader/src/ui/mods/UpdateModListState.hpp new file mode 100644 index 00000000..6bf6a100 --- /dev/null +++ b/loader/src/ui/mods/UpdateModListState.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include "sources/ModSource.hpp" + +using namespace geode::prelude; + +struct UpdatePageNumberState final { + constexpr bool operator==(UpdatePageNumberState const&) const = default; +}; +struct UpdateWholeState final { + constexpr bool operator==(UpdateWholeState const&) const = default; +}; +struct UpdateModState final { + std::string modID; + inline explicit UpdateModState(std::string const& modID) : modID(modID) {}; + constexpr bool operator==(UpdateModState const&) const = default; +}; +using UpdateState = std::variant; + +struct UpdateModListStateEvent : public Event { + UpdateState target; + + UpdateModListStateEvent(UpdateState&& target); +}; + +class UpdateModListStateFilter : public EventFilter { +public: + using Callback = void(UpdateModListStateEvent*); + +protected: + UpdateState m_target; + +public: + ListenerResult handle(MiniFunction fn, UpdateModListStateEvent* event); + + UpdateModListStateFilter(); + UpdateModListStateFilter(UpdateState&& target); +}; diff --git a/loader/src/ui/mods/list/ModItem.cpp b/loader/src/ui/mods/list/ModItem.cpp index 6eb4dc0c..a9aa17dd 100644 --- a/loader/src/ui/mods/list/ModItem.cpp +++ b/loader/src/ui/mods/list/ModItem.cpp @@ -126,6 +126,10 @@ bool ModItem::init(ModSource&& source) { this->updateState(); + // Only listen for updates on this mod specifically + m_updateStateListener.setFilter(UpdateModListStateFilter(UpdateModState(m_source.getID()))); + m_updateStateListener.bind([this](auto) { this->updateState(); }); + return true; } @@ -161,11 +165,6 @@ void ModItem::updateState() { m_bg->setOpacity(40); } - // Propagate update up the chain - if (m_updateParentState) { - m_updateParentState(); - } - // Update enable toggle state if (m_enableToggle && m_source.asMod()) { m_enableToggle->toggle(m_source.asMod()->isOrWillBeEnabled()); @@ -216,10 +215,6 @@ void ModItem::updateSize(float width, bool big) { this->updateLayout(); } -void ModItem::onUpdateParentState(MiniFunction listener) { - m_updateParentState = listener; -} - ModItem* ModItem::create(ModSource&& source) { auto ret = new ModItem(); if (ret && ret->init(std::move(source))) { @@ -232,11 +227,7 @@ ModItem* ModItem::create(ModSource&& source) { void ModItem::onView(CCObject*) { // Always open up the popup for the installed mod page if that is possible - auto popup = ModPopup::create(m_source.tryConvertToMod()); - popup->onUpdateParentState([this]() { - this->updateState(); - }); - popup->show(); + ModPopup::create(m_source.tryConvertToMod())->show(); } void ModItem::onEnable(CCObject*) { @@ -252,6 +243,6 @@ void ModItem::onEnable(CCObject*) { } } - // Update whole state of the mod item - this->updateState(); + // Update state of the mod item + UpdateModListStateEvent(UpdateModState(m_source.getID())).post(); } diff --git a/loader/src/ui/mods/list/ModItem.hpp b/loader/src/ui/mods/list/ModItem.hpp index 228cca01..8c742aa2 100644 --- a/loader/src/ui/mods/list/ModItem.hpp +++ b/loader/src/ui/mods/list/ModItem.hpp @@ -3,6 +3,7 @@ #include #include #include "../sources/ModSource.hpp" +#include "../UpdateModListState.hpp" using namespace geode::prelude; @@ -19,9 +20,9 @@ protected: CCLabelBMFont* m_developerLabel; ButtonSprite* m_restartRequiredLabel = nullptr; CCMenu* m_viewMenu; - MiniFunction m_updateParentState = nullptr; CCMenuItemToggler* m_enableToggle = nullptr; CCScale9Sprite* m_checkmark = nullptr; + EventListener m_updateStateListener; /** * @warning Make sure `getMetadata` and `createModLogo` are callable @@ -29,8 +30,6 @@ protected: */ bool init(ModSource&& source); - // This should never be exposed outside, so the parent can't call this and - // cause an infinite loop during state updating void updateState(); void onEnable(CCObject*); @@ -40,6 +39,4 @@ public: static ModItem* create(ModSource&& source); void updateSize(float width, bool big); - - void onUpdateParentState(MiniFunction listener); }; diff --git a/loader/src/ui/mods/list/ModList.cpp b/loader/src/ui/mods/list/ModList.cpp index 42187c53..6faf82cb 100644 --- a/loader/src/ui/mods/list/ModList.cpp +++ b/loader/src/ui/mods/list/ModList.cpp @@ -194,7 +194,6 @@ void ModList::onPromise(typename ModListSource::PageLoadEvent* event) { } first = false; m_list->m_contentLayer->addChild(item); - item->onUpdateParentState(m_updateParentState); } this->updateSize(m_bigSize); @@ -319,10 +318,8 @@ void ModList::updatePageNumber() { m_pagePrevBtn->setVisible(pageCount && m_page > 0); m_pageNextBtn->setVisible(pageCount && m_page < pageCount.value() - 1); - // Notify container about page count update - if (m_updateParentState) { - m_updateParentState(); - } + // Post the update page number event + UpdateModListStateEvent(UpdatePageNumberState()).post(); } void ModList::reloadPage() { @@ -373,10 +370,6 @@ void ModList::showStatus(ModListStatus status, std::string const& message, std:: m_statusContainer->updateLayout(); } -void ModList::onUpdateParentState(MiniFunction listener) { - m_updateParentState = listener; -} - void ModList::onFilters(CCObject*) { TagsPopup::create(m_source, [this]() { this->gotoPage(0); diff --git a/loader/src/ui/mods/list/ModList.hpp b/loader/src/ui/mods/list/ModList.hpp index ef1124f8..f4ef1497 100644 --- a/loader/src/ui/mods/list/ModList.hpp +++ b/loader/src/ui/mods/list/ModList.hpp @@ -32,7 +32,7 @@ protected: CCMenuItemSpriteExtra* m_pageNextBtn; Ref m_searchMenu; TextInput* m_searchInput; - MiniFunction m_updateParentState = nullptr; + EventListener m_updateStateListener; bool m_bigSize = false; std::atomic m_searchInputThreads = 0; @@ -47,8 +47,6 @@ protected: public: static ModList* create(ModListSource* src, CCSize const& size); - // poor man's delegate - void onUpdateParentState(MiniFunction listener); size_t getPage() const; void reloadPage(); diff --git a/loader/src/ui/mods/popups/ConfirmUninstallPopup.cpp b/loader/src/ui/mods/popups/ConfirmUninstallPopup.cpp index af724441..d4e21ab0 100644 --- a/loader/src/ui/mods/popups/ConfirmUninstallPopup.cpp +++ b/loader/src/ui/mods/popups/ConfirmUninstallPopup.cpp @@ -55,9 +55,7 @@ void ConfirmUninstallPopup::onUninstall(CCObject*) { )->show(); } - if (m_updateParentState) { - m_updateParentState(); - } + UpdateModListStateEvent(UpdateModState(m_mod->getID())).post(); this->onClose(nullptr); } @@ -71,7 +69,3 @@ ConfirmUninstallPopup* ConfirmUninstallPopup::create(Mod* mod) { CC_SAFE_DELETE(ret); return nullptr; } - -void ConfirmUninstallPopup::onUpdateParentState(MiniFunction listener) { - m_updateParentState = listener; -} diff --git a/loader/src/ui/mods/popups/ConfirmUninstallPopup.hpp b/loader/src/ui/mods/popups/ConfirmUninstallPopup.hpp index 99064681..116069b4 100644 --- a/loader/src/ui/mods/popups/ConfirmUninstallPopup.hpp +++ b/loader/src/ui/mods/popups/ConfirmUninstallPopup.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "../UpdateModListState.hpp" using namespace geode::prelude; @@ -8,7 +9,7 @@ class ConfirmUninstallPopup : public Popup { protected: Mod* m_mod; CCMenuItemToggler* m_deleteDataToggle; - MiniFunction m_updateParentState = nullptr; + EventListener m_updateStateListener; bool setup(Mod* mod) override; @@ -16,7 +17,4 @@ protected: public: static ConfirmUninstallPopup* create(Mod* mod); - - // todo: replace all of these with a single event - void onUpdateParentState(MiniFunction listener); }; diff --git a/loader/src/ui/mods/popups/ModPopup.cpp b/loader/src/ui/mods/popups/ModPopup.cpp index 7e7ee74b..ce65060c 100644 --- a/loader/src/ui/mods/popups/ModPopup.cpp +++ b/loader/src/ui/mods/popups/ModPopup.cpp @@ -434,6 +434,10 @@ bool ModPopup::setup(ModSource&& src) { m_tagsListener.bind(this, &ModPopup::onLoadTags); m_tagsListener.setFilter(m_source.fetchValidTags().listen()); + // Only listen for updates on this mod specifically + m_updateStateListener.setFilter(UpdateModListStateFilter(UpdateModState(m_source.getID()))); + m_updateStateListener.bind([this](auto) { this->updateState(); }); + return true; } @@ -463,11 +467,6 @@ void ModPopup::updateState() { } m_installMenu->updateLayout(); - - // Propagate update up the chain - if (m_updateParentState) { - m_updateParentState(); - } } void ModPopup::setStatIcon(CCNode* stat, const char* spr) { @@ -653,17 +652,12 @@ void ModPopup::onEnable(CCObject*) { else { FLAlertLayer::create("Error Toggling Mod", "This mod can not be toggled!", "OK")->show(); } - this->updateState(); + UpdateModListStateEvent(UpdateModState(m_source.getID())).post(); } void ModPopup::onUninstall(CCObject*) { if (auto mod = m_source.asMod()) { - auto popup = ConfirmUninstallPopup::create(mod); - popup->onUpdateParentState([this] { - this->updateState(); - this->onClose(nullptr); - }); - popup->show(); + ConfirmUninstallPopup::create(mod)->show(); } else { FLAlertLayer::create( @@ -693,7 +687,3 @@ ModPopup* ModPopup::create(ModSource&& src) { CC_SAFE_DELETE(ret); return nullptr; } - -void ModPopup::onUpdateParentState(MiniFunction listener) { - m_updateParentState = listener; -} diff --git a/loader/src/ui/mods/popups/ModPopup.hpp b/loader/src/ui/mods/popups/ModPopup.hpp index a8a9d98e..1917553a 100644 --- a/loader/src/ui/mods/popups/ModPopup.hpp +++ b/loader/src/ui/mods/popups/ModPopup.hpp @@ -4,6 +4,7 @@ #include #include "../sources/ModSource.hpp" #include "../GeodeStyle.hpp" +#include "../UpdateModListState.hpp" using namespace geode::prelude; @@ -31,7 +32,7 @@ protected: std::unordered_map>> m_tabs; EventListener> m_statsListener; EventListener, server::ServerError>> m_tagsListener; - MiniFunction m_updateParentState = nullptr; + EventListener m_updateStateListener; bool setup(ModSource&& src) override; void updateState(); @@ -52,6 +53,4 @@ protected: public: static ModPopup* create(ModSource&& src); - - void onUpdateParentState(MiniFunction listener); }; diff --git a/loader/src/ui/mods/sources/ModSource.cpp b/loader/src/ui/mods/sources/ModSource.cpp index aefa7977..6695bc8f 100644 --- a/loader/src/ui/mods/sources/ModSource.cpp +++ b/loader/src/ui/mods/sources/ModSource.cpp @@ -4,6 +4,17 @@ ModSource::ModSource(Mod* mod) : m_value(mod) {} ModSource::ModSource(server::ServerModMetadata&& metadata) : m_value(metadata) {} +std::string ModSource::getID() const { + return std::visit(makeVisitor { + [](Mod* mod) { + return mod->getID(); + }, + [](server::ServerModMetadata const& metadata) { + // Versions should be guaranteed to have at least one item + return metadata.id; + } + }, m_value); +} ModMetadata ModSource::getMetadata() const { return std::visit(makeVisitor { [](Mod* mod) { diff --git a/loader/src/ui/mods/sources/ModSource.hpp b/loader/src/ui/mods/sources/ModSource.hpp index a69c5d81..013614ec 100644 --- a/loader/src/ui/mods/sources/ModSource.hpp +++ b/loader/src/ui/mods/sources/ModSource.hpp @@ -14,6 +14,7 @@ public: ModSource(Mod* mod); ModSource(server::ServerModMetadata&& metadata); + std::string getID() const; ModMetadata getMetadata() const; std::optional getAbout() const; std::optional getChangelog() const;