// Copyright (c) 2023 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #pragma once #include "StateEvent.hpp" #include "StateProperty.hpp" namespace Dawn { class StateOwner : public IStateOwner { private: std::vector eventsSubscribed; /** * Called by the state event when it is disposing (before the StateOwner * itself is). * * @param evt Event that is being disposed. */ void _stateEventDisposed(IStateEvent *evt) override { auto it = eventsSubscribed.begin(); while(it != eventsSubscribed.end()) { if(*it == evt) { it = eventsSubscribed.erase(it); } else { ++it; } } } public: /** * Listen for changes to a state property and invoke the provided func * when the value is changed. * * @param fn The callback to be invoked when the state value changes. * @param property Property to listen for affect changees to. * @return Returns callback that invokes the provided FN immediately. */ std::function useEffect( const std::function &fn, IStateProperty &property ) { property._effectListners.push_back(fn); return fn; } /** * Listen for changes to a set of state properties and invoke the provided * func when any of their values are changed. * * @param fn The callback to be invoked when the state value changes. * @param property Vector list of properties to listen for changes to. * @return Returns callback that invokes the provided FN immediately. */ std::function useEffect( const std::function &fn, std::vector props ) { auto itProp = props.begin(); while(itProp != props.end()) { (*itProp)->_effectListners.push_back(fn); ++itProp; } return fn; } /** * Listen for changes to a state property and invoke the provided callback * also, when state is changed this will run the returned teardown * callback. * * @param fn The callback to be invoked when the state value changes. * @param property Vector list of properties to listen for changes to. * @return Returns callback that invokes the provided FN immediately. */ std::function useEffectWithTeardown( const std::function()> &fn, IStateProperty &property ) { property._effectListnersWithTeardown.push_back(fn); return std::bind([&]( std::function()> &callback, IStateProperty *prop ) { auto teardown = callback(); prop->_effectTeardowns.push_back(teardown); }, fn, &property); } /** * Listen for when an event is invoked by a state event. This is intended * to allow for cross-state-owner communication in a simple and effective * way. * * @tparam F The type of the callback function. * @tparam A The arguments from the state event that are calledback. * @param fn The function to be inokved on event trigger. * @param event The event that is being subscribed to. */ template std::function useEvent(F fn, StateEvent &event) { // Create a listener structure struct StateEventListener listener; listener.listener = fn; listener.owner = this; listener.event = &event; listener.unsubWithParams = [&](struct StateEventListener listener) { auto itFound = listener.event->_eventListeners.begin(); while(itFound != listener.event->_eventListeners.end()) { if(itFound->id == listener.id) { listener.event->_eventListeners.erase(itFound); break; } ++itFound++; } }; listener.id = event.stateEventId++; listener.unsub = std::bind(listener.unsubWithParams, listener); // Put that listener structure on to the event stack event._eventListeners.push_back(listener); this->eventsSubscribed.push_back(&event); return listener.unsub; } virtual ~StateOwner() { auto it = this->eventsSubscribed.begin(); while(it != this->eventsSubscribed.end()) { (*it)->_stateOwnerDestroyed(this); ++it; } } }; }