Dawn/src/dawn/state/State.hpp

140 lines
4.6 KiB
C++

// 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<IStateEvent*> 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<void()> useEffect(
const std::function<void()> &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<void()> useEffect(
const std::function<void()> &fn,
std::vector<IStateProperty*> 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<void()> useEffectWithTeardown(
const std::function<std::function<void()>()> &fn,
IStateProperty &property
) {
property._effectListnersWithTeardown.push_back(fn);
return std::bind([&](
std::function<std::function<void()>()> &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<typename F, typename... A>
std::function<void()> useEvent(F fn, StateEvent<A...> &event) {
// Create a listener structure
struct StateEventListener<A...> listener;
listener.listener = fn;
listener.owner = this;
listener.event = &event;
listener.unsubWithParams = [&](struct StateEventListener<A...> 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;
}
}
};
}