UI menu now accepts mouse input from absolutes.
This commit is contained in:
@ -1,243 +1,245 @@
|
||||
// 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 {
|
||||
template<typename ...A>
|
||||
class StateOwnerEventLegacy : public IStateOwnerEventLegacy {
|
||||
public:
|
||||
IStateOwner *owner;
|
||||
Event<A...> *event;
|
||||
std::function<void(A...)> 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<IStateEvent*> eventsSubscribed;
|
||||
std::vector<IStateOwnerEventLegacy*> eventLegacyBridge;
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
) {
|
||||
if(property.owner == nullptr) {
|
||||
property.owner = this;
|
||||
} else {
|
||||
// TODO: This actually isn't needed, but because I need to teardown
|
||||
// all the listeners on StateProperty<> that belong to this StateOwner
|
||||
// I need to keep track of what events I've subbed to, consuming more
|
||||
// memory, and I don't know if I actually need to do this or not.
|
||||
assertTrue(property.owner == this);
|
||||
}
|
||||
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()) {
|
||||
auto property = *itProp;
|
||||
if(property->owner == nullptr) { property->owner = this; } else { assertTrue(property->owner == this); }
|
||||
property->_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
|
||||
) {
|
||||
if(property.owner == nullptr) { property.owner = this; } else { assertTrue(property.owner == this); }
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<typename F, typename... A>
|
||||
std::function<void()> useEventLegacy(
|
||||
F fn,
|
||||
Event<A...> &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<A...>();
|
||||
bridge->owner = this;
|
||||
bridge->event = &event;
|
||||
bridge->fn = fn;
|
||||
event.addListener(bridge, &StateOwnerEventLegacy<A...>::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;
|
||||
}
|
||||
}
|
||||
};
|
||||
// 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 {
|
||||
template<typename ...A>
|
||||
class StateOwnerEventLegacy : public IStateOwnerEventLegacy {
|
||||
public:
|
||||
IStateOwner *owner;
|
||||
Event<A...> *event;
|
||||
std::function<void(A...)> 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<IStateEvent*> eventsSubscribed;
|
||||
std::vector<IStateOwnerEventLegacy*> eventLegacyBridge;
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
) {
|
||||
if(property.owner == nullptr) {
|
||||
property.owner = this;
|
||||
} else {
|
||||
// TODO: This actually isn't needed, but because I need to teardown
|
||||
// all the listeners on StateProperty<> that belong to this StateOwner
|
||||
// I need to keep track of what events I've subbed to, consuming more
|
||||
// memory, and I don't know if I actually need to do this or not.
|
||||
assertTrue(property.owner == this);
|
||||
}
|
||||
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()) {
|
||||
auto property = *itProp;
|
||||
if(property->owner == nullptr) { property->owner = this; } else { assertTrue(property->owner == this); }
|
||||
property->_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
|
||||
) {
|
||||
if(property.owner == nullptr) { property.owner = this; } else { assertTrue(property.owner == this); }
|
||||
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.
|
||||
* @return A method that, when invoked, will unsubscribe from the event.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return A method that, when invoked, will unsubscribe from the event.
|
||||
*/
|
||||
template<typename F, typename... A>
|
||||
std::function<void()> useEventLegacy(
|
||||
F fn,
|
||||
Event<A...> &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<A...>();
|
||||
bridge->owner = this;
|
||||
bridge->event = &event;
|
||||
bridge->fn = fn;
|
||||
event.addListener(bridge, &StateOwnerEventLegacy<A...>::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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -80,6 +80,28 @@ namespace Dawn {
|
||||
return *this;
|
||||
}
|
||||
|
||||
StateProperty& operator++() {
|
||||
this->setInternal(_realValue + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
V operator++(int) {
|
||||
V temp = _realValue;
|
||||
this->setInternal(_realValue + 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
StateProperty& operator--() {
|
||||
this->setInternal(_realValue - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
V operator--(int) {
|
||||
V temp = _realValue;
|
||||
this->setInternal(_realValue - 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const V operator->() const {
|
||||
return this->_realValue;
|
||||
}
|
||||
|
Reference in New Issue
Block a user