Skip to content

Commit eb6d024

Browse files
committed
v5 event migration guide
1 parent 247ea40 commit eb6d024

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

tutorials/migrate-v5.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,128 @@ set(CMAKE_CXX_STANDARD 20)
99

1010
to say `23` instead of `20`.
1111

12+
## Changes to `Event`
13+
14+
The entire event system has been reworked. There are now multiple event classes for different purposes, and filters are far more performant, though a lot less dynamic. \
15+
Events now also have priority ordering, for defining the order in which listeners are handled.
16+
17+
### Usage migration
18+
19+
```cpp
20+
// BEFORE:
21+
// # Sending an event
22+
PlayerHealthEvent("joe123", 65).post();
23+
24+
// # Listening to an event
25+
// leaking the EventListener
26+
new EventListener<EventFilter<PlayerHealthEvent>>(+[](PlayerHealthEvent* ev) {
27+
if (ev->getName() == "alice_") {
28+
log::info("Alice's health is now {}", ev->getHealth());
29+
}
30+
return ListenerResult::Propagate;
31+
});
32+
```
33+
34+
```cpp
35+
// AFTER:
36+
// # Sending an event
37+
PlayerHealthEvent("joe123").send(65);
38+
39+
// # Listening to an event
40+
// listener is a geode::comm::ListenerHandle
41+
auto listener = PlayerHealthEvent("alice_").listen([](int health) {
42+
log::info("Alice's health is now {}", health);
43+
return ListenerResult::Propagate;
44+
// return false; would also be equivalent
45+
});
46+
// destroying the listener will prevent our callback from being called,
47+
// so leaking it is an option
48+
listener.leak();
49+
50+
// # Priorities
51+
PlayerHealthEvent("joe123").listen([](int health) {
52+
// if the lambda returns void then its treated the same as propagate
53+
}, Priority::VeryEarly);
54+
```
55+
56+
### Migration for Event subclasses
57+
58+
```cpp
59+
// BEFORE:
60+
class PlayerHealthEvent : public Event {
61+
public:
62+
PlayerHealthEvent(std::string name, int health);
63+
64+
std::string m_name;
65+
int m_health;
66+
};
67+
```
68+
69+
```cpp
70+
// AFTER: the std::string is a filter argument for the player's name
71+
class PlayerHealthEvent : public Event<PlayerHealthEvent, bool(int health), std::string> {
72+
public:
73+
using Event::Event;
74+
};
75+
```
76+
77+
### Migration for `DispatchEvent`
78+
79+
Renamed to `geode::Dispatch`, but `DispatchEvent` is kept as an alias for easier migration. Its a thread-safe event with one filter arg for the id:
80+
81+
```cpp
82+
template <class... Args>
83+
class Dispatch : public ThreadSafeEvent<Dispatch<Args...>, bool(Args...), std::string> {
84+
// [...]
85+
};
86+
87+
template<class... Args>
88+
using DispatchEvent = Dispatch<Args...>;
89+
```
90+
91+
Usage is the same as a regular event usage
92+
93+
### Thread safety
94+
95+
Thread safety is now opt-in for events, meaning that if you want to send/listen to an event from different threads you should use the `ThreadSafe` variants.
96+
97+
```
98+
Event -> ThreadSafeEvent
99+
GlobalEvent -> ThreadSafeGlobalEvent
100+
```
101+
102+
Note that this has nothing to do with GD's main thread, event listeners will always get triggered on the same thread that sent the event.
103+
104+
### `GlobalEvent`
105+
106+
If you have an Event with a filter, it's not possible to have a listener for any filter args. In those cases, you should use `GlobalEvent`.
107+
108+
```cpp
109+
// std::string is a filter arg
110+
struct MyEvent : GlobalEvent<MyEvent, bool(int value), std::string> {
111+
using GlobalEvent::GlobalEvent;
112+
};
113+
// if you want to be efficient and not copy the std::string for each listener you can use:
114+
// struct MyEvent : GlobalEvent<MyEvent, bool(std::string_view id, int value), bool(int value), std::string> { [...] }
115+
```
116+
Usage is mostly the same, except for `listen`:
117+
```cpp
118+
// These are both valid:
119+
MyEvent().listen([](auto id, auto value) {
120+
// will receive all events, unfiltered
121+
});
122+
123+
MyEvent("some-id").listen([](auto value) {
124+
// will only trigger when id is "some-id"
125+
});
126+
```
127+
12128
## Changes to `Popup`
13129
14130
`geode::Popup` is no longer templated, and instead accepts its own arguments in `Popup::init` (which also replaces `initAnchored`). It now uses the pattern similar to any other node. For example the following code:
15131
16132
```cpp
133+
// BEFORE:
17134
class MyPopup : public Popup<int> {
18135
public:
19136
static MyPopup* create(int value) {

0 commit comments

Comments
 (0)