VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
eventHandler.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4// Created by Arnis Lektauers on 12.10.2025.
5//
6#include "eventHandler.h"
7
9{
10 namespace
11 {
12 bool callbacksEquivalent(const HandleEventCallback& lhs, const HandleEventCallback& rhs)
13 {
14 if (!lhs || !rhs) {
15 return false;
16 }
17
18 if (lhs.target_type() != rhs.target_type()) {
19 return false;
20 }
21
22 if (const auto lhsFn = lhs.template target<void(*)(const EventArgs&)>()) {
23 const auto rhsFn = rhs.template target<void(*)(const EventArgs&)>();
24 return rhsFn && *lhsFn == *rhsFn;
25 }
26
27 return false;
28 }
29 }
30
31 void EventHandle::setRemoved(const bool value)
32 {
33 if (value) {
34 _removed = true;
35 }
36 }
37
38 void EventHandle::callback(const EventArgs& args) const
39 {
40 if (_callback) {
41 _callback(args);
42 }
43 }
44
46 {
47 if (_handler && !_removed) {
48 _handler->offByHandle(this);
49 }
50 }
51
52 EventHandle::EventHandle(EventHandler* handler, const std::string& name, HandleEventCallback callback, void* scope,
53 bool once): _handler(handler), _name(name), _callback(std::move(callback)), _scope(scope), _once(once), _removed(false) {}
54
55 EventHandle* EventHandler::on(const std::string& name, HandleEventCallback callback, void* scope)
56 {
57 return addCallback(name, callback, scope, false);
58 }
59
60 EventHandle* EventHandler::once(const std::string& name, HandleEventCallback callback, void* scope)
61 {
62 return addCallback(name, callback, scope, true);
63 }
64
65 EventHandle* EventHandler::addCallback(const std::string& name, HandleEventCallback callback, void* scope, bool once)
66 {
67 if (_callbacks.find(name) == _callbacks.end()) {
68 _callbacks[name] = std::vector<EventHandle*>();
69 }
70
71 // if we are adding a callback to the list that is executing right now,
72 // ensure we preserve an initial list before modifications
73 auto activeIt = _callbackActive.find(name);
74 if (activeIt != _callbackActive.end()) {
75 auto callbackIt = _callbacks.find(name);
76 if (callbackIt != _callbacks.end() && activeIt->second == callbackIt->second) {
77 _callbackActive[name] = std::vector<EventHandle*>(activeIt->second);
78 }
79 }
80
81 auto evt = std::make_unique<EventHandle>(this, name, std::move(callback), scope, once);
82 EventHandle* evtRaw = evt.get();
83 _eventHandles.push_back(std::move(evt));
84
85 _callbacks[name].push_back(evtRaw);
86 return evtRaw;
87 }
88
89 EventHandler* EventHandler::off(const std::string& name, const HandleEventCallback& callback, void* scope)
90 {
91 auto preserveActiveList = [this](const std::string& eventName) {
92 auto activeIt = _callbackActive.find(eventName);
93 if (activeIt == _callbackActive.end()) {
94 return;
95 }
96
97 auto callbackIt = _callbacks.find(eventName);
98 if (callbackIt != _callbacks.end() && activeIt->second == callbackIt->second) {
99 _callbackActive[eventName] = std::vector<EventHandle*>(activeIt->second);
100 }
101 };
102
103 auto removePredicate = [&](EventHandle* evt) {
104 if (!evt) {
105 return false;
106 }
107
108 if (scope && evt->_scope != scope) {
109 return false;
110 }
111
112 if (!callback) {
113 return true;
114 }
115
116 return callbacksEquivalent(evt->_callback, callback);
117 };
118
119 if (name.empty()) {
120 for (const auto& [eventName, callbacks] : _callbacks) {
121 (void)callbacks;
122 preserveActiveList(eventName);
123 }
124
125 for (auto& [_, callbacks] : _callbacks) {
126 callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
127 [&](EventHandle* evt) {
128 if (removePredicate(evt)) {
129 evt->setRemoved(true);
130 evt->_handler = nullptr;
131 evt->_callback = HandleEventCallback();
132 return true;
133 }
134 return false;
135 }), callbacks.end());
136 }
137
138 for (auto it = _callbacks.begin(); it != _callbacks.end();) {
139 if (it->second.empty()) {
140 it = _callbacks.erase(it);
141 } else {
142 ++it;
143 }
144 }
145
146 compactRemovedHandles();
147
148 return this;
149 }
150
151 preserveActiveList(name);
152 auto callbackIt = _callbacks.find(name);
153 if (callbackIt == _callbacks.end()) {
154 return this;
155 }
156
157 std::vector<EventHandle*>& callbacks = callbackIt->second;
158 callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
159 [&](EventHandle* evt) {
160 if (removePredicate(evt)) {
161 evt->setRemoved(true);
162 evt->_handler = nullptr;
163 evt->_callback = HandleEventCallback();
164 return true;
165 }
166 return false;
167 }), callbacks.end());
168
169 if (callbacks.empty()) {
170 _callbacks.erase(name);
171 }
172
173 compactRemovedHandles();
174
175 return this;
176 }
177
179 {
180 if (!handle || handle->_removed) {
181 return this;
182 }
183
184 const std::string& name = handle->_name;
185 handle->setRemoved(true);
186 handle->_handler = nullptr;
187 handle->_callback = HandleEventCallback();
188
189 auto activeIt = _callbackActive.find(name);
190 if (activeIt != _callbackActive.end()) {
191 auto callbackIt = _callbacks.find(name);
192 if (callbackIt != _callbacks.end() && activeIt->second == callbackIt->second) {
193 _callbackActive[name] = std::vector<EventHandle*>(activeIt->second);
194 }
195 }
196
197 auto callbacksIt = _callbacks.find(name);
198 if (callbacksIt == _callbacks.end()) {
199 return this;
200 }
201
202 std::vector<EventHandle*>& callbacks = callbacksIt->second;
203 callbacks.erase(std::remove(callbacks.begin(), callbacks.end(), handle), callbacks.end());
204
205 if (callbacks.empty()) {
206 _callbacks.erase(name);
207 }
208
209 compactRemovedHandles();
210
211 return this;
212 }
213
214 bool EventHandler::hasEvent(const std::string& name) const
215 {
216 auto it = _callbacks.find(name);
217 return it != _callbacks.end() && !it->second.empty();
218 }
219
221 {
222 for (auto& [_, handles] : _callbacks) {
223 for (auto* handle : handles) {
224 if (handle) {
225 handle->setRemoved(true);
226 handle->_handler = nullptr;
227 handle->_callback = HandleEventCallback();
228 }
229 }
230 }
231 _callbacks.clear();
232 _callbackActive.clear();
233 compactRemovedHandles();
234 }
235
236 void EventHandler::compactRemovedHandles()
237 {
238 // Callback lists can temporarily hold removed handles while dispatch is active.
239 if (!_callbackActive.empty()) {
240 return;
241 }
242
243 _eventHandles.erase(std::remove_if(_eventHandles.begin(), _eventHandles.end(),
244 [](const std::unique_ptr<EventHandle>& handle) {
245 return !handle || handle->removed();
246 }), _eventHandles.end());
247 }
248}
void callback(const EventArgs &args) const
EventHandle(EventHandler *handler, const std::string &name, HandleEventCallback callback, void *scope=nullptr, bool once=false)
EventHandle * once(const std::string &name, HandleEventCallback callback, void *scope=nullptr)
EventHandle * addCallback(const std::string &name, HandleEventCallback callback, void *scope=nullptr, bool once=false)
bool hasEvent(const std::string &name) const
EventHandler * offByHandle(EventHandle *handle)
EventHandler * off(const std::string &name="", const HandleEventCallback &callback=HandleEventCallback(), void *scope=nullptr)
EventHandle * on(const std::string &name, HandleEventCallback callback, void *scope=nullptr)
std::function< void(const EventArgs &)> HandleEventCallback
std::vector< std::any > EventArgs