Dawn/src/dawn/event/CustomEvent.hpp
2024-06-19 11:45:12 -05:00

141 lines
3.6 KiB
C++

// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
enum class CustomEventResult {
NOTHING,
REMOVE,
INVOKE,
INVOKE_AND_REMOVE
};
template<
typename InternalData,
typename InternalListenerData,
typename ListenerArgument,
typename ...InvokeArgs
>
class CustomEvent {
private:
int32_t nextId = 0;
std::unordered_map<
int32_t,
std::pair<InternalListenerData, std::function<void(InvokeArgs...)>>
> listeners;
protected:
InternalData internalData;
/**
* Custom event filter. Decides whether or not the event should be emitted
* to the listener.
*
* @param listenerData Data for this listener.
* @param args The arguments to pass to the listeners.
* @return The result of the filter.
*/
virtual enum CustomEventResult shouldEmit(
const InternalListenerData &listenerData,
const InvokeArgs... args
) = 0;
/**
* Transform the arguments for listener data when the listener is first
* subscribed.
*
* @param argument The argument to transform.
* @return The transformed argument into an internal data format.
*/
virtual InternalListenerData transformData(
const ListenerArgument &argument
) = 0;
/**
* Transform the data for listener data after the event has been emitted.
*
* @param internalData The internal data to transform.
* @return Updated/Transformed internal data.
*/
virtual InternalListenerData transformDataAfterEmit(
const InternalListenerData &internalData
) {
return internalData;
}
public:
/**
* Emits the event.
* @param args The arguments to pass to the listeners.
*/
void emit(InvokeArgs... args) {
auto copy = listeners;
for(auto &pair : copy) {
// Check emit test.
auto result = this->shouldEmit(
pair.second.first,
args...
);
if(
result == CustomEventResult::INVOKE ||
result == CustomEventResult::INVOKE_AND_REMOVE
) {
pair.second.second(args...);
}
if(
result == CustomEventResult::REMOVE ||
result == CustomEventResult::INVOKE_AND_REMOVE
) {
listeners.erase(pair.first);
continue;
}
if(
result == CustomEventResult::INVOKE ||
result == CustomEventResult::INVOKE_AND_REMOVE
) {
// Update the internal data.
listeners[pair.first].first = transformDataAfterEmit(
pair.second.first
);
}
}
}
/**
* Listens to the event.
*
* @param data Listener data to use.
* @param listener The listener to add.
* @returns A function that can be called to remove the listener.
*/
std::function<void()> listen(
const ListenerArgument &data,
const std::function<void(InvokeArgs...)> listener
) {
int32_t id = nextId++;
auto pair = std::make_pair(
transformData(data),
listener
);
listeners[id] = pair;
return [this, id]() {
listeners.erase(id);
};
}
/**
* Destroys the custom event.
*/
virtual ~CustomEvent() {
listeners.clear();
}
};
}