plusui-native-core 0.1.50 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/Core/CMakeLists.txt +2 -3
  2. package/Core/Features/App/app.cpp +58 -1
  3. package/Core/Features/Connection/ARCHITECTURE.md +369 -0
  4. package/Core/Features/Connection/README.md +165 -0
  5. package/Core/Features/Connection/connection.cpp +57 -0
  6. package/Core/Features/Connection/connection.ts +186 -0
  7. package/Core/Features/Connection/examples/simple_tags_example.hpp +130 -0
  8. package/Core/Features/Connection/examples/simple_tags_example.ts +247 -0
  9. package/Core/Features/Window/window.cpp +187 -151
  10. package/Core/generated/bridge.hpp +302 -0
  11. package/Core/include/plusui/connection.hpp +145 -0
  12. package/Core/include/plusui/plusui.hpp +1 -3
  13. package/Core/include/plusui/window.hpp +17 -19
  14. package/package.json +1 -1
  15. package/Core/Features/Bindings/ARCHITECTURE.md +0 -328
  16. package/Core/Features/Bindings/CustomBindings/custom_bindings.cpp +0 -55
  17. package/Core/Features/Bindings/CustomBindings/custom_bindings.ts +0 -35
  18. package/Core/Features/Bindings/EXAMPLE_USAGE.hpp +0 -143
  19. package/Core/Features/Bindings/EXAMPLE_USAGE.tsx +0 -210
  20. package/Core/Features/Bindings/IPC_GUIDE.md +0 -325
  21. package/Core/Features/Bindings/NativeBindings/native_bindings.cpp +0 -30
  22. package/Core/Features/Bindings/NativeBindings/native_bindings.ts +0 -29
  23. package/Core/Features/Bindings/UNIFIED_SYSTEM.md +0 -351
  24. package/Core/Features/Event/Events.ts +0 -166
  25. package/Core/Features/Event/events.cpp +0 -200
  26. package/Core/include/plusui/bindings.hpp +0 -65
  27. package/Core/include/plusui/custom_bindings.hpp +0 -17
  28. package/Core/include/plusui/events.hpp +0 -58
@@ -1,351 +0,0 @@
1
- # PlusUI Unified Binding System
2
-
3
- ## One Macro to Rule Them All: `PLUSUI_BIND`
4
-
5
- The PlusUI framework now uses a **single, unified macro** for all frontend ↔ backend communication.
6
-
7
- ### Simple. Type-Safe. Bidirectional.
8
-
9
- ```cpp
10
- PLUSUI_BIND(name, returnType, ...paramTypes)
11
- ```
12
-
13
- ## How It Works
14
-
15
- ### 1. Define Bindings in C++
16
-
17
- ```cpp
18
- // src/features/myapp.hpp
19
- #pragma once
20
- #include <plusui/bindings.hpp>
21
-
22
- //====================================
23
- // Frontend → Backend (Methods)
24
- //====================================
25
- PLUSUI_BIND(getUserName, string, void)
26
- PLUSUI_BIND(setTheme, void, string)
27
- PLUSUI_BIND(calculate, int, int, int)
28
- PLUSUI_BIND(queryDatabase, json, string)
29
-
30
- //====================================
31
- // Backend → Frontend (Events)
32
- // Names starting with "on" = events
33
- //====================================
34
- PLUSUI_BIND(onDataUpdated, void, json)
35
- PLUSUI_BIND(onProgressUpdate, void, int)
36
- PLUSUI_BIND(onError, void, string)
37
- ```
38
-
39
- ### 2. Run Code Generation
40
-
41
- ```bash
42
- # Generate type-safe TypeScript bindings
43
- plusui generate
44
-
45
- # Or directly:
46
- npx plusui-bindgen . ./src/Bindings
47
- ```
48
-
49
- ### 3. Use in TypeScript
50
-
51
- ```typescript
52
- // Auto-generated: ./src/Bindings/bindings.gen.ts
53
- import { plusui } from './bindings.gen';
54
-
55
- //====================================
56
- // Call Backend Methods
57
- //====================================
58
- const name = await plusui.myapp.getUserName();
59
- await plusui.myapp.setTheme('dark');
60
- const result = await plusui.myapp.calculate(10, 20);
61
- const data = await plusui.myapp.queryDatabase('SELECT * FROM users');
62
-
63
- //====================================
64
- // Listen to Backend Events
65
- //====================================
66
- plusui.myapp.onDataUpdated((data) => {
67
- console.log('Data changed:', data);
68
- updateUI(data);
69
- });
70
-
71
- const unsubscribe = plusui.myapp.onProgressUpdate((percent) => {
72
- setProgress(percent);
73
- });
74
-
75
- plusui.myapp.onError((error) => {
76
- toast.error(error);
77
- });
78
-
79
- // Clean up when component unmounts
80
- unsubscribe();
81
- ```
82
-
83
- ### 4. Implement in C++
84
-
85
- ```cpp
86
- // src/features/myapp.cpp
87
- #include "myapp.hpp"
88
- #include <plusui/bindings.hpp>
89
-
90
- using json = nlohmann::json;
91
-
92
- //====================================
93
- // Implement Methods
94
- //====================================
95
- std::string getUserName() {
96
- return "PlusUI Developer";
97
- }
98
-
99
- void setTheme(const std::string& theme) {
100
- applyTheme(theme);
101
- }
102
-
103
- int calculate(int x, int y) {
104
- return x + y;
105
- }
106
-
107
- json queryDatabase(const std::string& sql) {
108
- return db.execute(sql);
109
- }
110
-
111
- //====================================
112
- // Emit Events from C++
113
- //====================================
114
- void onDataChanged() {
115
- json update = {
116
- {"timestamp", getCurrentTime()},
117
- {"records", getRecordCount()}
118
- };
119
- plusui::bindings::emitToFrontend("onDataUpdated", update);
120
- }
121
-
122
- void processFile(const std::string& path) {
123
- for (int i = 0; i < 100; i++) {
124
- // Do work...
125
-
126
- // Emit progress
127
- plusui::bindings::emitToFrontend("onProgressUpdate", i);
128
- }
129
- }
130
- ```
131
-
132
- ## Two Categories of Bindings
133
-
134
- ### NativeBindings (Core Framework)
135
-
136
- Auto-detected based on your TypeScript imports:
137
-
138
- ```typescript
139
- import { window, app, display } from 'plusui-native-core';
140
-
141
- // These automatically generate native bindings:
142
- await window.minimize();
143
- await app.quit();
144
- const displays = await display.getAll();
145
- ```
146
-
147
- **Features:**
148
- - Window (minimize, maximize, show, hide, etc.)
149
- - App (quit, ready lifecycle)
150
- - Display (get displays)
151
- - Clipboard (read/write)
152
- - Keyboard (shortcuts)
153
- - Menu (native menus)
154
- - Tray (system tray)
155
- - Browser (open URLs)
156
-
157
- ### CustomBindings (Your App Logic)
158
-
159
- Defined by **you** using `PLUSUI_BIND`:
160
-
161
- ```cpp
162
- // Your custom features
163
- PLUSUI_BIND(authenticateUser, bool, string, string)
164
- PLUSUI_BIND(loadFile, string, string)
165
- PLUSUI_BIND(saveFile, void, string, string)
166
- PLUSUI_BIND(onFileChanged, void, json)
167
- ```
168
-
169
- ## Generated File Structure
170
-
171
- After running `plusui generate`:
172
-
173
- ```
174
- src/Bindings/
175
- ├── bindings.gen.ts # Main unified TypeScript export
176
- ├── bindings.gen.hpp # C++ binding metadata
177
- ├── bindings.report.json # Generation report
178
-
179
- ├── NativeBindings/
180
- │ ├── CPP_IO/
181
- │ │ ├── core.bindings.gen.hpp
182
- │ │ └── core.bindings.gen.cpp
183
- │ └── WEB_IO/
184
- │ └── core.bindings.gen.ts
185
-
186
- └── CustomBindings/
187
- ├── CPP_IO/
188
- │ ├── custom.bindings.gen.hpp
189
- │ └── custom.bindings.gen.cpp
190
- └── WEB_IO/
191
- └── custom.bindings.gen.ts
192
-
193
- include/Bindings/ # Mirrored C++ headers for imports
194
- ├── bindings.gen.hpp
195
- ├── NativeBindings/
196
- │ └── CPP_IO/
197
- │ └── core.bindings.gen.hpp
198
- └── CustomBindings/
199
- └── CPP_IO/
200
- └── custom.bindings.gen.hpp
201
- ```
202
-
203
- ## Type Mapping
204
-
205
- | C++ Type | TypeScript Type | Example |
206
- |-----------------------|-----------------|-----------------|
207
- | `void` | `void` | No return |
208
- | `bool` | `boolean` | `true`, `false` |
209
- | `int`, `float`, `double` | `number` | `42`, `3.14` |
210
- | `string`, `std::string` | `string` | `"hello"` |
211
- | `json`, `nlohmann::json` | `any` | Objects, arrays |
212
-
213
- ## Naming Conventions
214
-
215
- ### Methods (Frontend → Backend)
216
- - Any descriptive name
217
- - Examples: `getUserData`, `calculate`, `saveFile`, `queryDatabase`
218
-
219
- ### Events (Backend → Frontend)
220
- - **Must start with "on" prefix**
221
- - Examples: `onDataChanged`, `onError`, `onProgress`, `onFileUpdated`
222
-
223
- This makes the direction clear at a glance!
224
-
225
- ## Import Styles
226
-
227
- ### Recommended: Unified Import
228
-
229
- ```typescript
230
- import { plusui } from './bindings.gen';
231
-
232
- // Access everything through plusui
233
- await plusui.myFeature.myMethod();
234
- plusui.myFeature.onEvent((data) => { });
235
- ```
236
-
237
- ### Alternative: Individual Imports
238
-
239
- ```typescript
240
- import { myFeature, otherFeature } from './bindings.gen';
241
-
242
- // Direct access
243
- await myFeature.myMethod();
244
- myFeature.onEvent((data) => { });
245
- ```
246
-
247
- ## Best Practices
248
-
249
- ✅ **Use PLUSUI_BIND for Everything**
250
- - Don't mix with manual bindings
251
- - One system = simple codebase
252
-
253
- ✅ **Event Names Start with "on"**
254
- - Makes direction obvious
255
- - `onProgress`, `onError`, `onUpdate`
256
-
257
- ✅ **Group Related Bindings**
258
- - `database.hpp` for DB bindings
259
- - `auth.hpp` for auth bindings
260
- - etc.
261
-
262
- ✅ **Run Generate After Changes**
263
- ```bash
264
- plusui generate
265
- ```
266
-
267
- ✅ **Use `json` for Complex Data**
268
- ```cpp
269
- PLUSUI_BIND(getUser, json, int) // Not: (string, string, int, bool, ...)
270
- ```
271
-
272
- ✅ **Clean Up Event Listeners**
273
- ```typescript
274
- const unsubscribe = plusui.feature.onEvent(() => {});
275
- // Later:
276
- unsubscribe();
277
- ```
278
-
279
- ## Migration from Old System
280
-
281
- ### Before (Multiple Macros)
282
- ```cpp
283
- PLUSUI_METHOD(getUserName, string, void)
284
- PLUSUI_EVENT(onDataChanged, json)
285
- PLUSUI_SERVICE(dataService, json, string)
286
- PLUSUI_STREAM(logStream, string)
287
- ```
288
-
289
- ### After (Unified)
290
- ```cpp
291
- PLUSUI_BIND(getUserName, string, void)
292
- PLUSUI_BIND(onDataChanged, void, json)
293
- PLUSUI_BIND(dataService, json, string)
294
- PLUSUI_BIND(logStream, void, string) // Events use void + "on" prefix
295
- ```
296
-
297
- ## Runtime/Dynamic Bindings (Advanced)
298
-
299
- For advanced scenarios (plugins, hot-reload):
300
-
301
- ```typescript
302
- import { registerMethod, registerEvent } from './bindings/custom_bindings';
303
-
304
- // Register at runtime
305
- registerMethod('dynamicMethod', async (params) => {
306
- return processParams(params);
307
- });
308
-
309
- registerEvent('dynamicEvent', (data) => {
310
- console.log('Dynamic event:', data);
311
- });
312
- ```
313
-
314
- ## Under the Hood
315
-
316
- ### Compile Time
317
- 1. `plusui-bindgen` scans all C++ files for `PLUSUI_BIND`
318
- 2. Parses type signatures
319
- 3. Auto-detects direction (method vs event)
320
- 4. Generates TypeScript classes with proper types
321
- 5. Generates C++ registration metadata
322
-
323
- ### Runtime
324
- 1. Frontend calls `plusui.feature.method(args)`
325
- 2. TypeScript bridge serializes as JSON
326
- 3. WebView IPC sends to C++ via `__invoke__`
327
- 4. C++ dispatcher routes to registered handler
328
- 5. Result serialized back to frontend
329
- 6. Promise resolves with typed result
330
-
331
- ### Events
332
- 1. C++ calls `plusui::bindings::emitToFrontend(name, data)`
333
- 2. Serializes JSON data
334
- 3. WebView dispatches as CustomEvent
335
- 4. Frontend listeners receive typed data
336
-
337
- ## Summary
338
-
339
- **Old Way:**
340
- - Multiple macros (PLUSUI_METHOD, PLUSUI_EVENT, PLUSUI_SERVICE, PLUSUI_STREAM)
341
- - Confusing overlap between Events and Bindings
342
- - Manual type mapping
343
-
344
- **New Way:**
345
- - **One macro**: `PLUSUI_BIND`
346
- - **Simple naming**: methods vs events (starting with "on")
347
- - **Auto-generated**: Run `plusui generate`
348
- - **Type-safe**: Full TypeScript autocomplete
349
- - **Bidirectional**: Methods and events in one system
350
-
351
- **Result:** Clean, simple, powerful! 🚀
@@ -1,166 +0,0 @@
1
- /**
2
- * PlusUI Event System - scalable bidirectional events
3
- *
4
- * Backend:
5
- * event::on("myEvent", callback) // Listen for events from frontend
6
- * event::once("myEvent", callback) // Listen once
7
- * event::emit("myEvent", data) // Send events to frontend
8
- *
9
- * Frontend:
10
- * const stop = event.on("myEvent", callback) // Listen + unsubscribe
11
- * event.once("myEvent", callback) // Listen once
12
- * event.emit("myEvent", data) // Send events to backend
13
- *
14
- * Users create custom event names anywhere.
15
- */
16
-
17
- type EventCallback<T = unknown> = (data: T) => void;
18
- type AnyEventCallback = (eventName: string, data: unknown) => void;
19
- export type Unsubscribe = () => void;
20
-
21
- const listeners = new Map<string, Set<EventCallback>>();
22
- const anyListeners = new Set<AnyEventCallback>();
23
- const attachedDomBridges = new Set<string>();
24
-
25
- let _invoke: ((method: string, args?: unknown[]) => Promise<unknown>) | null = null;
26
-
27
- export function setInvokeFn(fn: (method: string, args?: unknown[]) => Promise<unknown>) {
28
- _invoke = fn;
29
- }
30
-
31
- async function invoke(method: string, args?: unknown[]): Promise<unknown> {
32
- if (!_invoke) {
33
- if (typeof window !== 'undefined' && (window as any).__invoke__) {
34
- _invoke = (window as any).__invoke__;
35
- } else {
36
- throw new Error('Event API not initialized');
37
- }
38
- }
39
- return _invoke!(method, args);
40
- }
41
-
42
- function validateEventName(name: string): void {
43
- if (!name || !name.trim()) {
44
- throw new Error('Event name must be a non-empty string');
45
- }
46
- }
47
-
48
- function emitToLocalListeners(name: string, data: unknown): void {
49
- const eventListeners = listeners.get(name);
50
- if (eventListeners && eventListeners.size > 0) {
51
- for (const callback of Array.from(eventListeners)) {
52
- callback(data);
53
- }
54
- }
55
-
56
- for (const callback of Array.from(anyListeners)) {
57
- callback(name, data);
58
- }
59
- }
60
-
61
- function ensureDomBridge(name: string): void {
62
- if (typeof window === 'undefined' || attachedDomBridges.has(name)) {
63
- return;
64
- }
65
-
66
- const domEvent = `plusui:event:${name}`;
67
- const handler = (event: Event) => {
68
- emitToLocalListeners(name, (event as CustomEvent).detail);
69
- };
70
-
71
- window.addEventListener(domEvent, handler as EventListener);
72
- attachedDomBridges.add(name);
73
- }
74
-
75
- export const event = {
76
- /**
77
- * Listen for events from backend
78
- * Returns an unsubscribe function.
79
- */
80
- on: <T = unknown>(name: string, callback: EventCallback<T>): Unsubscribe => {
81
- validateEventName(name);
82
-
83
- if (!listeners.has(name)) {
84
- listeners.set(name, new Set());
85
- }
86
- listeners.get(name)!.add(callback as EventCallback);
87
-
88
- ensureDomBridge(name);
89
-
90
- return () => {
91
- event.off(name, callback as EventCallback);
92
- };
93
- },
94
-
95
- /**
96
- * Listen once for an event from backend.
97
- */
98
- once: <T = unknown>(name: string, callback: EventCallback<T>): Unsubscribe => {
99
- validateEventName(name);
100
-
101
- let unsubscribe: Unsubscribe = () => {};
102
- const wrapped = (data: unknown) => {
103
- unsubscribe();
104
- callback(data as T);
105
- };
106
-
107
- unsubscribe = event.on(name, wrapped);
108
- return unsubscribe;
109
- },
110
-
111
- /**
112
- * Listen to every incoming backend event that has a registered bridge.
113
- */
114
- onAny: (callback: AnyEventCallback): Unsubscribe => {
115
- if (typeof callback !== 'function') {
116
- throw new Error('onAny callback must be a function');
117
- }
118
-
119
- anyListeners.add(callback);
120
-
121
- return () => {
122
- anyListeners.delete(callback);
123
- };
124
- },
125
-
126
- /**
127
- * Remove one callback from a named event.
128
- */
129
- off: (name: string, callback: EventCallback): boolean => {
130
- validateEventName(name);
131
-
132
- const set = listeners.get(name);
133
- if (!set) {
134
- return false;
135
- }
136
-
137
- const removed = set.delete(callback);
138
- if (set.size === 0) {
139
- listeners.delete(name);
140
- }
141
-
142
- return removed;
143
- },
144
-
145
- /**
146
- * Remove all listeners. If name is set, clear only that event.
147
- */
148
- clear: (name?: string): void => {
149
- if (name === undefined) {
150
- listeners.clear();
151
- anyListeners.clear();
152
- return;
153
- }
154
-
155
- validateEventName(name);
156
- listeners.delete(name);
157
- },
158
-
159
- /**
160
- * Emit events to backend.
161
- */
162
- emit: async (name: string, data: unknown): Promise<void> => {
163
- validateEventName(name);
164
- await invoke('event.emit', [name, data]);
165
- },
166
- };
@@ -1,200 +0,0 @@
1
- // Event API implementation
2
- #include <plusui/events.hpp>
3
- #include <functional>
4
- #include <unordered_map>
5
- #include <vector>
6
- #include <mutex>
7
- #include <atomic>
8
- #include <algorithm>
9
- #include <utility>
10
- #include <iostream>
11
-
12
- namespace plusui {
13
- namespace event {
14
-
15
- struct ListenerEntry {
16
- ListenerId id;
17
- EventCallback callback;
18
- bool once;
19
- };
20
-
21
- struct AnyListenerEntry {
22
- ListenerId id;
23
- AnyEventCallback callback;
24
- bool once;
25
- };
26
-
27
- // Event listeners registry
28
- static std::unordered_map<std::string, std::vector<ListenerEntry>> listeners;
29
- static std::vector<AnyListenerEntry> any_listeners;
30
- static std::mutex mutex;
31
- static std::atomic<ListenerId> next_listener_id{1};
32
-
33
- // Bridge to frontend (set by webview initialization)
34
- std::function<void(const std::string&, const EventData&)> __bridge_to_frontend__;
35
-
36
- // Listen for events from frontend
37
- ListenerId on(const std::string& name, EventCallback callback) {
38
- if (name.empty() || !callback) {
39
- return 0;
40
- }
41
-
42
- const ListenerId id = next_listener_id.fetch_add(1);
43
- std::lock_guard<std::mutex> lock(mutex);
44
- listeners[name].push_back({id, std::move(callback), false});
45
- return id;
46
- }
47
-
48
- ListenerId once(const std::string& name, EventCallback callback) {
49
- if (name.empty() || !callback) {
50
- return 0;
51
- }
52
-
53
- const ListenerId id = next_listener_id.fetch_add(1);
54
- std::lock_guard<std::mutex> lock(mutex);
55
- listeners[name].push_back({id, std::move(callback), true});
56
- return id;
57
- }
58
-
59
- ListenerId on_any(AnyEventCallback callback) {
60
- if (!callback) {
61
- return 0;
62
- }
63
-
64
- const ListenerId id = next_listener_id.fetch_add(1);
65
- std::lock_guard<std::mutex> lock(mutex);
66
- any_listeners.push_back({id, std::move(callback), false});
67
- return id;
68
- }
69
-
70
- bool off(const std::string& name, ListenerId id) {
71
- if (name.empty() || id == 0) {
72
- return false;
73
- }
74
-
75
- std::lock_guard<std::mutex> lock(mutex);
76
- auto it = listeners.find(name);
77
- if (it == listeners.end()) {
78
- return false;
79
- }
80
-
81
- auto& bucket = it->second;
82
- const auto old_size = bucket.size();
83
- bucket.erase(
84
- std::remove_if(bucket.begin(), bucket.end(), [id](const ListenerEntry& entry) {
85
- return entry.id == id;
86
- }),
87
- bucket.end()
88
- );
89
- const auto new_size = bucket.size();
90
-
91
- if (bucket.empty()) {
92
- listeners.erase(it);
93
- }
94
-
95
- return new_size != old_size;
96
- }
97
-
98
- bool off_any(ListenerId id) {
99
- if (id == 0) {
100
- return false;
101
- }
102
-
103
- std::lock_guard<std::mutex> lock(mutex);
104
- const auto old_size = any_listeners.size();
105
- any_listeners.erase(
106
- std::remove_if(any_listeners.begin(), any_listeners.end(), [id](const AnyListenerEntry& entry) {
107
- return entry.id == id;
108
- }),
109
- any_listeners.end()
110
- );
111
-
112
- return any_listeners.size() != old_size;
113
- }
114
-
115
- std::size_t off_all(const std::string& name) {
116
- if (name.empty()) {
117
- return 0;
118
- }
119
-
120
- std::lock_guard<std::mutex> lock(mutex);
121
- auto it = listeners.find(name);
122
- if (it == listeners.end()) {
123
- return 0;
124
- }
125
-
126
- const auto removed = it->second.size();
127
- listeners.erase(it);
128
- return removed;
129
- }
130
-
131
- void clear() {
132
- std::lock_guard<std::mutex> lock(mutex);
133
- listeners.clear();
134
- any_listeners.clear();
135
- }
136
-
137
- // Emit events to frontend
138
- void emit(const std::string& name, const EventData& data) {
139
- if (__bridge_to_frontend__) {
140
- __bridge_to_frontend__(name, data);
141
- }
142
- }
143
-
144
- // Internal: Receive event from frontend (called by binding system)
145
- // This is registered as "event.emit" in the IPC handlers
146
- void __dispatch_from_frontend__(const std::string& name, const EventData& data) {
147
- std::vector<ListenerEntry> named_callbacks;
148
- std::vector<AnyListenerEntry> global_callbacks;
149
-
150
- {
151
- std::lock_guard<std::mutex> lock(mutex);
152
- auto it = listeners.find(name);
153
- if (it != listeners.end()) {
154
- named_callbacks = it->second;
155
- it->second.erase(
156
- std::remove_if(it->second.begin(), it->second.end(), [](const ListenerEntry& entry) {
157
- return entry.once;
158
- }),
159
- it->second.end()
160
- );
161
-
162
- if (it->second.empty()) {
163
- listeners.erase(it);
164
- }
165
- }
166
-
167
- global_callbacks = any_listeners;
168
- any_listeners.erase(
169
- std::remove_if(any_listeners.begin(), any_listeners.end(), [](const AnyListenerEntry& entry) {
170
- return entry.once;
171
- }),
172
- any_listeners.end()
173
- );
174
- }
175
-
176
- for (const auto& entry : named_callbacks) {
177
- try {
178
- entry.callback(data);
179
- } catch (const std::exception& ex) {
180
- std::cerr << "[PlusUI:event] listener error for '" << name << "': " << ex.what() << std::endl;
181
- } catch (...) {
182
- std::cerr << "[PlusUI:event] listener error for '" << name << "': unknown exception" << std::endl;
183
- }
184
- }
185
-
186
- for (const auto& entry : global_callbacks) {
187
- try {
188
- entry.callback(name, data);
189
- } catch (const std::exception& ex) {
190
- std::cerr << "[PlusUI:event] on_any listener error for '" << name << "': " << ex.what() << std::endl;
191
- } catch (...) {
192
- std::cerr << "[PlusUI:event] on_any listener error for '" << name << "': unknown exception" << std::endl;
193
- }
194
- }
195
- }
196
-
197
- } // namespace event
198
- } // namespace plusui
199
-
200
-