elementdrawing 1.0.0

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/elementdrawing.min.js +3 -0
  3. package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
  4. package/dist/elementdrawing.min.js.map +1 -0
  5. package/dist/index.html +1 -0
  6. package/package.json +127 -0
  7. package/src/core/bridge.h +855 -0
  8. package/src/core/diff.c +900 -0
  9. package/src/core/element.c +1078 -0
  10. package/src/core/event.c +813 -0
  11. package/src/core/fiber.c +1027 -0
  12. package/src/core/hooks.c +919 -0
  13. package/src/core/renderer.c +963 -0
  14. package/src/core/scheduler.c +702 -0
  15. package/src/core/state.c +803 -0
  16. package/src/css/animations.css +779 -0
  17. package/src/css/base.css +615 -0
  18. package/src/css/components.css +1311 -0
  19. package/src/css/tailwind.css +370 -0
  20. package/src/css/themes.css +517 -0
  21. package/src/css/utilities.css +475 -0
  22. package/src/index.js +746 -0
  23. package/src/js/animation.js +655 -0
  24. package/src/js/dom.js +665 -0
  25. package/src/js/events.js +585 -0
  26. package/src/js/http.js +446 -0
  27. package/src/js/index.js +26 -0
  28. package/src/js/router.js +483 -0
  29. package/src/js/store.js +539 -0
  30. package/src/js/utils.js +593 -0
  31. package/src/js/validator.js +529 -0
  32. package/src/jsx/components/Accordion.jsx +210 -0
  33. package/src/jsx/components/Alert.jsx +169 -0
  34. package/src/jsx/components/Avatar.jsx +214 -0
  35. package/src/jsx/components/Badge.jsx +136 -0
  36. package/src/jsx/components/Breadcrumb.jsx +200 -0
  37. package/src/jsx/components/Button.jsx +188 -0
  38. package/src/jsx/components/Card.jsx +192 -0
  39. package/src/jsx/components/Carousel.jsx +278 -0
  40. package/src/jsx/components/Checkbox.jsx +215 -0
  41. package/src/jsx/components/Dialog.jsx +242 -0
  42. package/src/jsx/components/Drawer.jsx +190 -0
  43. package/src/jsx/components/Dropdown.jsx +268 -0
  44. package/src/jsx/components/Form.jsx +274 -0
  45. package/src/jsx/components/Input.jsx +285 -0
  46. package/src/jsx/components/Menu.jsx +276 -0
  47. package/src/jsx/components/Modal.jsx +274 -0
  48. package/src/jsx/components/Navbar.jsx +292 -0
  49. package/src/jsx/components/Pagination.jsx +268 -0
  50. package/src/jsx/components/Progress.jsx +252 -0
  51. package/src/jsx/components/Radio.jsx +208 -0
  52. package/src/jsx/components/Select.jsx +397 -0
  53. package/src/jsx/components/Sidebar.jsx +250 -0
  54. package/src/jsx/components/Slider.jsx +310 -0
  55. package/src/jsx/components/Spinner.jsx +198 -0
  56. package/src/jsx/components/Switch.jsx +201 -0
  57. package/src/jsx/components/Table.jsx +332 -0
  58. package/src/jsx/components/Tabs.jsx +227 -0
  59. package/src/jsx/components/Textarea.jsx +212 -0
  60. package/src/jsx/components/Toast.jsx +270 -0
  61. package/src/jsx/components/Tooltip.jsx +178 -0
  62. package/src/jsx/components/Typography.jsx +299 -0
  63. package/src/jsx/components/index.jsx +70 -0
  64. package/src/jsx/core/element.js +3 -0
  65. package/src/jsx/hooks/index.js +356 -0
  66. package/src/jsx/hooks/useCallback.js +472 -0
  67. package/src/jsx/hooks/useContext.js +586 -0
  68. package/src/jsx/hooks/useEffect.js +704 -0
  69. package/src/jsx/hooks/useLayoutEffect.js +508 -0
  70. package/src/jsx/hooks/useMemo.js +689 -0
  71. package/src/jsx/hooks/useReducer.js +729 -0
  72. package/src/jsx/hooks/useRef.js +542 -0
  73. package/src/jsx/hooks/useState.js +854 -0
  74. package/src/jsx/runtime/commit.js +903 -0
  75. package/src/jsx/runtime/createElement.js +860 -0
  76. package/src/jsx/runtime/index.js +356 -0
  77. package/src/jsx/runtime/reconcile.js +687 -0
  78. package/src/jsx/runtime/render.js +914 -0
@@ -0,0 +1,813 @@
1
+ /*
2
+ * event.c - ElementDrawing Core: Event System
3
+ *
4
+ * Implements the event system for the ElementDrawing engine.
5
+ * Supports event creation/destruction, listener management with
6
+ * capture/bubble phases, event delegation, stop propagation,
7
+ * prevent default, and event dispatching with full phase support.
8
+ */
9
+
10
+ #include "bridge.h"
11
+
12
+ /* ================================================================
13
+ * Internal Event System State
14
+ * ================================================================ */
15
+
16
+ static uint32_t g_listener_id_counter = 0;
17
+ static uint32_t g_event_id_counter = 0;
18
+
19
+ /* ================================================================
20
+ * Event Creation and Destruction
21
+ * ================================================================ */
22
+
23
+ EDEvent* ed_event_create(const char* type, bool bubbles, bool cancelable)
24
+ {
25
+ EDEvent* event = (EDEvent*)calloc(1, sizeof(EDEvent));
26
+ if (!event) return NULL;
27
+
28
+ if (type) {
29
+ strncpy(event->type, type, sizeof(event->type) - 1);
30
+ event->type[sizeof(event->type) - 1] = '\0';
31
+ }
32
+
33
+ event->phase = ED_EVENT_PHASE_NONE;
34
+ event->target = NULL;
35
+ event->current_target = NULL;
36
+ event->bubbles = bubbles;
37
+ event->cancelable = cancelable;
38
+ event->default_prevented = false;
39
+ event->propagation_stopped = false;
40
+ event->immediate_propagation_stopped = false;
41
+ event->timestamp = ed_get_timestamp_ms();
42
+ event->native_event = NULL;
43
+ event->synthetic_parent = NULL;
44
+ event->event_phase = 0;
45
+
46
+ return event;
47
+ }
48
+
49
+ void ed_event_destroy(EDEvent* event)
50
+ {
51
+ if (!event) return;
52
+ free(event);
53
+ }
54
+
55
+ void ed_event_stop_propagation(EDEvent* event)
56
+ {
57
+ if (!event) return;
58
+ event->propagation_stopped = true;
59
+ }
60
+
61
+ void ed_event_prevent_default(EDEvent* event)
62
+ {
63
+ if (!event) return;
64
+ if (event->cancelable) {
65
+ event->default_prevented = true;
66
+ }
67
+ }
68
+
69
+ /* ================================================================
70
+ * Event Listener Creation
71
+ * ================================================================ */
72
+
73
+ static EDEventListener* ed_event_listener_create(const char* event_type,
74
+ EDEventCallback handler,
75
+ void* user_data,
76
+ bool capture)
77
+ {
78
+ EDEventListener* listener = (EDEventListener*)calloc(1, sizeof(EDEventListener));
79
+ if (!listener) return NULL;
80
+
81
+ if (event_type) {
82
+ strncpy(listener->event_type, event_type, sizeof(listener->event_type) - 1);
83
+ listener->event_type[sizeof(listener->event_type) - 1] = '\0';
84
+ }
85
+
86
+ listener->handler = handler;
87
+ listener->user_data = user_data;
88
+ listener->capture = capture;
89
+ listener->passive = false;
90
+ listener->once = false;
91
+ listener->priority = 0;
92
+ listener->next = NULL;
93
+
94
+ return listener;
95
+ }
96
+
97
+ static void ed_event_listener_destroy(EDEventListener* listener)
98
+ {
99
+ if (!listener) return;
100
+ free(listener);
101
+ }
102
+
103
+ /* ================================================================
104
+ * Event System Creation and Destruction
105
+ * ================================================================ */
106
+
107
+ EDEventSystem* ed_event_system_create(void)
108
+ {
109
+ EDEventSystem* system = (EDEventSystem*)calloc(1, sizeof(EDEventSystem));
110
+ if (!system) return NULL;
111
+
112
+ for (uint32_t i = 0; i < ED_EVENT_MAX_LISTENERS; i++) {
113
+ system->listeners[i] = NULL;
114
+ system->listener_counts[i] = 0;
115
+ }
116
+
117
+ system->delegation_root = NULL;
118
+ system->is_dispatching = false;
119
+ system->active_event_count = 0;
120
+ system->total_events_processed = 0;
121
+ system->current_event = NULL;
122
+
123
+ ed_log_debug("Event system created");
124
+ return system;
125
+ }
126
+
127
+ void ed_event_system_destroy(EDEventSystem* system)
128
+ {
129
+ if (!system) return;
130
+
131
+ /* Destroy all listeners */
132
+ for (uint32_t i = 0; i < ED_EVENT_MAX_LISTENERS; i++) {
133
+ EDEventListener* listener = system->listeners[i];
134
+ while (listener) {
135
+ EDEventListener* next = listener->next;
136
+ ed_event_listener_destroy(listener);
137
+ listener = next;
138
+ }
139
+ system->listeners[i] = NULL;
140
+ }
141
+
142
+ free(system);
143
+ ed_log_debug("Event system destroyed");
144
+ }
145
+
146
+ /* ================================================================
147
+ * Event Type Hash (for listener bucketing)
148
+ * ================================================================ */
149
+
150
+ static uint32_t ed_event_type_hash(const char* event_type)
151
+ {
152
+ if (!event_type) return 0;
153
+ return ed_hash_fnv1a(event_type) % ED_EVENT_MAX_LISTENERS;
154
+ }
155
+
156
+ /* ================================================================
157
+ * Listener Registration
158
+ * ================================================================ */
159
+
160
+ uint32_t ed_event_add_listener(EDEventSystem* system, const char* event_type,
161
+ EDEventCallback handler, void* user_data, bool capture)
162
+ {
163
+ if (!system || !event_type || !handler) {
164
+ ed_log_error("Invalid arguments to ed_event_add_listener");
165
+ return 0;
166
+ }
167
+
168
+ uint32_t bucket = ed_event_type_hash(event_type);
169
+
170
+ /* Check max listeners per bucket */
171
+ if (system->listener_counts[bucket] >= ED_EVENT_BUBBLE_DEPTH * 4) {
172
+ ed_log_warn("Listener bucket %u full for event type '%s'",
173
+ bucket, event_type);
174
+ return 0;
175
+ }
176
+
177
+ EDEventListener* listener = ed_event_listener_create(event_type, handler, user_data, capture);
178
+ if (!listener) return 0;
179
+
180
+ uint32_t id = ++g_listener_id_counter;
181
+ listener->priority = id;
182
+
183
+ /* Insert into bucket (capture listeners first, then bubble, sorted by priority) */
184
+ EDEventListener** prev = &system->listeners[bucket];
185
+ EDEventListener* current = system->listeners[bucket];
186
+
187
+ while (current) {
188
+ /* Capture phase listeners come first */
189
+ if (!capture && current->capture) {
190
+ prev = &current->next;
191
+ current = current->next;
192
+ continue;
193
+ }
194
+ if (capture && !current->capture) {
195
+ break; /* Insert before bubble listeners */
196
+ }
197
+ prev = &current->next;
198
+ current = current->next;
199
+ }
200
+
201
+ listener->next = current;
202
+ *prev = listener;
203
+ system->listener_counts[bucket]++;
204
+
205
+ ed_log_debug("Listener added: type='%s', capture=%s, id=%u",
206
+ event_type, capture ? "true" : "false", id);
207
+
208
+ return id;
209
+ }
210
+
211
+ bool ed_event_remove_listener(EDEventSystem* system, uint32_t listener_id)
212
+ {
213
+ if (!system || listener_id == 0) return false;
214
+
215
+ /* Search all buckets for the listener */
216
+ for (uint32_t i = 0; i < ED_EVENT_MAX_LISTENERS; i++) {
217
+ EDEventListener** prev = &system->listeners[i];
218
+ EDEventListener* current = system->listeners[i];
219
+
220
+ while (current) {
221
+ if (current->priority == listener_id) {
222
+ *prev = current->next;
223
+ current->next = NULL;
224
+ ed_event_listener_destroy(current);
225
+ system->listener_counts[i]--;
226
+ ed_log_debug("Listener removed: id=%u", listener_id);
227
+ return true;
228
+ }
229
+ prev = &current->next;
230
+ current = current->next;
231
+ }
232
+ }
233
+
234
+ ed_log_warn("Listener not found: id=%u", listener_id);
235
+ return false;
236
+ }
237
+
238
+ /* ================================================================
239
+ * Event Path Computation
240
+ *
241
+ * Computes the propagation path from the target to the root
242
+ * for capture phase, and root to target for bubble phase.
243
+ * ================================================================ */
244
+
245
+ typedef struct EDEventPathEntry {
246
+ void* node;
247
+ EDEventListener* capture_listeners;
248
+ EDEventListener* bubble_listeners;
249
+ } EDEventPathEntry;
250
+
251
+ typedef struct EDEventPath {
252
+ EDEventPathEntry entries[ED_EVENT_BUBBLE_DEPTH];
253
+ uint32_t count;
254
+ } EDEventPath;
255
+
256
+ static void ed_event_path_init(EDEventPath* path)
257
+ {
258
+ if (!path) return;
259
+ memset(path, 0, sizeof(EDEventPath));
260
+ }
261
+
262
+ /* In a real implementation, this would traverse the DOM/fiber tree
263
+ * from the target to the root, collecting listeners at each level.
264
+ * Here we provide a simplified version. */
265
+
266
+ static void ed_event_path_build(EDEventSystem* system, EDEventPath* path,
267
+ void* target)
268
+ {
269
+ if (!system || !path || !target) return;
270
+
271
+ /* Simplified: just add the target as a single entry */
272
+ if (path->count < ED_EVENT_BUBBLE_DEPTH) {
273
+ path->entries[path->count].node = target;
274
+ path->entries[path->count].capture_listeners = NULL;
275
+ path->entries[path->count].bubble_listeners = NULL;
276
+ path->count++;
277
+ }
278
+
279
+ /* In a real implementation, walk up the tree from target to root,
280
+ * collecting capture and bubble listeners at each level */
281
+ }
282
+
283
+ /* ================================================================
284
+ * Event Dispatching
285
+ * ================================================================ */
286
+
287
+ static void ed_event_invoke_listeners(EDEventSystem* system, EDEvent* event,
288
+ EDEventListener* listener, EDEventPhase phase)
289
+ {
290
+ if (!system || !event || !listener) return;
291
+
292
+ while (listener) {
293
+ /* Check if propagation stopped */
294
+ if (event->immediate_propagation_stopped) return;
295
+ if (event->propagation_stopped) return;
296
+
297
+ /* Check event type match */
298
+ if (strcmp(listener->event_type, event->type) != 0) {
299
+ listener = listener->next;
300
+ continue;
301
+ }
302
+
303
+ /* Check phase match */
304
+ bool phase_match = false;
305
+ if (phase == ED_EVENT_PHASE_CAPTURE && listener->capture) {
306
+ phase_match = true;
307
+ } else if (phase == ED_EVENT_PHASE_BUBBLE && !listener->capture) {
308
+ phase_match = true;
309
+ } else if (phase == ED_EVENT_PHASE_AT_TARGET) {
310
+ phase_match = true;
311
+ }
312
+
313
+ if (!phase_match) {
314
+ listener = listener->next;
315
+ continue;
316
+ }
317
+
318
+ /* Set current phase and target */
319
+ event->phase = phase;
320
+ event->event_phase = (int32_t)phase;
321
+
322
+ /* Invoke the handler */
323
+ if (listener->handler) {
324
+ listener->handler(event, listener->user_data);
325
+ }
326
+
327
+ /* Handle once listener */
328
+ if (listener->once) {
329
+ ed_event_remove_listener(system, listener->priority);
330
+ }
331
+
332
+ listener = listener->next;
333
+ }
334
+ }
335
+
336
+ void ed_event_dispatch(EDEventSystem* system, EDEvent* event)
337
+ {
338
+ if (!system || !event) return;
339
+
340
+ if (system->is_dispatching) {
341
+ ed_log_warn("Recursive event dispatch detected for '%s'", event->type);
342
+ return;
343
+ }
344
+
345
+ system->is_dispatching = true;
346
+ system->current_event = event;
347
+ system->active_event_count++;
348
+
349
+ ed_log_debug("Dispatching event '%s' (bubbles=%s, cancelable=%s)",
350
+ event->type,
351
+ event->bubbles ? "true" : "false",
352
+ event->cancelable ? "true" : "false");
353
+
354
+ /* Build event path */
355
+ EDEventPath path;
356
+ ed_event_path_init(&path);
357
+ ed_event_path_build(system, &path, event->target);
358
+
359
+ /* Phase 1: Capture phase (root -> target) */
360
+ event->phase = ED_EVENT_PHASE_CAPTURE;
361
+ event->event_phase = ED_EVENT_PHASE_CAPTURE;
362
+
363
+ for (uint32_t i = 0; i < path.count && !event->propagation_stopped; i++) {
364
+ if (i == path.count - 1) break; /* Target handled separately */
365
+
366
+ event->current_target = path.entries[i].node;
367
+ uint32_t bucket = ed_event_type_hash(event->type);
368
+
369
+ /* Invoke capture listeners */
370
+ EDEventListener* listener = system->listeners[bucket];
371
+ while (listener && !event->immediate_propagation_stopped) {
372
+ if (listener->capture && strcmp(listener->event_type, event->type) == 0) {
373
+ if (listener->handler) {
374
+ listener->handler(event, listener->user_data);
375
+ }
376
+ }
377
+ listener = listener->next;
378
+ }
379
+ }
380
+
381
+ /* Phase 2: At target */
382
+ if (!event->propagation_stopped && path.count > 0) {
383
+ event->phase = ED_EVENT_PHASE_AT_TARGET;
384
+ event->event_phase = ED_EVENT_PHASE_AT_TARGET;
385
+ event->current_target = event->target;
386
+
387
+ uint32_t bucket = ed_event_type_hash(event->type);
388
+ EDEventListener* listener = system->listeners[bucket];
389
+
390
+ while (listener && !event->immediate_propagation_stopped) {
391
+ if (strcmp(listener->event_type, event->type) == 0) {
392
+ if (listener->handler) {
393
+ listener->handler(event, listener->user_data);
394
+ }
395
+ }
396
+ listener = listener->next;
397
+ }
398
+ }
399
+
400
+ /* Phase 3: Bubble phase (target -> root) */
401
+ if (event->bubbles && !event->propagation_stopped) {
402
+ event->phase = ED_EVENT_PHASE_BUBBLE;
403
+ event->event_phase = ED_EVENT_PHASE_BUBBLE;
404
+
405
+ for (int i = (int)path.count - 2; i >= 0 && !event->propagation_stopped; i--) {
406
+ event->current_target = path.entries[i].node;
407
+
408
+ uint32_t bucket = ed_event_type_hash(event->type);
409
+ EDEventListener* listener = system->listeners[bucket];
410
+
411
+ while (listener && !event->immediate_propagation_stopped) {
412
+ if (!listener->capture && strcmp(listener->event_type, event->type) == 0) {
413
+ if (listener->handler) {
414
+ listener->handler(event, listener->user_data);
415
+ }
416
+ }
417
+ listener = listener->next;
418
+ }
419
+ }
420
+ }
421
+
422
+ /* Reset event phase */
423
+ event->phase = ED_EVENT_PHASE_NONE;
424
+ event->event_phase = 0;
425
+ event->current_target = NULL;
426
+
427
+ system->is_dispatching = false;
428
+ system->current_event = NULL;
429
+ system->active_event_count--;
430
+ system->total_events_processed++;
431
+
432
+ ed_log_debug("Event '%s' dispatch complete (default_prevented=%s)",
433
+ event->type, event->default_prevented ? "true" : "false");
434
+ }
435
+
436
+ /* ================================================================
437
+ * Event Delegation
438
+ *
439
+ * Implements event delegation at a root node. Instead of attaching
440
+ * listeners to each individual element, a single listener is
441
+ * attached at the root and events are dispatched based on the
442
+ * target element.
443
+ * ================================================================ */
444
+
445
+ typedef struct EDDelegationEntry {
446
+ char event_type[64];
447
+ bool is_registered;
448
+ EDEventCallback root_handler;
449
+ void* root_data;
450
+ } EDDelegationEntry;
451
+
452
+ #define ED_MAX_DELEGATED_TYPES 64
453
+
454
+ static EDDelegationEntry g_delegated_events[ED_MAX_DELEGATED_TYPES] = {0};
455
+ static uint32_t g_delegated_count = 0;
456
+
457
+ static bool ed_event_is_delegated(const char* event_type)
458
+ {
459
+ if (!event_type) return false;
460
+
461
+ for (uint32_t i = 0; i < g_delegated_count; i++) {
462
+ if (g_delegated_events[i].is_registered &&
463
+ strcmp(g_delegated_events[i].event_type, event_type) == 0) {
464
+ return true;
465
+ }
466
+ }
467
+ return false;
468
+ }
469
+
470
+ static void ed_event_setup_delegation(EDEventSystem* system, const char* event_type,
471
+ EDEventCallback root_handler, void* root_data)
472
+ {
473
+ if (!system || !event_type || g_delegated_count >= ED_MAX_DELEGATED_TYPES) return;
474
+
475
+ /* Check if already delegated */
476
+ if (ed_event_is_delegated(event_type)) return;
477
+
478
+ EDDelegationEntry* entry = &g_delegated_events[g_delegated_count++];
479
+ strncpy(entry->event_type, event_type, sizeof(entry->event_type) - 1);
480
+ entry->event_type[sizeof(entry->event_type) - 1] = '\0';
481
+ entry->is_registered = true;
482
+ entry->root_handler = root_handler;
483
+ entry->root_data = root_data;
484
+
485
+ /* Register capture listener at root for delegation */
486
+ ed_event_add_listener(system, event_type, root_handler, root_data, true);
487
+
488
+ ed_log_debug("Event delegation set up for '%s'", event_type);
489
+ }
490
+
491
+ static void ed_event_teardown_delegation(EDEventSystem* system, const char* event_type)
492
+ {
493
+ if (!system || !event_type) return;
494
+
495
+ for (uint32_t i = 0; i < g_delegated_count; i++) {
496
+ if (g_delegated_events[i].is_registered &&
497
+ strcmp(g_delegated_events[i].event_type, event_type) == 0) {
498
+ g_delegated_events[i].is_registered = false;
499
+ ed_log_debug("Event delegation torn down for '%s'", event_type);
500
+ return;
501
+ }
502
+ }
503
+ }
504
+
505
+ /* ================================================================
506
+ * Synthetic Event Pooling
507
+ *
508
+ * Reuse event objects to reduce garbage collection pressure.
509
+ * ================================================================ */
510
+
511
+ #define ED_EVENT_POOL_SIZE 64
512
+
513
+ typedef struct EDEventPool {
514
+ EDEvent* pool[ED_EVENT_POOL_SIZE];
515
+ uint32_t available;
516
+ uint32_t total_created;
517
+ uint32_t total_reused;
518
+ } EDEventPool;
519
+
520
+ static EDEventPool g_event_pool = {0};
521
+
522
+ static EDEvent* ed_event_pool_acquire(const char* type, bool bubbles, bool cancelable)
523
+ {
524
+ if (g_event_pool.available > 0) {
525
+ EDEvent* event = g_event_pool.pool[g_event_pool.available - 1];
526
+ g_event_pool.available--;
527
+ g_event_pool.total_reused++;
528
+
529
+ /* Reset event */
530
+ if (type) {
531
+ strncpy(event->type, type, sizeof(event->type) - 1);
532
+ event->type[sizeof(event->type) - 1] = '\0';
533
+ }
534
+ event->phase = ED_EVENT_PHASE_NONE;
535
+ event->target = NULL;
536
+ event->current_target = NULL;
537
+ event->bubbles = bubbles;
538
+ event->cancelable = cancelable;
539
+ event->default_prevented = false;
540
+ event->propagation_stopped = false;
541
+ event->immediate_propagation_stopped = false;
542
+ event->timestamp = ed_get_timestamp_ms();
543
+ event->native_event = NULL;
544
+ event->synthetic_parent = NULL;
545
+ event->event_phase = 0;
546
+
547
+ return event;
548
+ }
549
+
550
+ /* Create new event */
551
+ EDEvent* event = ed_event_create(type, bubbles, cancelable);
552
+ if (event) {
553
+ g_event_pool.total_created++;
554
+ }
555
+ return event;
556
+ }
557
+
558
+ static void ed_event_pool_release(EDEvent* event)
559
+ {
560
+ if (!event) return;
561
+
562
+ if (g_event_pool.available < ED_EVENT_POOL_SIZE) {
563
+ /* Reset for reuse */
564
+ event->phase = ED_EVENT_PHASE_NONE;
565
+ event->target = NULL;
566
+ event->current_target = NULL;
567
+ event->default_prevented = false;
568
+ event->propagation_stopped = false;
569
+ event->immediate_propagation_stopped = false;
570
+ event->native_event = NULL;
571
+ event->synthetic_parent = NULL;
572
+ event->event_phase = 0;
573
+
574
+ g_event_pool.pool[g_event_pool.available++] = event;
575
+ } else {
576
+ ed_event_destroy(event);
577
+ }
578
+ }
579
+
580
+ static void ed_event_pool_cleanup(void)
581
+ {
582
+ for (uint32_t i = 0; i < g_event_pool.available; i++) {
583
+ ed_event_destroy(g_event_pool.pool[i]);
584
+ g_event_pool.pool[i] = NULL;
585
+ }
586
+ g_event_pool.available = 0;
587
+ }
588
+
589
+ /* ================================================================
590
+ * Passive Event Support
591
+ *
592
+ * Passive event listeners cannot call preventDefault().
593
+ * This is important for scroll/touch events where blocking
594
+ * the main thread for preventDefault checks causes jank.
595
+ * ================================================================ */
596
+
597
+ static void ed_event_dispatch_passive(EDEventSystem* system, EDEvent* event)
598
+ {
599
+ if (!system || !event) return;
600
+
601
+ uint32_t bucket = ed_event_type_hash(event->type);
602
+ EDEventListener* listener = system->listeners[bucket];
603
+
604
+ while (listener) {
605
+ if (strcmp(listener->event_type, event->type) == 0 && listener->passive) {
606
+ /* For passive listeners, preventDefault is a no-op */
607
+ bool was_cancelable = event->cancelable;
608
+ event->cancelable = false;
609
+
610
+ if (listener->handler) {
611
+ listener->handler(event, listener->user_data);
612
+ }
613
+
614
+ event->cancelable = was_cancelable;
615
+ }
616
+ listener = listener->next;
617
+ }
618
+ }
619
+
620
+ /* ================================================================
621
+ * Event Priority Support
622
+ *
623
+ * Events can have different priorities. Discrete events (click, key)
624
+ * are dispatched immediately. Continuous events (mousemove, scroll)
625
+ * may be batched.
626
+ * ================================================================ */
627
+
628
+ typedef enum {
629
+ ED_EVENT_PRIORITY_DISCRETE = 0,
630
+ ED_EVENT_PRIORITY_CONTINUOUS = 1,
631
+ ED_EVENT_PRIORITY_DEFAULT = 2
632
+ } EDEventPriority;
633
+
634
+ static EDEventPriority ed_event_get_priority(const char* event_type)
635
+ {
636
+ if (!event_type) return ED_EVENT_PRIORITY_DEFAULT;
637
+
638
+ /* Discrete events: immediate dispatch */
639
+ if (strcmp(event_type, "click") == 0 ||
640
+ strcmp(event_type, "keydown") == 0 ||
641
+ strcmp(event_type, "keyup") == 0 ||
642
+ strcmp(event_type, "keypress") == 0 ||
643
+ strcmp(event_type, "focus") == 0 ||
644
+ strcmp(event_type, "blur") == 0 ||
645
+ strcmp(event_type, "submit") == 0 ||
646
+ strcmp(event_type, "change") == 0 ||
647
+ strcmp(event_type, "input") == 0) {
648
+ return ED_EVENT_PRIORITY_DISCRETE;
649
+ }
650
+
651
+ /* Continuous events: may be batched */
652
+ if (strcmp(event_type, "mousemove") == 0 ||
653
+ strcmp(event_type, "touchmove") == 0 ||
654
+ strcmp(event_type, "scroll") == 0 ||
655
+ strcmp(event_type, "wheel") == 0 ||
656
+ strcmp(event_type, "drag") == 0) {
657
+ return ED_EVENT_PRIORITY_CONTINUOUS;
658
+ }
659
+
660
+ return ED_EVENT_PRIORITY_DEFAULT;
661
+ }
662
+
663
+ /* ================================================================
664
+ * Event Queue for Batched Dispatch
665
+ * ================================================================ */
666
+
667
+ typedef struct EDPendingEvent {
668
+ EDEvent* event;
669
+ EDEventPriority priority;
670
+ uint64_t timestamp;
671
+ struct EDPendingEvent* next;
672
+ } EDPendingEvent;
673
+
674
+ typedef struct EDEventQueue {
675
+ EDPendingEvent* head;
676
+ EDPendingEvent* tail;
677
+ uint32_t count;
678
+ } EDEventQueue;
679
+
680
+ static EDEventQueue g_event_queue = {0};
681
+
682
+ static void ed_event_queue_push(EDEvent* event, EDEventPriority priority)
683
+ {
684
+ if (!event) return;
685
+
686
+ EDPendingEvent* pending = (EDPendingEvent*)calloc(1, sizeof(EDPendingEvent));
687
+ if (!pending) {
688
+ /* Fallback: dispatch immediately */
689
+ return;
690
+ }
691
+
692
+ pending->event = event;
693
+ pending->priority = priority;
694
+ pending->timestamp = ed_get_timestamp_ms();
695
+ pending->next = NULL;
696
+
697
+ if (!g_event_queue.tail) {
698
+ g_event_queue.head = pending;
699
+ g_event_queue.tail = pending;
700
+ } else {
701
+ /* Insert by priority */
702
+ EDPendingEvent** prev = &g_event_queue.head;
703
+ EDPendingEvent* current = g_event_queue.head;
704
+
705
+ while (current && current->priority <= priority) {
706
+ prev = &current->next;
707
+ current = current->next;
708
+ }
709
+
710
+ pending->next = current;
711
+ *prev = pending;
712
+
713
+ if (!pending->next) {
714
+ g_event_queue.tail = pending;
715
+ }
716
+ }
717
+ g_event_queue.count++;
718
+ }
719
+
720
+ static EDPendingEvent* ed_event_queue_pop(void)
721
+ {
722
+ if (!g_event_queue.head) return NULL;
723
+
724
+ EDPendingEvent* pending = g_event_queue.head;
725
+ g_event_queue.head = pending->next;
726
+ if (!g_event_queue.head) {
727
+ g_event_queue.tail = NULL;
728
+ }
729
+ g_event_queue.count--;
730
+
731
+ return pending;
732
+ }
733
+
734
+ static void ed_event_queue_flush(EDEventSystem* system)
735
+ {
736
+ if (!system) return;
737
+
738
+ uint32_t dispatched = 0;
739
+ while (g_event_queue.count > 0) {
740
+ EDPendingEvent* pending = ed_event_queue_pop();
741
+ if (pending) {
742
+ ed_event_dispatch(system, pending->event);
743
+ ed_event_pool_release(pending->event);
744
+ free(pending);
745
+ dispatched++;
746
+ }
747
+ }
748
+
749
+ if (dispatched > 0) {
750
+ ed_log_debug("Event queue flushed: %u events dispatched", dispatched);
751
+ }
752
+ }
753
+
754
+ /* ================================================================
755
+ * Custom Event Dispatching with Priority
756
+ * ================================================================ */
757
+
758
+ static void ed_event_dispatch_with_priority(EDEventSystem* system, EDEvent* event)
759
+ {
760
+ if (!system || !event) return;
761
+
762
+ EDEventPriority priority = ed_event_get_priority(event->type);
763
+
764
+ switch (priority) {
765
+ case ED_EVENT_PRIORITY_DISCRETE:
766
+ /* Dispatch immediately */
767
+ ed_event_dispatch(system, event);
768
+ break;
769
+
770
+ case ED_EVENT_PRIORITY_CONTINUOUS:
771
+ /* Queue for batched dispatch */
772
+ ed_event_queue_push(event, priority);
773
+ break;
774
+
775
+ case ED_EVENT_PRIORITY_DEFAULT:
776
+ default:
777
+ ed_event_dispatch(system, event);
778
+ break;
779
+ }
780
+ }
781
+
782
+ /* ================================================================
783
+ * Event Statistics
784
+ * ================================================================ */
785
+
786
+ typedef struct EDEventStats {
787
+ uint64_t total_processed;
788
+ uint32_t active_listeners;
789
+ uint32_t pool_available;
790
+ uint32_t pool_created;
791
+ uint32_t pool_reused;
792
+ uint32_t queued_events;
793
+ } EDEventStats;
794
+
795
+ static EDEventStats ed_event_system_get_stats(EDEventSystem* system)
796
+ {
797
+ EDEventStats stats = {0};
798
+
799
+ if (system) {
800
+ stats.total_processed = system->total_events_processed;
801
+
802
+ for (uint32_t i = 0; i < ED_EVENT_MAX_LISTENERS; i++) {
803
+ stats.active_listeners += system->listener_counts[i];
804
+ }
805
+ }
806
+
807
+ stats.pool_available = g_event_pool.available;
808
+ stats.pool_created = g_event_pool.total_created;
809
+ stats.pool_reused = g_event_pool.total_reused;
810
+ stats.queued_events = g_event_queue.count;
811
+
812
+ return stats;
813
+ }