From c658fdd08ff9c3b7b948d219a40f96dc95a7ab35 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Wed, 1 Mar 2023 08:03:03 -0800 Subject: [PATCH] More work on State System --- src/dawn/event/Event.hpp | 7 +- src/dawn/state/State.hpp | 101 +++++++++++++++++++- src/dawn/state/StateEvent.hpp | 36 +------ src/dawn/state/StateInterfaces.hpp | 53 ++++++++++ src/dawn/state/StateProperty.hpp | 9 +- src/dawntictactoe/scenes/TicTacToeScene.hpp | 9 +- 6 files changed, 167 insertions(+), 48 deletions(-) create mode 100644 src/dawn/state/StateInterfaces.hpp diff --git a/src/dawn/event/Event.hpp b/src/dawn/event/Event.hpp index 054914f4..9b621d70 100644 --- a/src/dawn/event/Event.hpp +++ b/src/dawn/event/Event.hpp @@ -8,12 +8,12 @@ #include "assert/assert.hpp" namespace Dawn { - template struct IEventListener { /** * Abstracted method for C++ template reasons. Invokes the listener. * + * @deprecated * @param args Arguments to pass to the listener. */ virtual void invoke(A... args) = 0; @@ -27,6 +27,7 @@ namespace Dawn { /** * Construct a new event listener structure. * + * @deprecated * @param instance Instance that the callback belongs to. * @param callback Callback method that invokes back. */ @@ -42,6 +43,7 @@ namespace Dawn { } }; + /** @deprecated */ template class Event { private: @@ -51,6 +53,7 @@ namespace Dawn { /** * Add a listener to this event. * + * @deprecated * @tparam T The class that will receive the event. * @param instance Instance of type T that will receive the callback. * @param callback Callback method attached to T to receive the event. @@ -72,6 +75,7 @@ namespace Dawn { /** * Removes an event listener from this event. * + * @deprecated * @tparam T The class that was once receiving the event. * @param instance Instance of type T that did receive the callback. * @param callback Callback method attached to T for the event. @@ -99,6 +103,7 @@ namespace Dawn { /** * Invokes the event and emits to all of the listeners. * + * @deprecated * @param args Arguments for this event to pass to the listeners. */ void invoke(A... args) { diff --git a/src/dawn/state/State.hpp b/src/dawn/state/State.hpp index e9161229..3bc85f07 100644 --- a/src/dawn/state/State.hpp +++ b/src/dawn/state/State.hpp @@ -8,10 +8,44 @@ #include "StateProperty.hpp" namespace Dawn { + template + class StateOwnerEventLegacy : public IStateOwnerEventLegacy { + public: + IStateOwner *owner; + Event *event; + std::function fn; + + /** + * Function used to simply remove the event listener from the legacy + * event, does not deal with the state owner. + */ + void removeListener() override { + event->removeListener(this, &StateOwnerEventLegacy::callback); + } + + /** + * Function that can be used to tear down this legacy event. + */ + void teardown() override { + this->removeListener(); + owner->_stateLegacyEventDisposed(this); + } + + /** + * Callbaack method that is invoked by the legacy event. + * @param args Arguments received by legacy event. + */ + void callback(A... args) { + this->fn(args...); + } + }; + class StateOwner : public IStateOwner { private: std::vector eventsSubscribed; - + std::vector eventLegacyBridge; + + public: /** * Called by the state event when it is disposing (before the StateOwner * itself is). @@ -28,8 +62,25 @@ namespace Dawn { } } } - - public: + + /** + * Called by legacy events when they are being disposed in a way that was + * not called by this state owner disposing. + * + * @param evt Event that is being disposed. + */ + void _stateLegacyEventDisposed(IStateOwnerEventLegacy *evt) override { + auto it = this->eventLegacyBridge.begin(); + while(it != this->eventLegacyBridge.end()) { + if(*it == evt) { + this->eventLegacyBridge.erase(it); + break; + } else { + ++it; + } + } + } + /** * Listen for changes to a state property and invoke the provided func * when the value is changed. @@ -126,15 +177,55 @@ namespace Dawn { return listener.unsub; } - - + + /** + * Listen for callback of a legacy styled event. This will be removed in + * the future in favour of everything using State Events. This uses a lot + * more memory than the new state event listener. + * + * @deprecated In favour of StateEvent<> + * @tparam F The type of the callback function. + * @tparam A Argument types for the event. + * @param fn Callback function to be invoked when the event is triggered. + * @param event Event that will be listened to. + */ + template + std::function useEventLegacy( + F fn, + Event &event + ) { + // This is a legacy feature to make upgrading to the new useEffect a bit + // easier for me. For the time being I am just bodging this together to + // do what I need here. + auto bridge = new StateOwnerEventLegacy(); + bridge->owner = this; + bridge->event = &event; + bridge->fn = fn; + event.addListener(bridge, &StateOwnerEventLegacy::callback); + eventLegacyBridge.push_back(bridge); + return std::bind([&](IStateOwnerEventLegacy *evt){ + evt->teardown(); + }, bridge); + } + + /** + * State Owner teardown function. Mostly just used to remove any lingering + * useEffects or useEvents. + */ virtual ~StateOwner() { auto it = this->eventsSubscribed.begin(); while(it != this->eventsSubscribed.end()) { (*it)->_stateOwnerDestroyed(this); ++it; } + + auto itBridge = this->eventLegacyBridge.begin(); + while(itBridge != this->eventLegacyBridge.end()) { + (*itBridge)->removeListener(); + delete *itBridge; + ++itBridge; + } } }; } \ No newline at end of file diff --git a/src/dawn/state/StateEvent.hpp b/src/dawn/state/StateEvent.hpp index 12a0ddd2..5d75f387 100644 --- a/src/dawn/state/StateEvent.hpp +++ b/src/dawn/state/StateEvent.hpp @@ -4,31 +4,14 @@ // https://opensource.org/licenses/MIT #pragma once -#include "assert/assert.hpp" -#include "event/Event.hpp" +#include "StateInterfaces.hpp" namespace Dawn { - /** Forwarders / Fake Virtuals for other methods. */ template - class StateEvent; - class IStateEvent; - class IStateOwner { - public: - virtual void _stateEventDisposed(IStateEvent *evt) = 0; - }; - - template - struct StateEventListener { - uint32_t id; - IStateOwner *owner; - std::function listener; - std::function)> unsubWithParams; - std::function unsub; - StateEvent *event; - }; - - class IStateEvent { + class StateEvent : public IStateEvent { protected: + uint32_t stateEventId = 0; + /** * Received notification from a state owner to let this state event know * that it wishes to unsubscribe all the event listeners that it may have @@ -36,17 +19,6 @@ namespace Dawn { * * @param owner State owner that is being disposed. */ - virtual void _stateOwnerDestroyed(IStateOwner *owner) = 0; - - friend class StateOwner; - }; - - template - class StateEvent : public IStateEvent { - protected: - uint32_t stateEventId = 0; - - /** Refer to IStateEvent::_stateOwnerDestroyed */ void _stateOwnerDestroyed(IStateOwner *owner) override { auto it = this->_eventListeners.begin(); while(it != this->_eventListeners.end()) { diff --git a/src/dawn/state/StateInterfaces.hpp b/src/dawn/state/StateInterfaces.hpp new file mode 100644 index 00000000..010481ce --- /dev/null +++ b/src/dawn/state/StateInterfaces.hpp @@ -0,0 +1,53 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "assert/assert.hpp" +#include "event/Event.hpp" + +namespace Dawn { + class IStateEvent; + + template + class StateEvent; + + template + class StateOwnerEventLegacy; + + class IStateOwnerEventLegacy { + public: + virtual void removeListener() = 0; + virtual void teardown() = 0; + }; + + class IStateOwner { + public: + virtual void _stateEventDisposed(IStateEvent *evt) = 0; + virtual void _stateLegacyEventDisposed(IStateOwnerEventLegacy *evt) = 0; + }; + + template + struct StateEventListener { + uint32_t id; + IStateOwner *owner; + std::function listener; + std::function)> unsubWithParams; + std::function unsub; + StateEvent *event; + }; + + class IStateProperty { + public: + std::vector> _effectListners; + std::vector()>> _effectListnersWithTeardown; + std::vector> _effectTeardowns; + }; + + class IStateEvent { + protected: + virtual void _stateOwnerDestroyed(IStateOwner *owner) = 0; + friend class StateOwner; + }; +} \ No newline at end of file diff --git a/src/dawn/state/StateProperty.hpp b/src/dawn/state/StateProperty.hpp index f34ecea4..158d1262 100644 --- a/src/dawn/state/StateProperty.hpp +++ b/src/dawn/state/StateProperty.hpp @@ -4,16 +4,9 @@ // https://opensource.org/licenses/MIT #pragma once -#include "assert/assert.hpp" +#include "StateInterfaces.hpp" namespace Dawn { - class IStateProperty { - public: - std::vector> _effectListners; - std::vector()>> _effectListnersWithTeardown; - std::vector> _effectTeardowns; - }; - template class StateProperty : public IStateProperty { private: diff --git a/src/dawntictactoe/scenes/TicTacToeScene.hpp b/src/dawntictactoe/scenes/TicTacToeScene.hpp index 9acb84f3..a50beba7 100644 --- a/src/dawntictactoe/scenes/TicTacToeScene.hpp +++ b/src/dawntictactoe/scenes/TicTacToeScene.hpp @@ -17,8 +17,8 @@ namespace Dawn { class TicTacToeScene : public Scene { protected: Camera *camera; - StateProperty age; - StateEvent ageEvent; + int32_t age = 0; + std::function evtUnsub; void stage() override { camera = Camera::create(this); @@ -43,6 +43,11 @@ namespace Dawn { tile->ticTacToe->tile = i++; } } + + evtUnsub = useEventLegacy([&]{ + std::cout << "Legacy Event Invoked " << age << std::endl; + evtUnsub(); + }, this->eventSceneUpdate); } std::vector getRequiredAssets() override {