// 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> > 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 listen( const ListenerArgument &data, const std::function 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(); } }; }