Skip to content

Commit a8ffc6d

Browse files
committed
Refactor event system for type-erased listeners
Removed TypedEvent and TypedEventListener in favor of a simplified Event base class and type-erased listener wrappers. Updated all event classes to inherit directly from Event and removed type index methods. EventEmitter now manages listeners using type-erased wrappers, improving type safety and flexibility. Cleaned up macOS tray icon menu delegate logic and ensured context menu cleanup via event listeners.
1 parent 84ac3f8 commit a8ffc6d

File tree

9 files changed

+131
-231
lines changed

9 files changed

+131
-231
lines changed

src/display_event.h

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,6 @@ class DisplayAddedEvent : public DisplayEvent {
5555
* Get a string representation of the event type
5656
*/
5757
std::string GetTypeName() const override { return "DisplayAddedEvent"; }
58-
59-
/**
60-
* Get the static type index for this event type
61-
*/
62-
static std::type_index GetStaticType() { return std::type_index(typeid(DisplayAddedEvent)); }
63-
64-
/**
65-
* Get the type index for this event instance
66-
*/
67-
std::type_index GetType() const { return GetStaticType(); }
6858
};
6959

7060
/**
@@ -80,16 +70,6 @@ class DisplayRemovedEvent : public DisplayEvent {
8070
* Get a string representation of the event type
8171
*/
8272
std::string GetTypeName() const override { return "DisplayRemovedEvent"; }
83-
84-
/**
85-
* Get the static type index for this event type
86-
*/
87-
static std::type_index GetStaticType() { return std::type_index(typeid(DisplayRemovedEvent)); }
88-
89-
/**
90-
* Get the type index for this event instance
91-
*/
92-
std::type_index GetType() const { return GetStaticType(); }
9373
};
9474

9575
/**
@@ -119,16 +99,6 @@ class DisplayChangedEvent : public DisplayEvent {
11999
*/
120100
std::string GetTypeName() const override { return "DisplayChangedEvent"; }
121101

122-
/**
123-
* Get the static type index for this event type
124-
*/
125-
static std::type_index GetStaticType() { return std::type_index(typeid(DisplayChangedEvent)); }
126-
127-
/**
128-
* Get the type index for this event instance
129-
*/
130-
std::type_index GetType() const { return GetStaticType(); }
131-
132102
private:
133103
Display old_display_;
134104
};

src/foundation/event.h

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
#include <functional>
55
#include <memory>
66
#include <string>
7-
#include <typeindex>
8-
#include <unordered_map>
97

108
namespace nativeapi {
119

@@ -29,68 +27,67 @@ class Event {
2927
};
3028

3129
/**
32-
* Template for typed events. This provides type safety and automatic
33-
* type identification for events.
30+
* Generic event listener interface providing type-safe event handling.
31+
*
32+
* This interface supports both generic and specific event handling:
33+
* - Use EventListener<Event> to handle all event types (requires manual type checking)
34+
* - Use EventListener<SpecificEventType> for compile-time type safety with specific events
35+
*
36+
* Example:
37+
* ```cpp
38+
* class MyListener : public EventListener<MyCustomEvent> {
39+
* public:
40+
* void OnEvent(const MyCustomEvent& event) override {
41+
* // Handle the event with full type safety
42+
* }
43+
* };
44+
* ```
3445
*/
3546
template <typename T>
36-
class TypedEvent : public Event {
37-
public:
38-
static std::type_index GetStaticType() { return std::type_index(typeid(T)); }
39-
40-
std::type_index GetType() const { return GetStaticType(); }
41-
42-
std::string GetTypeName() const override { return typeid(T).name(); }
43-
};
44-
45-
/**
46-
* Generic event listener interface that can handle any event type.
47-
* This is the base interface for the observer pattern.
48-
*/
4947
class EventListener {
5048
public:
5149
virtual ~EventListener() = default;
5250

5351
/**
54-
* Handle an event. Implementations should check the event type
55-
* and cast appropriately.
52+
* Handles an incoming event of type T.
53+
*
54+
* The event parameter is guaranteed to be of type T or a subtype.
55+
* Implementation should process the event according to the listener's logic.
5656
*/
57-
virtual void OnEvent(const Event& event) = 0;
57+
virtual void OnEvent(const T& event) = 0;
5858
};
5959

6060
/**
61-
* Template for typed event listeners. This provides type safety
62-
* by automatically casting events to the correct type.
61+
* A callback-based event listener that wraps function callbacks into the EventListener interface.
62+
*
63+
* This implementation allows using function references, lambda functions, or any callable
64+
* as event handlers without requiring a full class implementation. It's particularly useful
65+
* for simple event handling scenarios or when you want to use inline functions.
66+
*
67+
* Example usage:
68+
* ```cpp
69+
* // Using a lambda function
70+
* auto listener = std::make_unique<CallbackEventListener<MyEvent>>(
71+
* [](const MyEvent& event) { std::cout << "Received: " << event << std::endl; });
72+
*
73+
* // Using a function reference
74+
* void handleMyEvent(const MyEvent& event) { // handle event }
75+
* auto listener = std::make_unique<CallbackEventListener<MyEvent>>(handleMyEvent);
76+
* ```
6377
*/
64-
template <typename EventType>
65-
class TypedEventListener : public EventListener {
78+
template <typename T>
79+
class CallbackEventListener : public EventListener<T> {
6680
public:
67-
virtual ~TypedEventListener() = default;
68-
69-
void OnEvent(const Event& event) override {
70-
// Check if this is the correct event type
71-
if (auto typed_event = dynamic_cast<const EventType*>(&event)) {
72-
OnTypedEvent(*typed_event);
73-
}
74-
}
81+
using CallbackType = std::function<void(const T&)>;
7582

7683
/**
77-
* Handle a typed event. Subclasses should override this method.
84+
* Creates a new callback-based event listener with the specified callback function.
85+
*
86+
* The callback must accept a single parameter of type T and return void.
7887
*/
79-
virtual void OnTypedEvent(const EventType& event) = 0;
80-
};
81-
82-
/**
83-
* Callback-based event handler that wraps std::function callbacks.
84-
* This allows using lambda functions or function pointers as event handlers.
85-
*/
86-
template <typename EventType>
87-
class CallbackEventListener : public TypedEventListener<EventType> {
88-
public:
89-
using CallbackType = std::function<void(const EventType&)>;
90-
9188
explicit CallbackEventListener(CallbackType callback) : callback_(std::move(callback)) {}
9289

93-
void OnTypedEvent(const EventType& event) override {
90+
void OnEvent(const T& event) override {
9491
if (callback_) {
9592
callback_(event);
9693
}

src/foundation/event_emitter.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ EventEmitter::~EventEmitter() {
1313
}
1414

1515
size_t EventEmitter::AddListener(std::type_index event_type,
16-
EventListener* listener) {
16+
std::unique_ptr<EventListenerBase> listener) {
1717
if (!listener) {
1818
return 0; // Invalid listener
1919
}
2020

2121
std::lock_guard<std::mutex> lock(listeners_mutex_);
2222
size_t listener_id = next_listener_id_.fetch_add(1);
2323

24-
listeners_[event_type].push_back({listener, listener_id});
24+
listeners_[event_type].push_back({std::move(listener), listener_id});
2525

2626
return listener_id;
2727
}
@@ -57,7 +57,7 @@ void EventEmitter::RemoveAllListeners() {
5757

5858
void EventEmitter::EmitSync(const Event& event) {
5959
std::type_index event_type = typeid(event);
60-
std::vector<EventListener*> listeners_copy;
60+
std::vector<EventListenerBase*> listeners_copy;
6161

6262
// Copy listeners to avoid holding the lock during dispatch
6363
{
@@ -66,7 +66,7 @@ void EventEmitter::EmitSync(const Event& event) {
6666
if (it != listeners_.end()) {
6767
listeners_copy.reserve(it->second.size());
6868
for (const auto& info : it->second) {
69-
listeners_copy.push_back(info.listener);
69+
listeners_copy.push_back(info.listener.get());
7070
}
7171
}
7272
}

src/foundation/event_emitter.h

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,21 @@ class EventEmitter {
5252
* @return A unique listener ID that can be used to remove the listener
5353
*/
5454
template <typename EventType>
55-
size_t AddListener(TypedEventListener<EventType>* listener) {
56-
return AddListener(TypedEvent<EventType>::GetStaticType(), listener);
55+
size_t AddListener(EventListener<EventType>* listener) {
56+
// Create a wrapper that handles the type conversion
57+
struct TypedListenerWrapper : public EventListenerBase {
58+
EventListener<EventType>* listener_;
59+
60+
TypedListenerWrapper(EventListener<EventType>* listener) : listener_(listener) {}
61+
62+
void OnEvent(const Event& event) override {
63+
if (auto typed_event = dynamic_cast<const EventType*>(&event)) {
64+
listener_->OnEvent(*typed_event);
65+
}
66+
}
67+
};
68+
69+
return AddListener(typeid(EventType), std::make_unique<TypedListenerWrapper>(listener));
5770
}
5871

5972
/**
@@ -64,18 +77,22 @@ class EventEmitter {
6477
*/
6578
template <typename EventType>
6679
size_t AddListener(std::function<void(const EventType&)> callback) {
67-
auto callback_listener =
68-
std::make_unique<CallbackEventListener<EventType>>(std::move(callback));
69-
auto listener_ptr = callback_listener.get();
70-
71-
// Store the callback listener first, then add it
72-
{
73-
std::lock_guard<std::mutex> lock(listeners_mutex_);
74-
callback_listeners_.emplace_back(std::move(callback_listener));
75-
}
76-
77-
// Use the type-erased method to avoid infinite recursion
78-
return AddListener(TypedEvent<EventType>::GetStaticType(), listener_ptr);
80+
// Create a wrapper that handles the callback and type conversion
81+
struct CallbackListenerWrapper : public EventListenerBase {
82+
std::function<void(const EventType&)> callback_;
83+
84+
CallbackListenerWrapper(std::function<void(const EventType&)> callback)
85+
: callback_(std::move(callback)) {}
86+
87+
void OnEvent(const Event& event) override {
88+
if (auto typed_event = dynamic_cast<const EventType*>(&event)) {
89+
callback_(*typed_event);
90+
}
91+
}
92+
};
93+
94+
return AddListener(typeid(EventType),
95+
std::make_unique<CallbackListenerWrapper>(std::move(callback)));
7996
}
8097

8198
/**
@@ -91,7 +108,7 @@ class EventEmitter {
91108
*/
92109
template <typename EventType>
93110
void RemoveAllListeners() {
94-
RemoveAllListeners(TypedEvent<EventType>::GetStaticType());
111+
RemoveAllListeners(typeid(EventType));
95112
}
96113

97114
/**
@@ -104,7 +121,7 @@ class EventEmitter {
104121
*/
105122
template <typename EventType>
106123
size_t GetListenerCount() const {
107-
return GetListenerCount(TypedEvent<EventType>::GetStaticType());
124+
return GetListenerCount(typeid(EventType));
108125
}
109126

110127
/**
@@ -173,13 +190,19 @@ class EventEmitter {
173190
}
174191

175192
private:
193+
// Base interface for type-erased listeners
194+
struct EventListenerBase {
195+
virtual ~EventListenerBase() = default;
196+
virtual void OnEvent(const Event& event) = 0;
197+
};
198+
176199
struct ListenerInfo {
177-
EventListener* listener;
200+
std::unique_ptr<EventListenerBase> listener;
178201
size_t id;
179202
};
180203

181204
// Type-erased methods for internal use
182-
size_t AddListener(std::type_index event_type, EventListener* listener);
205+
size_t AddListener(std::type_index event_type, std::unique_ptr<EventListenerBase> listener);
183206
void RemoveAllListeners(std::type_index event_type);
184207
size_t GetListenerCount(std::type_index event_type) const;
185208

@@ -191,7 +214,7 @@ class EventEmitter {
191214
std::unordered_map<std::type_index, std::vector<ListenerInfo>> listeners_;
192215

193216
// Storage for callback listeners to manage their lifetime
194-
std::vector<std::unique_ptr<EventListener>> callback_listeners_;
217+
std::vector<std::unique_ptr<EventListenerBase>> callback_listeners_;
195218

196219
// Async event processing
197220
std::mutex queue_mutex_;
@@ -212,9 +235,10 @@ class EventEmitter {
212235
* DEFINE_EVENT(MyEvent) {
213236
* std::string message;
214237
* int code;
238+
* std::string GetTypeName() const override { return "MyEvent"; }
215239
* };
216240
*/
217-
#define DEFINE_EVENT(EventName) class EventName : public TypedEvent<EventName>
241+
#define DEFINE_EVENT(EventName) class EventName : public Event
218242

219243
/**
220244
* Helper macro to begin defining an event with custom constructor.
@@ -224,10 +248,11 @@ class EventEmitter {
224248
* std::string message;
225249
* int code;
226250
* MyEvent(std::string msg, int c) : message(std::move(msg)), code(c) {}
251+
* std::string GetTypeName() const override { return "MyEvent"; }
227252
* DEFINE_EVENT_END();
228253
*/
229-
#define DEFINE_EVENT_BEGIN(EventName) \
230-
class EventName : public TypedEvent<EventName> { \
254+
#define DEFINE_EVENT_BEGIN(EventName) \
255+
class EventName : public Event { \
231256
public:
232257

233258
#define DEFINE_EVENT_END() \
@@ -237,11 +262,12 @@ class EventEmitter {
237262
/**
238263
* Simpler macro for events with basic data members.
239264
* Creates a constructor that initializes all members.
265+
* Note: You must implement GetTypeName() in the class body.
240266
*/
241-
#define SIMPLE_EVENT(EventName, ...) \
242-
class EventName : public TypedEvent<EventName> { \
243-
public: \
244-
__VA_ARGS__ \
267+
#define SIMPLE_EVENT(EventName, ...) \
268+
class EventName : public Event { \
269+
public: \
270+
__VA_ARGS__ \
245271
};
246272

247273
} // namespace nativeapi

0 commit comments

Comments
 (0)