nomen-lang 0.7.1 → 0.8.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 (50) hide show
  1. package/core/System/Buffer.nm +32 -0
  2. package/core/System/ClassBuffer.nm +24 -0
  3. package/core/System/Controls/Alert.nm +326 -0
  4. package/core/System/Controls/App.nm +147 -0
  5. package/core/System/Controls/Button.nm +119 -1
  6. package/core/System/Controls/CheckBox.nm +24 -0
  7. package/core/System/Controls/ColorPicker.nm +307 -0
  8. package/core/System/Controls/ComboBox.nm +430 -0
  9. package/core/System/Controls/Container.nm +837 -9
  10. package/core/System/Controls/Control.nm +443 -0
  11. package/core/System/Controls/DatePicker.nm +288 -0
  12. package/core/System/Controls/Form.nm +70 -0
  13. package/core/System/Controls/GroupBox.nm +267 -0
  14. package/core/System/Controls/ImageView.nm +311 -0
  15. package/core/System/Controls/LevelIndicator.nm +305 -0
  16. package/core/System/Controls/ListView.nm +633 -0
  17. package/core/System/Controls/Menu.nm +428 -0
  18. package/core/System/Controls/NumberInput.nm +56 -0
  19. package/core/System/Controls/OtpInput.nm +50 -0
  20. package/core/System/Controls/OutlineView.nm +598 -0
  21. package/core/System/Controls/Pagination.nm +142 -0
  22. package/core/System/Controls/Popover.nm +296 -0
  23. package/core/System/Controls/Progress.nm +348 -0
  24. package/core/System/Controls/Radio.nm +292 -0
  25. package/core/System/Controls/Scrollable.nm +584 -0
  26. package/core/System/Controls/SearchField.nm +326 -0
  27. package/core/System/Controls/SecureField.nm +344 -0
  28. package/core/System/Controls/Segmented.nm +380 -0
  29. package/core/System/Controls/Select.nm +340 -0
  30. package/core/System/Controls/Separator.nm +198 -0
  31. package/core/System/Controls/Sheet.nm +329 -0
  32. package/core/System/Controls/Slider.nm +315 -0
  33. package/core/System/Controls/Spinner.nm +287 -0
  34. package/core/System/Controls/SplitView.nm +509 -0
  35. package/core/System/Controls/Stepper.nm +349 -0
  36. package/core/System/Controls/Switch.nm +259 -0
  37. package/core/System/Controls/Table.nm +833 -0
  38. package/core/System/Controls/Tabs.nm +500 -0
  39. package/core/System/Controls/Text.nm +108 -11
  40. package/core/System/Controls/TextBox.nm +112 -1
  41. package/core/System/Controls/TextView.nm +329 -0
  42. package/core/System/Controls/Toggle.nm +346 -0
  43. package/core/System/Controls/Toolbar.nm +501 -0
  44. package/core/System/Controls/Tooltip.nm +76 -0
  45. package/core/System/Controls/Window.nm +1549 -91
  46. package/core/System/List.nm +34 -0
  47. package/core/System/Map.nm +21 -4
  48. package/core/System/Set.nm +14 -0
  49. package/dist/index.mjs +2556 -656
  50. package/package.json +1 -1
@@ -8,7 +8,7 @@
8
8
  * pointer form); the constructor's `self` is the inline instance (the `.`
9
9
  * accessor).
10
10
  **/
11
- pub mv struct Window : Control {
11
+ pub mv struct Window: Control {
12
12
  var uint64 handle = 0
13
13
 
14
14
  pub func #init = (self, string title, int width, int height) {
@@ -160,18 +160,290 @@ pub mv struct Window : Control {
160
160
  }
161
161
 
162
162
  // Process pending events without blocking. Returns 1 if a left-mouse-down
163
- // was among the events (i.e. a click happened), 0 otherwise. This lets a
164
- // manual event loop detect clicks for re-layout without target/action.
165
- // Also runs the run loop briefly to process Apple Events (e.g. dock Quit).
163
+ // was among the events (i.e. a click happened), 2 if a right-mouse-down
164
+ // was, 0 otherwise. This lets a manual event loop detect clicks (and
165
+ // right-clicks) for hit-testing without target/action. The click's
166
+ // coordinates are captured here (see click_x/click_y), and the click's
167
+ // button kind is stamped for the drains (nomen_last_click_kind —
168
+ // Container.take_right_click reads it). Also runs the run
169
+ // loop briefly to process Apple Events (e.g. dock Quit).
166
170
  pub func process_events = (out int) {
167
171
  ```
168
172
  #arch: c
169
173
  #platform: macos
174
+ #scope: file
175
+ // The most recent mouse-down (NSLeftMouseDown = 1 or NSRightMouseDown = 3)
176
+ // among the events the latest process_events call pumped, in the event
177
+ // window's content coordinates (top-left origin). Written by the pump;
178
+ // read by click_x/click_y. click_x/click_y deliberately do NOT read the
179
+ // live currentEvent: any event after the click in the same batch (a
180
+ // mouse-move, drag, or key) would make the coordinates stale or missing,
181
+ // so hit-tests would intermittently miss.
182
+ static int nomen_last_click_x = -1;
183
+ static int nomen_last_click_y = -1;
184
+ // The same record's button kind (0 none / 1 left / 2 right), stamped
185
+ // by every pump — a quiet pump stamps 0, so the edge expires with
186
+ // the frame. External linkage (NOT static, unlike the coordinates):
187
+ // Container's take_right_click drain externs it from another TU —
188
+ // the focus snapshot's sharing pattern (nomen_window_focus_poll).
189
+ int nomen_last_click_kind = 0;
190
+ // The full mouse-down record for THIS pump (the exhaustive input
191
+ // surface — every button, not just left/right): 1-based button
192
+ // number (0 = no mouse-down this frame), AppKit clickCount, and the
193
+ // device-independent modifier flags (Command = 0x100000, Option =
194
+ // 0x80000, Control = 0x40000, Shift = 0x20000). Zeroed by every
195
+ // pump, so the edge expires with the frame. External linkage:
196
+ // Container.take_mouse_down externs the button from another TU.
197
+ int nomen_last_click_button = 0;
198
+ int nomen_last_click_count = 0;
199
+ int nomen_last_click_modifiers = 0;
200
+ // Scroll-wheel deltas accumulated across this pump's events
201
+ // (trackpads emit many small events per frame; momentum counts).
202
+ // Zeroed by every pump. External linkage: Scrollable.take_scroll
203
+ // externs them from another TU.
204
+ double nomen_scroll_dx_acc = 0.0;
205
+ double nomen_scroll_dy_acc = 0.0;
206
+ // The keyboard record for THIS pump: the hardware keycode of the
207
+ // last key-down (NSEventTypeKeyDown = 10) and key-up (type 11),
208
+ // -1 = none this frame; the modifiers at key-down; and whether the
209
+ // key-down was an auto-repeat (a held key). Zeroed by every pump.
210
+ // External linkage: Container.take_key_down / take_key_up extern
211
+ // them from another TU. Text entry does NOT flow here (the field
212
+ // editor feeds TextBox.on_input) — this record is for shortcuts and
213
+ // raw key handling.
214
+ int nomen_last_key_code = -1;
215
+ int nomen_last_key_modifiers = 0;
216
+ int nomen_last_key_repeat = 0;
217
+ int nomen_last_key_up_code = -1;
218
+ // The pointer-MOVE record for THIS pump (NSEventTypeMouseMoved = 5,
219
+ // plus the dragged variants 6/7/26 while a button is held): the last
220
+ // seen position, -1 = no move this frame. Zeroed by every pump.
221
+ // External linkage: Container.take_mouse_move externs it. Requires
222
+ // setAcceptsMouseMovedEvents (armed by every pump below) — without
223
+ // it AppKit delivers no move events at all.
224
+ int nomen_last_mouse_move_x = -1;
225
+ int nomen_last_mouse_move_y = -1;
226
+ // Frame time: the pump's wall-clock interval, persisted across
227
+ // pumps (NOT zeroed — the first frame reports 0). Read by win.dt().
228
+ double nomen_last_pump_time = 0.0;
229
+ double nomen_last_dt_ms = 0.0;
230
+ // The window-resize record for THIS frame, stamped by Window.run's
231
+ // resize detection (not by the pump). External linkage:
232
+ // Container.take_resize externs the edge from another TU; the size
233
+ // getters (resize_width/resize_height) are Window methods reading
234
+ // these directly.
235
+ int nomen_last_resized = 0;
236
+ int nomen_last_resize_w = 0;
237
+ int nomen_last_resize_h = 0;
238
+ ```
239
+ ```
240
+ #arch: aarch64_use_c
241
+ #platform: macos
242
+ #scope: file
243
+ // The most recent mouse-down (NSLeftMouseDown = 1 or NSRightMouseDown = 3)
244
+ // among the events the latest process_events call pumped, in the event
245
+ // window's content coordinates (top-left origin). Written by the pump;
246
+ // read by click_x/click_y. click_x/click_y deliberately do NOT read the
247
+ // live currentEvent: any event after the click in the same batch (a
248
+ // mouse-move, drag, or key) would make the coordinates stale or missing,
249
+ // so hit-tests would intermittently miss.
250
+ static int nomen_last_click_x = -1;
251
+ static int nomen_last_click_y = -1;
252
+ // The same record's button kind (0 none / 1 left / 2 right), stamped
253
+ // by every pump — a quiet pump stamps 0, so the edge expires with
254
+ // the frame. External linkage (NOT static, unlike the coordinates):
255
+ // Container's take_right_click drain externs it from another TU —
256
+ // the focus snapshot's sharing pattern (nomen_window_focus_poll).
257
+ int nomen_last_click_kind = 0;
258
+ // The full mouse-down record for THIS pump (the exhaustive input
259
+ // surface — every button, not just left/right): 1-based button
260
+ // number (0 = no mouse-down this frame), AppKit clickCount, and the
261
+ // device-independent modifier flags (Command = 0x100000, Option =
262
+ // 0x80000, Control = 0x40000, Shift = 0x20000). Zeroed by every
263
+ // pump, so the edge expires with the frame. External linkage:
264
+ // Container.take_mouse_down externs the button from another TU.
265
+ int nomen_last_click_button = 0;
266
+ int nomen_last_click_count = 0;
267
+ int nomen_last_click_modifiers = 0;
268
+ // Scroll-wheel deltas accumulated across this pump's events
269
+ // (trackpads emit many small events per frame; momentum counts).
270
+ // Zeroed by every pump. External linkage: Scrollable.take_scroll
271
+ // externs them from another TU.
272
+ double nomen_scroll_dx_acc = 0.0;
273
+ double nomen_scroll_dy_acc = 0.0;
274
+ // The keyboard record for THIS pump: the hardware keycode of the
275
+ // last key-down (NSEventTypeKeyDown = 10) and key-up (type 11),
276
+ // -1 = none this frame; the modifiers at key-down; and whether the
277
+ // key-down was an auto-repeat (a held key). Zeroed by every pump.
278
+ // External linkage: Container.take_key_down / take_key_up extern
279
+ // them from another TU. Text entry does NOT flow here (the field
280
+ // editor feeds TextBox.on_input) — this record is for shortcuts and
281
+ // raw key handling.
282
+ int nomen_last_key_code = -1;
283
+ int nomen_last_key_modifiers = 0;
284
+ int nomen_last_key_repeat = 0;
285
+ int nomen_last_key_up_code = -1;
286
+ // The pointer-MOVE record for THIS pump (NSEventTypeMouseMoved = 5,
287
+ // plus the dragged variants 6/7/26 while a button is held): the last
288
+ // seen position, -1 = no move this frame. Zeroed by every pump.
289
+ // External linkage: Container.take_mouse_move externs it. Requires
290
+ // setAcceptsMouseMovedEvents (armed by every pump below) — without
291
+ // it AppKit delivers no move events at all.
292
+ int nomen_last_mouse_move_x = -1;
293
+ int nomen_last_mouse_move_y = -1;
294
+ // Frame time: the pump's wall-clock interval, persisted across
295
+ // pumps (NOT zeroed — the first frame reports 0). Read by win.dt().
296
+ double nomen_last_pump_time = 0.0;
297
+ double nomen_last_dt_ms = 0.0;
298
+ // The window-resize record for THIS frame, stamped by Window.run's
299
+ // resize detection (not by the pump). External linkage:
300
+ // Container.take_resize externs the edge from another TU; the size
301
+ // getters (resize_width/resize_height) are Window methods reading
302
+ // these directly.
303
+ int nomen_last_resized = 0;
304
+ int nomen_last_resize_w = 0;
305
+ int nomen_last_resize_h = 0;
306
+ ```
307
+ ```
308
+ #arch: c
309
+ #platform: macos
310
+ // A manual event loop has no autorelease pool of its own: AppKit
311
+ // autoreleases objects on every pump (events, strings, cell caches)
312
+ // and without a pool they accumulate for the process lifetime — the
313
+ // loop leaked tens of MB per second and a busy window eventually
314
+ // died. One pool per pump; the returned had_click is a plain int.
315
+ id pool = ((id(*)(id, SEL))objc_msgSend)(
316
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")),
317
+ sel_registerName("init"));
318
+ id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
319
+ id distant_past = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSDate"), sel_registerName("distantPast"));
320
+ id default_mode = ((id(*)(id, SEL, const char*))objc_msgSend)(
321
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode");
322
+ int had_click = 0;
323
+ // This pump's mouse record: zeroed first, so a quiet pump expires
324
+ // the previous frame's edge — the getters and drains read these,
325
+ // and 0 means "no mouse-down / no scroll this frame".
326
+ nomen_last_click_button = 0;
327
+ nomen_last_click_count = 0;
328
+ nomen_last_click_modifiers = 0;
329
+ nomen_scroll_dx_acc = 0.0;
330
+ nomen_scroll_dy_acc = 0.0;
331
+ nomen_last_key_code = -1;
332
+ nomen_last_key_modifiers = 0;
333
+ nomen_last_key_repeat = 0;
334
+ nomen_last_key_up_code = -1;
335
+ nomen_last_mouse_move_x = -1;
336
+ nomen_last_mouse_move_y = -1;
337
+ while (1) {
338
+ id event = ((id(*)(id, SEL, unsigned long long, id, id, BOOL))objc_msgSend)(
339
+ app, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
340
+ 0xFFFFFFFFFFFFFFFFULL, // NSEventMaskAny
341
+ distant_past, // untilDate — returns immediately if no events
342
+ default_mode, // inMode
343
+ 1); // dequeue
344
+ if (!event) break;
345
+ ((void(*)(id, SEL, id))objc_msgSend)(app, sel_registerName("sendEvent:"), event);
346
+ long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
347
+ // A mouse-down (left = 1, right = 3, other/middle/extra = 25):
348
+ // record where it landed and which button. locationInWindow is
349
+ // in the event's own window's coordinates (bottom-left origin);
350
+ // flip y against that window's content height to match the
351
+ // layout's top-left origin. buttonNumber is 0-based (0 left, 1
352
+ // right, 2 middle, 3+ extras) — stored 1-based to match
353
+ // FrameEvents.click's 1 = left / 2 = right contract. clickCount
354
+ // gives double/triple clicks; modifierFlags are masked to the
355
+ // device-independent bits.
356
+ if (type == 1 || type == 3 || type == 25) {
357
+ id evwin = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("window"));
358
+ id cv = ((id(*)(id, SEL))objc_msgSend)(evwin, sel_registerName("contentView"));
359
+ CGRect frame = ((CGRect(*)(id, SEL))objc_msgSend)(cv, sel_registerName("frame"));
360
+ CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
361
+ nomen_last_click_x = (int)loc.x;
362
+ nomen_last_click_y = (int)(frame.size.height - loc.y);
363
+ nomen_last_click_button = (int)(((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"))) + 1;
364
+ nomen_last_click_count = (int)((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("clickCount"));
365
+ nomen_last_click_modifiers = (int)(((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")) & 0xFFF00000ULL);
366
+ }
367
+ // A scroll-wheel event (NSEventTypeScrollWheel = 22): accumulate
368
+ // the deltas for this frame. AppKit deltaY is positive scrolling
369
+ // up/away from the user; deltaX is positive scrolling right.
370
+ if (type == 22) {
371
+ nomen_scroll_dy_acc += ((double(*)(id, SEL))objc_msgSend)(event, sel_registerName("deltaY"));
372
+ nomen_scroll_dx_acc += ((double(*)(id, SEL))objc_msgSend)(event, sel_registerName("deltaX"));
373
+ }
374
+ // A key-down (NSEventTypeKeyDown = 10): record the hardware
375
+ // keycode (Space = 49, Return = 36, Delete = 51, Esc = 53,
376
+ // arrows = 123–126, letters a…z = 0–25), the modifiers, and
377
+ // whether this is an auto-repeat (a held key — filter it for
378
+ // first-press-only shortcuts).
379
+ if (type == 10) {
380
+ nomen_last_key_code = (int)((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
381
+ nomen_last_key_modifiers = (int)(((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")) & 0xFFF00000ULL);
382
+ nomen_last_key_repeat = ((BOOL(*)(id, SEL))objc_msgSend)(event, sel_registerName("isARepeat")) ? 1 : 0;
383
+ }
384
+ // A key-up (NSEventTypeKeyUp = 11): press-and-hold handling.
385
+ if (type == 11) {
386
+ nomen_last_key_up_code = (int)((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
387
+ }
388
+ // A pointer move (NSMouseMoved = 5; the dragged variants 6/7/26
389
+ // while a button is held): record where the pointer is now.
390
+ if (type == 5 || type == 6 || type == 7 || type == 26) {
391
+ id evwin = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("window"));
392
+ id cv = ((id(*)(id, SEL))objc_msgSend)(evwin, sel_registerName("contentView"));
393
+ CGRect frame = ((CGRect(*)(id, SEL))objc_msgSend)(cv, sel_registerName("frame"));
394
+ CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
395
+ nomen_last_mouse_move_x = (int)loc.x;
396
+ nomen_last_mouse_move_y = (int)(frame.size.height - loc.y);
397
+ }
398
+ if (type == 1) had_click = 1; // NSLeftMouseDown = 1
399
+ if (type == 3) had_click = 2; // NSRightMouseDown = 3 (reported as 2)
400
+ }
401
+ // Process run loop sources (Apple Events, timers) so that dock Quit
402
+ // and other system interactions work without NSApplication.run().
403
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1);
404
+ // Frame time: the interval since the previous pump (0 on the first).
405
+ double now = CFAbsoluteTimeGetCurrent();
406
+ if (nomen_last_pump_time != 0.0) {
407
+ nomen_last_dt_ms = (now - nomen_last_pump_time) * 1000.0;
408
+ }
409
+ nomen_last_pump_time = now;
410
+ int result = had_click;
411
+ // Stamp the kind for the drains (Container.take_right_click reads
412
+ // it in the handle_events pass that follows this pump).
413
+ nomen_last_click_kind = had_click;
414
+ ((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
415
+ return result;
416
+ ```
417
+ ```
418
+ #arch: aarch64_use_c
419
+ #platform: macos
420
+ // A manual event loop has no autorelease pool of its own: AppKit
421
+ // autoreleases objects on every pump (events, strings, cell caches)
422
+ // and without a pool they accumulate for the process lifetime — the
423
+ // loop leaked tens of MB per second and a busy window eventually
424
+ // died. One pool per pump; the returned had_click is a plain int.
425
+ id pool = ((id(*)(id, SEL))objc_msgSend)(
426
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")),
427
+ sel_registerName("init"));
170
428
  id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
171
429
  id distant_past = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSDate"), sel_registerName("distantPast"));
172
430
  id default_mode = ((id(*)(id, SEL, const char*))objc_msgSend)(
173
431
  (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode");
174
432
  int had_click = 0;
433
+ // This pump's mouse record: zeroed first, so a quiet pump expires
434
+ // the previous frame's edge — the getters and drains read these,
435
+ // and 0 means "no mouse-down / no scroll this frame".
436
+ nomen_last_click_button = 0;
437
+ nomen_last_click_count = 0;
438
+ nomen_last_click_modifiers = 0;
439
+ nomen_scroll_dx_acc = 0.0;
440
+ nomen_scroll_dy_acc = 0.0;
441
+ nomen_last_key_code = -1;
442
+ nomen_last_key_modifiers = 0;
443
+ nomen_last_key_repeat = 0;
444
+ nomen_last_key_up_code = -1;
445
+ nomen_last_mouse_move_x = -1;
446
+ nomen_last_mouse_move_y = -1;
175
447
  while (1) {
176
448
  id event = ((id(*)(id, SEL, unsigned long long, id, id, BOOL))objc_msgSend)(
177
449
  app, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
@@ -182,37 +454,540 @@ pub mv struct Window : Control {
182
454
  if (!event) break;
183
455
  ((void(*)(id, SEL, id))objc_msgSend)(app, sel_registerName("sendEvent:"), event);
184
456
  long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
457
+ // A mouse-down (left = 1, right = 3, other/middle/extra = 25):
458
+ // record where it landed and which button. locationInWindow is
459
+ // in the event's own window's coordinates (bottom-left origin);
460
+ // flip y against that window's content height to match the
461
+ // layout's top-left origin. buttonNumber is 0-based (0 left, 1
462
+ // right, 2 middle, 3+ extras) — stored 1-based to match
463
+ // FrameEvents.click's 1 = left / 2 = right contract. clickCount
464
+ // gives double/triple clicks; modifierFlags are masked to the
465
+ // device-independent bits.
466
+ if (type == 1 || type == 3 || type == 25) {
467
+ id evwin = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("window"));
468
+ id cv = ((id(*)(id, SEL))objc_msgSend)(evwin, sel_registerName("contentView"));
469
+ CGRect frame = ((CGRect(*)(id, SEL))objc_msgSend)(cv, sel_registerName("frame"));
470
+ CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
471
+ nomen_last_click_x = (int)loc.x;
472
+ nomen_last_click_y = (int)(frame.size.height - loc.y);
473
+ nomen_last_click_button = (int)(((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"))) + 1;
474
+ nomen_last_click_count = (int)((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("clickCount"));
475
+ nomen_last_click_modifiers = (int)(((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")) & 0xFFF00000ULL);
476
+ }
477
+ // A scroll-wheel event (NSEventTypeScrollWheel = 22): accumulate
478
+ // the deltas for this frame. AppKit deltaY is positive scrolling
479
+ // up/away from the user; deltaX is positive scrolling right.
480
+ if (type == 22) {
481
+ nomen_scroll_dy_acc += ((double(*)(id, SEL))objc_msgSend)(event, sel_registerName("deltaY"));
482
+ nomen_scroll_dx_acc += ((double(*)(id, SEL))objc_msgSend)(event, sel_registerName("deltaX"));
483
+ }
484
+ // A key-down (NSEventTypeKeyDown = 10): record the hardware
485
+ // keycode (Space = 49, Return = 36, Delete = 51, Esc = 53,
486
+ // arrows = 123–126, letters a…z = 0–25), the modifiers, and
487
+ // whether this is an auto-repeat (a held key — filter it for
488
+ // first-press-only shortcuts).
489
+ if (type == 10) {
490
+ nomen_last_key_code = (int)((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
491
+ nomen_last_key_modifiers = (int)(((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")) & 0xFFF00000ULL);
492
+ nomen_last_key_repeat = ((BOOL(*)(id, SEL))objc_msgSend)(event, sel_registerName("isARepeat")) ? 1 : 0;
493
+ }
494
+ // A key-up (NSEventTypeKeyUp = 11): press-and-hold handling.
495
+ if (type == 11) {
496
+ nomen_last_key_up_code = (int)((unsigned long(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
497
+ }
498
+ // A pointer move (NSMouseMoved = 5; the dragged variants 6/7/26
499
+ // while a button is held): record where the pointer is now.
500
+ if (type == 5 || type == 6 || type == 7 || type == 26) {
501
+ id evwin = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("window"));
502
+ id cv = ((id(*)(id, SEL))objc_msgSend)(evwin, sel_registerName("contentView"));
503
+ CGRect frame = ((CGRect(*)(id, SEL))objc_msgSend)(cv, sel_registerName("frame"));
504
+ CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
505
+ nomen_last_mouse_move_x = (int)loc.x;
506
+ nomen_last_mouse_move_y = (int)(frame.size.height - loc.y);
507
+ }
185
508
  if (type == 1) had_click = 1; // NSLeftMouseDown = 1
509
+ if (type == 3) had_click = 2; // NSRightMouseDown = 3 (reported as 2)
510
+ }
511
+ // Process run loop sources (Apple Events, timers) so that dock Quit
512
+ // and other system interactions work without NSApplication.run().
513
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1);
514
+ // Frame time: the interval since the previous pump (0 on the first).
515
+ double now = CFAbsoluteTimeGetCurrent();
516
+ if (nomen_last_pump_time != 0.0) {
517
+ nomen_last_dt_ms = (now - nomen_last_pump_time) * 1000.0;
518
+ }
519
+ nomen_last_pump_time = now;
520
+ int result = had_click;
521
+ // Stamp the kind for the drains (Container.take_right_click reads
522
+ // it in the handle_events pass that follows this pump).
523
+ nomen_last_click_kind = had_click;
524
+ ((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
525
+ return result;
526
+ ```
527
+ ```
528
+ #arch: c
529
+ #platform: ios
530
+ return 0;
531
+ ```
532
+ ```
533
+ #arch: aarch64_use_c
534
+ #platform: ios
535
+ return 0;
536
+ ```
537
+ }
538
+
539
+ // Returns true if the window is still on screen. Use this to exit a
540
+ // manual event loop when the user closes the window.
541
+ pub func is_visible = (self, out bool) {
542
+ ```
543
+ #arch: c
544
+ #platform: macos
545
+ BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isVisible"));
546
+ return vis;
547
+ ```
548
+ ```
549
+ #arch: aarch64_use_c
550
+ #platform: macos
551
+ BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isVisible"));
552
+ return vis;
553
+ ```
554
+ ```
555
+ #arch: c
556
+ #platform: ios
557
+ BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isKeyWindow"));
558
+ return vis;
559
+ ```
560
+ ```
561
+ #arch: aarch64_use_c
562
+ #platform: ios
563
+ BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isKeyWindow"));
564
+ return vis;
565
+ ```
566
+ }
567
+
568
+ // Returns the x coordinate (in the window's content view) of the most
569
+ // recent mouse-down (left or right). The pump (process_events) captures
570
+ // the coordinates when the event is handled, so drag/move events that
571
+ // follow in the same batch cannot make them stale. Call after
572
+ // process_events returned 1 or 2 (-1 before any click was ever seen).
573
+ pub func click_x = (self, out int) {
574
+ ```
575
+ #arch: c
576
+ #platform: macos
577
+ return nomen_last_click_x;
578
+ ```
579
+ ```
580
+ #arch: aarch64_use_c
581
+ #platform: macos
582
+ return nomen_last_click_x;
583
+ ```
584
+ ```
585
+ #arch: c
586
+ #platform: ios
587
+ return -1;
588
+ ```
589
+ ```
590
+ #arch: aarch64_use_c
591
+ #platform: ios
592
+ return -1;
593
+ ```
594
+ }
595
+
596
+ // Returns the y coordinate (flipped to top-left origin) of the most
597
+ // recent mouse-down, in the window's content view. Captured by the pump;
598
+ // see click_x.
599
+ pub func click_y = (self, out int) {
600
+ ```
601
+ #arch: c
602
+ #platform: macos
603
+ return nomen_last_click_y;
604
+ ```
605
+ ```
606
+ #arch: aarch64_use_c
607
+ #platform: macos
608
+ return nomen_last_click_y;
609
+ ```
610
+ ```
611
+ #arch: c
612
+ #platform: ios
613
+ return -1;
614
+ ```
615
+ ```
616
+ #arch: aarch64_use_c
617
+ #platform: ios
618
+ return -1;
619
+ ```
620
+ }
621
+
622
+ // ── The full mouse record (this frame's pump) ──
623
+ // The exhaustive input surface beside click_x/click_y: EVERY button
624
+ // (middle, mouse 4+), the click count (double/triple), and the modifier
625
+ // flags. All five are per-frame: stamped by the pump that saw the
626
+ // events, zeroed by the next — so a handler registered on
627
+ // `on_mouse_down` (Container.take_mouse_down) or a `frame` reading
628
+ // FrameEvents sees exactly this frame's record. `mouse_button` is
629
+ // 1-based (1 left, 2 right, 3 middle, 4+ extra buttons), 0 on a quiet
630
+ // frame — the same contract as FrameEvents.mouse_button.
631
+ //
632
+ // Modifier bits (NSEventModifierFlagDeviceIndependentFlagsMask, already
633
+ // applied by the pump): Command = 0x100000, Option = 0x80000,
634
+ // Control = 0x40000, Shift = 0x20000, Function = 0x800000.
635
+
636
+ pub func mouse_button = (self, out int) {
637
+ ```
638
+ #arch: c
639
+ #platform: macos
640
+ return nomen_last_click_button;
641
+ ```
642
+ ```
643
+ #arch: aarch64_use_c
644
+ #platform: macos
645
+ return nomen_last_click_button;
646
+ ```
647
+ ```
648
+ #arch: c
649
+ #platform: ios
650
+ return 0;
651
+ ```
652
+ ```
653
+ #arch: aarch64_use_c
654
+ #platform: ios
655
+ return 0;
656
+ ```
657
+ }
658
+
659
+ pub func mouse_click_count = (self, out int) {
660
+ ```
661
+ #arch: c
662
+ #platform: macos
663
+ return nomen_last_click_count;
664
+ ```
665
+ ```
666
+ #arch: aarch64_use_c
667
+ #platform: macos
668
+ return nomen_last_click_count;
669
+ ```
670
+ ```
671
+ #arch: c
672
+ #platform: ios
673
+ return 0;
674
+ ```
675
+ ```
676
+ #arch: aarch64_use_c
677
+ #platform: ios
678
+ return 0;
679
+ ```
680
+ }
681
+
682
+ pub func mouse_modifiers = (self, out int) {
683
+ ```
684
+ #arch: c
685
+ #platform: macos
686
+ return nomen_last_click_modifiers;
687
+ ```
688
+ ```
689
+ #arch: aarch64_use_c
690
+ #platform: macos
691
+ return nomen_last_click_modifiers;
692
+ ```
693
+ ```
694
+ #arch: c
695
+ #platform: ios
696
+ return 0;
697
+ ```
698
+ ```
699
+ #arch: aarch64_use_c
700
+ #platform: ios
701
+ return 0;
702
+ ```
703
+ }
704
+
705
+ // This frame's accumulated scroll deltas (AppKit deltaY/deltaX: dy is
706
+ // positive scrolling up/away from the user, dx positive scrolling
707
+ // right), truncated to ints. Scroll events accumulate across the pump's
708
+ // batch — a trackpad frame sums every small event — and expire with the
709
+ // frame. `Scrollable.on_scroll` is the registered-handler form.
710
+ pub func scroll_dx = (self, out int) {
711
+ ```
712
+ #arch: c
713
+ #platform: macos
714
+ return (int)nomen_scroll_dx_acc;
715
+ ```
716
+ ```
717
+ #arch: aarch64_use_c
718
+ #platform: macos
719
+ return (int)nomen_scroll_dx_acc;
720
+ ```
721
+ ```
722
+ #arch: c
723
+ #platform: ios
724
+ return 0;
725
+ ```
726
+ ```
727
+ #arch: aarch64_use_c
728
+ #platform: ios
729
+ return 0;
730
+ ```
731
+ }
732
+
733
+ pub func scroll_dy = (self, out int) {
734
+ ```
735
+ #arch: c
736
+ #platform: macos
737
+ return (int)nomen_scroll_dy_acc;
738
+ ```
739
+ ```
740
+ #arch: aarch64_use_c
741
+ #platform: macos
742
+ return (int)nomen_scroll_dy_acc;
743
+ ```
744
+ ```
745
+ #arch: c
746
+ #platform: ios
747
+ return 0;
748
+ ```
749
+ ```
750
+ #arch: aarch64_use_c
751
+ #platform: ios
752
+ return 0;
753
+ ```
754
+ }
755
+
756
+ // ── The keyboard record (this frame's pump) ──
757
+ // Hardware keycodes (NSEventKeyCode): Space = 49, Return = 36,
758
+ // Delete = 51, Esc = 53, arrows = 123–126, letters a…z = 0–25.
759
+ // `on_key_down` / `on_key_up` (Container.take_key_down / take_key_up)
760
+ // are the registered forms; FrameEvents carries the same record
761
+ // (key_code / key_modifiers / key_repeat / key_up_code). All per-frame:
762
+ // -1 = no key-down/key-up this frame.
763
+
764
+ pub func key_code = (self, out int) {
765
+ ```
766
+ #arch: c
767
+ #platform: macos
768
+ return nomen_last_key_code;
769
+ ```
770
+ ```
771
+ #arch: aarch64_use_c
772
+ #platform: macos
773
+ return nomen_last_key_code;
774
+ ```
775
+ ```
776
+ #arch: c
777
+ #platform: ios
778
+ return -1;
779
+ ```
780
+ ```
781
+ #arch: aarch64_use_c
782
+ #platform: ios
783
+ return -1;
784
+ ```
785
+ }
786
+
787
+ pub func key_modifiers = (self, out int) {
788
+ ```
789
+ #arch: c
790
+ #platform: macos
791
+ return nomen_last_key_modifiers;
792
+ ```
793
+ ```
794
+ #arch: aarch64_use_c
795
+ #platform: macos
796
+ return nomen_last_key_modifiers;
797
+ ```
798
+ ```
799
+ #arch: c
800
+ #platform: ios
801
+ return 0;
802
+ ```
803
+ ```
804
+ #arch: aarch64_use_c
805
+ #platform: ios
806
+ return 0;
807
+ ```
808
+ }
809
+
810
+ pub func key_repeat = (self, out bool) {
811
+ ```
812
+ #arch: c
813
+ #platform: macos
814
+ return nomen_last_key_repeat != 0;
815
+ ```
816
+ ```
817
+ #arch: aarch64_use_c
818
+ #platform: macos
819
+ return nomen_last_key_repeat != 0;
820
+ ```
821
+ ```
822
+ #arch: c
823
+ #platform: ios
824
+ return 0;
825
+ ```
826
+ ```
827
+ #arch: aarch64_use_c
828
+ #platform: ios
829
+ return 0;
830
+ ```
831
+ }
832
+
833
+ pub func key_up_code = (self, out int) {
834
+ ```
835
+ #arch: c
836
+ #platform: macos
837
+ return nomen_last_key_up_code;
838
+ ```
839
+ ```
840
+ #arch: aarch64_use_c
841
+ #platform: macos
842
+ return nomen_last_key_up_code;
843
+ ```
844
+ ```
845
+ #arch: c
846
+ #platform: ios
847
+ return -1;
848
+ ```
849
+ ```
850
+ #arch: aarch64_use_c
851
+ #platform: ios
852
+ return -1;
853
+ ```
854
+ }
855
+
856
+ // ── The pointer-move record (this frame's pump) ──
857
+ // The current pointer position — moves (and drags) stamped by the pump,
858
+ // -1 on a frame with no movement. `on_mouse_move`
859
+ // (Container.take_mouse_move) is the registered-handler form; hover,
860
+ // cursors, and tooltips hit-test this against Container.contains.
861
+
862
+ pub func mouse_x = (self, out int) {
863
+ ```
864
+ #arch: c
865
+ #platform: macos
866
+ return nomen_last_mouse_move_x;
867
+ ```
868
+ ```
869
+ #arch: aarch64_use_c
870
+ #platform: macos
871
+ return nomen_last_mouse_move_x;
872
+ ```
873
+ ```
874
+ #arch: c
875
+ #platform: ios
876
+ return -1;
877
+ ```
878
+ ```
879
+ #arch: aarch64_use_c
880
+ #platform: ios
881
+ return -1;
882
+ ```
883
+ }
884
+
885
+ pub func mouse_y = (self, out int) {
886
+ ```
887
+ #arch: c
888
+ #platform: macos
889
+ return nomen_last_mouse_move_y;
890
+ ```
891
+ ```
892
+ #arch: aarch64_use_c
893
+ #platform: macos
894
+ return nomen_last_mouse_move_y;
895
+ ```
896
+ ```
897
+ #arch: c
898
+ #platform: ios
899
+ return -1;
900
+ ```
901
+ ```
902
+ #arch: aarch64_use_c
903
+ #platform: ios
904
+ return -1;
905
+ ```
906
+ }
907
+
908
+ // ── Frame time ──
909
+ // Milliseconds since the previous pump (0 on the first frame) — the
910
+ // animation clock. `FrameEvents.dt` carries the same value.
911
+
912
+ pub func dt = (self, out int) {
913
+ ```
914
+ #arch: c
915
+ #platform: macos
916
+ return (int)nomen_last_dt_ms;
917
+ ```
918
+ ```
919
+ #arch: aarch64_use_c
920
+ #platform: macos
921
+ return (int)nomen_last_dt_ms;
922
+ ```
923
+ ```
924
+ #arch: c
925
+ #platform: ios
926
+ return 0;
927
+ ```
928
+ ```
929
+ #arch: aarch64_use_c
930
+ #platform: ios
931
+ return 0;
932
+ ```
933
+ }
934
+
935
+ // ── Window resize ──
936
+ // `on_resize` (Container.take_resize) fires on the frame whose content
937
+ // rect changed; the new size rides the dispatch window via these
938
+ // getters. The framework has ALREADY re-laid-out the registered roots
939
+ // (the delegate + the relayout pass) — handlers re-layout app-owned
940
+ // panes (tab pages, split panes). FrameEvents.resized / width / height
941
+ // carry the same record for `frame`.
942
+
943
+ // Stamp the resize record — run's resize detection calls this (the
944
+ // record lives in C statics shared with Container.take_resize; params
945
+ // because raw blocks in deep control flow can only address them).
946
+ func stamp_resize = (self, bool on, int w, int h) {
947
+ ```
948
+ #arch: c
949
+ #platform: macos
950
+ extern int nomen_last_resized;
951
+ extern int nomen_last_resize_w;
952
+ extern int nomen_last_resize_h;
953
+ nomen_last_resized = on ? 1 : 0;
954
+ if (on) {
955
+ nomen_last_resize_w = w;
956
+ nomen_last_resize_h = h;
186
957
  }
187
- // Process run loop sources (Apple Events, timers) so that dock Quit
188
- // and other system interactions work without NSApplication.run().
189
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1);
190
- return had_click;
191
958
  ```
192
959
  ```
193
960
  #arch: aarch64_use_c
194
961
  #platform: macos
195
- id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
196
- id distant_past = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSDate"), sel_registerName("distantPast"));
197
- id default_mode = ((id(*)(id, SEL, const char*))objc_msgSend)(
198
- (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode");
199
- int had_click = 0;
200
- while (1) {
201
- id event = ((id(*)(id, SEL, unsigned long long, id, id, BOOL))objc_msgSend)(
202
- app, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
203
- 0xFFFFFFFFFFFFFFFFULL, // NSEventMaskAny
204
- distant_past, // untilDate — returns immediately if no events
205
- default_mode, // inMode
206
- 1); // dequeue
207
- if (!event) break;
208
- ((void(*)(id, SEL, id))objc_msgSend)(app, sel_registerName("sendEvent:"), event);
209
- long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
210
- if (type == 1) had_click = 1; // NSLeftMouseDown = 1
962
+ extern int nomen_last_resized;
963
+ extern int nomen_last_resize_w;
964
+ extern int nomen_last_resize_h;
965
+ nomen_last_resized = on ? 1 : 0;
966
+ if (on) {
967
+ nomen_last_resize_w = w;
968
+ nomen_last_resize_h = h;
211
969
  }
212
- // Process run loop sources (Apple Events, timers) so that dock Quit
213
- // and other system interactions work without NSApplication.run().
214
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1);
215
- return had_click;
970
+ ```
971
+ ```
972
+ #arch: c
973
+ #platform: ios
974
+ ```
975
+ ```
976
+ #arch: aarch64_use_c
977
+ #platform: ios
978
+ ```
979
+ }
980
+
981
+ pub func resize_width = (self, out int) {
982
+ ```
983
+ #arch: c
984
+ #platform: macos
985
+ return nomen_last_resize_w;
986
+ ```
987
+ ```
988
+ #arch: aarch64_use_c
989
+ #platform: macos
990
+ return nomen_last_resize_w;
216
991
  ```
217
992
  ```
218
993
  #arch: c
@@ -226,114 +1001,80 @@ pub mv struct Window : Control {
226
1001
  ```
227
1002
  }
228
1003
 
229
- // Returns true if the window is still on screen. Use this to exit a
230
- // manual event loop when the user closes the window.
231
- pub func is_visible = (self, out bool) {
1004
+ pub func resize_height = (self, out int) {
232
1005
  ```
233
1006
  #arch: c
234
1007
  #platform: macos
235
- BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isVisible"));
236
- return vis;
1008
+ return nomen_last_resize_h;
237
1009
  ```
238
1010
  ```
239
1011
  #arch: aarch64_use_c
240
1012
  #platform: macos
241
- BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isVisible"));
242
- return vis;
1013
+ return nomen_last_resize_h;
243
1014
  ```
244
1015
  ```
245
1016
  #arch: c
246
1017
  #platform: ios
247
- BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isKeyWindow"));
248
- return vis;
1018
+ return 0;
249
1019
  ```
250
1020
  ```
251
1021
  #arch: aarch64_use_c
252
1022
  #platform: ios
253
- BOOL vis = ((BOOL(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("isKeyWindow"));
254
- return vis;
1023
+ return 0;
255
1024
  ```
256
1025
  }
257
1026
 
258
- // Returns the x coordinate (in the window's content view) of the most
259
- // recent mouse-down. Call after process_events returned 1.
260
- pub func click_x = (self, out int) {
1027
+ // ── Close interception ──
1028
+ // The red button routes through the App's `close_requested` (which may
1029
+ // refuse — the unsaved-changes pattern): the delegate refuses the
1030
+ // native close and sets a pending flag, and run consults the app on the
1031
+ // next frame. Programmatic `close()` is forced and bypasses this.
1032
+
1033
+ pub func close_pending = (self, out bool) {
261
1034
  ```
262
1035
  #arch: c
263
1036
  #platform: macos
264
- id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
265
- id event = ((id(*)(id, SEL))objc_msgSend)(app, sel_registerName("currentEvent"));
266
- if (!event) return -1;
267
- long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
268
- if (type != 1) return -1; // NSLeftMouseDown = 1
269
- // locationInWindow is a CGPoint inside the event; read it via struct-return
270
- CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
271
- return (int)loc.x;
1037
+ extern int nomen_close_pending;
1038
+ return nomen_close_pending != 0;
272
1039
  ```
273
1040
  ```
274
1041
  #arch: aarch64_use_c
275
1042
  #platform: macos
276
- id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
277
- id event = ((id(*)(id, SEL))objc_msgSend)(app, sel_registerName("currentEvent"));
278
- if (!event) return -1;
279
- long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
280
- if (type != 1) return -1; // NSLeftMouseDown = 1
281
- // locationInWindow is a CGPoint inside the event; read it via struct-return
282
- CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
283
- return (int)loc.x;
1043
+ extern int nomen_close_pending;
1044
+ return nomen_close_pending != 0;
284
1045
  ```
285
1046
  ```
286
1047
  #arch: c
287
1048
  #platform: ios
288
- return -1;
1049
+ return 0;
289
1050
  ```
290
1051
  ```
291
1052
  #arch: aarch64_use_c
292
1053
  #platform: ios
293
- return -1;
1054
+ return 0;
294
1055
  ```
295
1056
  }
296
1057
 
297
- // Returns the y coordinate (flipped to top-left origin) of the most
298
- // recent mouse-down, in the window's content view.
299
- pub func click_y = (self, out int) {
1058
+ pub func clear_close_pending = (self) {
300
1059
  ```
301
1060
  #arch: c
302
1061
  #platform: macos
303
- id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
304
- id event = ((id(*)(id, SEL))objc_msgSend)(app, sel_registerName("currentEvent"));
305
- if (!event) return -1;
306
- long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
307
- if (type != 1) return -1; // NSLeftMouseDown = 1
308
- CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
309
- id cv = ((id(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("contentView"));
310
- CGRect frame = ((CGRect(*)(id, SEL))objc_msgSend)(cv, sel_registerName("frame"));
311
- // NSWindow coords are bottom-left origin; flip to top-left for layout math
312
- return (int)(frame.size.height - loc.y);
1062
+ extern int nomen_close_pending;
1063
+ nomen_close_pending = 0;
313
1064
  ```
314
1065
  ```
315
1066
  #arch: aarch64_use_c
316
1067
  #platform: macos
317
- id app = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
318
- id event = ((id(*)(id, SEL))objc_msgSend)(app, sel_registerName("currentEvent"));
319
- if (!event) return -1;
320
- long type = ((long(*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
321
- if (type != 1) return -1; // NSLeftMouseDown = 1
322
- CGPoint loc = ((CGPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
323
- id cv = ((id(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("contentView"));
324
- CGRect frame = ((CGRect(*)(id, SEL))objc_msgSend)(cv, sel_registerName("frame"));
325
- // NSWindow coords are bottom-left origin; flip to top-left for layout math
326
- return (int)(frame.size.height - loc.y);
1068
+ extern int nomen_close_pending;
1069
+ nomen_close_pending = 0;
327
1070
  ```
328
1071
  ```
329
1072
  #arch: c
330
1073
  #platform: ios
331
- return -1;
332
1074
  ```
333
1075
  ```
334
1076
  #arch: aarch64_use_c
335
1077
  #platform: ios
336
- return -1;
337
1078
  ```
338
1079
  }
339
1080
 
@@ -409,6 +1150,270 @@ pub mv struct Window : Control {
409
1150
  ```
410
1151
  }
411
1152
 
1153
+ // Resize the window's content area to width x height (displayed). The
1154
+ // programmatic stand-in for a user drag: it fires the same
1155
+ // windowDidResize: notifications (and the Container resize delegate), so
1156
+ // tests can exercise resize relayout headlessly.
1157
+ pub func set_size = (self, int width, int height) {
1158
+ ```
1159
+ #arch: c
1160
+ #platform: macos
1161
+ ((void(*)(id, SEL, double, double))objc_msgSend)(
1162
+ (id)self->handle, sel_registerName("setContentSize:"), (double)width, (double)height);
1163
+ ```
1164
+ ```
1165
+ #arch: aarch64_use_c
1166
+ #platform: macos
1167
+ ((void(*)(id, SEL, double, double))objc_msgSend)(
1168
+ (id)self->handle, sel_registerName("setContentSize:"), (double)width, (double)height);
1169
+ ```
1170
+ ```
1171
+ #arch: c
1172
+ #platform: ios
1173
+ ((void(*)(id, SEL, CGRect))objc_msgSend)(
1174
+ (id)self->handle, sel_registerName("setFrame:"),
1175
+ ((CGRect(*)(double, double, double, double))CGRectMake)(0, 0, (double)width, (double)height));
1176
+ ```
1177
+ ```
1178
+ #arch: aarch64_use_c
1179
+ #platform: ios
1180
+ ((void(*)(id, SEL, CGRect))objc_msgSend)(
1181
+ (id)self->handle, sel_registerName("setFrame:"),
1182
+ ((CGRect(*)(double, double, double, double))CGRectMake)(0, 0, (double)width, (double)height));
1183
+ ```
1184
+ }
1185
+
1186
+ // ── The App run loop (see App.nm) ──
1187
+
1188
+ // Register the layout root for Window.run: the root Container's layout
1189
+ // re-runs on start, on every live-resize delegate tick, and on each
1190
+ // frame where the content rect changed. The grid must outlive the run
1191
+ // (an app field — registration stores its address). Call from
1192
+ // App.start; also arms the resize delegate's target pair.
1193
+ pub func set_content = (ref self, ref Container grid) {
1194
+ ```
1195
+ #arch: c
1196
+ #platform: macos
1197
+ #scope: file
1198
+ static void *nomen_run_root_grid = 0;
1199
+ static void *nomen_run_root_win = 0;
1200
+ ```
1201
+ ```
1202
+ #arch: aarch64_use_c
1203
+ #platform: macos
1204
+ #scope: file
1205
+ static void *nomen_run_root_grid = 0;
1206
+ static void *nomen_run_root_win = 0;
1207
+ ```
1208
+ ```
1209
+ #arch: c
1210
+ #platform: macos
1211
+ extern void nomen_set_resize_target(void *, void *);
1212
+ nomen_run_root_grid = (void *)grid;
1213
+ nomen_run_root_win = (void *)self;
1214
+ nomen_set_resize_target((void *)grid, (void *)self);
1215
+ ```
1216
+ ```
1217
+ #arch: aarch64_use_c
1218
+ #platform: macos
1219
+ extern void nomen_set_resize_target(void *, void *);
1220
+ nomen_run_root_grid = (void *)grid;
1221
+ nomen_run_root_win = (void *)self;
1222
+ nomen_set_resize_target((void *)grid, (void *)self);
1223
+ ```
1224
+ ```
1225
+ #arch: c
1226
+ #platform: ios
1227
+ /* TODO: iOS (see #init) */
1228
+ ```
1229
+ ```
1230
+ #arch: aarch64_use_c
1231
+ #platform: ios
1232
+ /* TODO: iOS (see #init) */
1233
+ ```
1234
+ }
1235
+
1236
+ // One framework relayout pass: the registered root Container, then every
1237
+ // registered Scrollable's document dance (`first` drives its one-time
1238
+ // scroll_to_top), then every registered pane root's layout (tabs pages /
1239
+ // split panes re-frame when THIS pass frames their host view — the app
1240
+ // cannot know the moment, so registration hands the timing here).
1241
+ func relayout = (self, int first) {
1242
+ ```
1243
+ #arch: c
1244
+ #platform: macos
1245
+ extern void nomen_layout_thunk(struct Container *, struct Window *);
1246
+ extern void nomen_scroll_relayout_all(void *, long);
1247
+ extern void nomen_pane_relayout_all(void);
1248
+ if (nomen_run_root_grid) {
1249
+ nomen_layout_thunk((struct Container *)nomen_run_root_grid, (struct Window *)self);
1250
+ }
1251
+ nomen_scroll_relayout_all((void *)self, first);
1252
+ nomen_pane_relayout_all();
1253
+ ```
1254
+ ```
1255
+ #arch: aarch64_use_c
1256
+ #platform: macos
1257
+ extern void nomen_layout_thunk(struct Container *, struct Window *);
1258
+ extern void nomen_scroll_relayout_all(void *, long);
1259
+ extern void nomen_pane_relayout_all(void);
1260
+ if (nomen_run_root_grid) {
1261
+ nomen_layout_thunk((struct Container *)nomen_run_root_grid, (struct Window *)self);
1262
+ }
1263
+ nomen_scroll_relayout_all((void *)self, first);
1264
+ nomen_pane_relayout_all();
1265
+ ```
1266
+ ```
1267
+ #arch: c
1268
+ #platform: ios
1269
+ /* TODO: iOS (see #init) */
1270
+ ```
1271
+ ```
1272
+ #arch: aarch64_use_c
1273
+ #platform: ios
1274
+ /* TODO: iOS (see #init) */
1275
+ ```
1276
+ }
1277
+
1278
+ // Arm mouse-MOVED delivery. AppKit sends move events only when the
1279
+ // window opts in — run() arms it before the loop; manual-loop apps
1280
+ // pumping process_events directly call this once after creating the
1281
+ // window. Idempotent.
1282
+ pub func arm_mouse_moves = (self) {
1283
+ ```
1284
+ #arch: c
1285
+ #platform: macos
1286
+ ((void(*)(id, SEL, BOOL))objc_msgSend)(
1287
+ (id)self->handle, sel_registerName("setAcceptsMouseMovedEvents:"), 1);
1288
+ ```
1289
+ ```
1290
+ #arch: aarch64_use_c
1291
+ #platform: macos
1292
+ ((void(*)(id, SEL, BOOL))objc_msgSend)(
1293
+ (id)self->handle, sel_registerName("setAcceptsMouseMovedEvents:"), 1);
1294
+ ```
1295
+ ```
1296
+ #arch: c
1297
+ #platform: ios
1298
+ ```
1299
+ ```
1300
+ #arch: aarch64_use_c
1301
+ #platform: ios
1302
+ ```
1303
+ }
1304
+
1305
+ // Close the window (the programmatic stand-in for the red button) — the
1306
+ // run loop exits on the next `is_visible` check. Programmatic close is
1307
+ // FORCED: it bypasses the close_requested interception (the delegate's
1308
+ // windowShouldClose: sees nomen_close_forced and allows). Only the red
1309
+ // button routes through `close_requested`.
1310
+ pub func close = (self) {
1311
+ ```
1312
+ #arch: c
1313
+ #platform: macos
1314
+ extern int nomen_close_forced;
1315
+ nomen_close_forced = 1;
1316
+ ((void(*)(id, SEL, id))objc_msgSend)((id)self->handle, sel_registerName("performClose:"), (id)0);
1317
+ ```
1318
+ ```
1319
+ #arch: aarch64_use_c
1320
+ #platform: macos
1321
+ extern int nomen_close_forced;
1322
+ nomen_close_forced = 1;
1323
+ ((void(*)(id, SEL, id))objc_msgSend)((id)self->handle, sel_registerName("performClose:"), (id)0);
1324
+ ```
1325
+ ```
1326
+ #arch: c
1327
+ #platform: ios
1328
+ /* TODO: iOS (see #init) */
1329
+ ```
1330
+ ```
1331
+ #arch: aarch64_use_c
1332
+ #platform: ios
1333
+ /* TODO: iOS (see #init) */
1334
+ ```
1335
+ }
1336
+
1337
+ // Run the app: the framework owns everything from here until the window
1338
+ // closes. Order: `app.start` (the app registers its layout roots and its
1339
+ // click handlers while in final storage), the pre-show layout pass,
1340
+ // `show`, the post-show pass (the toolbar settles the content rect only
1341
+ // after show; this pass also performs the one-time scroll_to_top), then
1342
+ // the loop — pump events, detect resizes (framework relayout already
1343
+ // applied), drain the app's registered handlers (its synthesized
1344
+ // `handle_events` pump — the control-event desugars), and hand the app
1345
+ // a FrameEvents each frame.
1346
+ // The app is consumed: it is destroyed when the loop exits (controls
1347
+ // release, buffers free).
1348
+ pub func run = (ref self, mv App app) {
1349
+ app.start(ref self)
1350
+ Container.arm_resize_delegate(self)
1351
+ self.arm_mouse_moves()
1352
+ self.relayout(0)
1353
+ self.show()
1354
+ self.relayout(1)
1355
+ var int prev_w = self.content_width()
1356
+ var int prev_h = self.content_height()
1357
+ var FrameEvents ev = FrameEvents()
1358
+ while self.is_visible() {
1359
+ // Close interception: the delegate refused the native close and
1360
+ // set the pending flag; consult the app. Accept → forced close
1361
+ // (bypasses the delegate); decline → window stays, flag cleared.
1362
+ if self.close_pending() {
1363
+ if app.close_requested(ref self) {
1364
+ self.close()
1365
+ }
1366
+ self.clear_close_pending()
1367
+ }
1368
+ // An accepted close above releases the native window mid-frame;
1369
+ // skip the rest of this iteration (the while re-checks and
1370
+ // exits).
1371
+ if self.is_visible() {
1372
+ ev.click = Window.process_events()
1373
+ ev.click_x = self.click_x()
1374
+ ev.click_y = self.click_y()
1375
+ ev.mouse_button = self.mouse_button()
1376
+ ev.mouse_click_count = self.mouse_click_count()
1377
+ ev.mouse_modifiers = self.mouse_modifiers()
1378
+ ev.scroll_dx = self.scroll_dx()
1379
+ ev.scroll_dy = self.scroll_dy()
1380
+ ev.key_code = self.key_code()
1381
+ ev.key_modifiers = self.key_modifiers()
1382
+ ev.key_repeat = self.key_repeat()
1383
+ ev.key_up_code = self.key_up_code()
1384
+ ev.mouse_x = self.mouse_x()
1385
+ ev.mouse_y = self.mouse_y()
1386
+ ev.dt = self.dt()
1387
+ ev.resized = false
1388
+ self.stamp_resize(false, 0, 0)
1389
+ var int cur_w = self.content_width()
1390
+ var int cur_h = self.content_height()
1391
+ if cur_w != prev_w {
1392
+ ev.resized = true
1393
+ }
1394
+ if cur_h != prev_h {
1395
+ ev.resized = true
1396
+ }
1397
+ if ev.resized {
1398
+ prev_w = cur_w
1399
+ prev_h = cur_h
1400
+ ev.width = cur_w
1401
+ ev.height = cur_h
1402
+ self.stamp_resize(true, cur_w, cur_h)
1403
+ self.relayout(0)
1404
+ }
1405
+ // The window refreshes the per-frame event sources (the
1406
+ // first-responder snapshot feeds the focus/blur drains), then the
1407
+ // app's synthesized `handle_events` pump drains registered
1408
+ // handlers — conformers without registrations inherit the
1409
+ // trait's empty default. One arm per registered control.
1410
+ nomen_window_focus_poll(self.handle)
1411
+ app.handle_events(ref self)
1412
+ app.frame(ref self, ev)
1413
+ }
1414
+ }
1415
+ }
1416
+
412
1417
  // ── Control trait conformance ──
413
1418
  // The window is the layout root: it measures to its content-view size and
414
1419
  // applies a frame to the native NSWindow/UIWindow.
@@ -431,16 +1436,22 @@ pub mv struct Window : Control {
431
1436
  ```
432
1437
  #arch: c
433
1438
  #platform: macos
434
- ((void(*)(id, SEL, CGRect))objc_msgSend)(
435
- (id)self->handle, sel_registerName("setFrame:"),
436
- ((CGRect(*)(double, double, double, double))CGRectMake)((double)x, (double)y, (double)width, (double)height));
1439
+ // NSWindow has no plain `setFrame:` (that is NSView's) — the window
1440
+ // API takes the display flag.
1441
+ ((void(*)(id, SEL, CGRect, BOOL))objc_msgSend)(
1442
+ (id)self->handle, sel_registerName("setFrame:display:"),
1443
+ ((CGRect(*)(double, double, double, double))CGRectMake)((double)x, (double)y, (double)width, (double)height),
1444
+ 1);
437
1445
  ```
438
1446
  ```
439
1447
  #arch: aarch64_use_c
440
1448
  #platform: macos
441
- ((void(*)(id, SEL, CGRect))objc_msgSend)(
442
- (id)self->handle, sel_registerName("setFrame:"),
443
- ((CGRect(*)(double, double, double, double))CGRectMake)((double)x, (double)y, (double)width, (double)height));
1449
+ // NSWindow has no plain `setFrame:` (that is NSView's) — the window
1450
+ // API takes the display flag.
1451
+ ((void(*)(id, SEL, CGRect, BOOL))objc_msgSend)(
1452
+ (id)self->handle, sel_registerName("setFrame:display:"),
1453
+ ((CGRect(*)(double, double, double, double))CGRectMake)((double)x, (double)y, (double)width, (double)height),
1454
+ 1);
444
1455
  ```
445
1456
  ```
446
1457
  #arch: c
@@ -493,3 +1504,450 @@ pub mv struct Window : Control {
493
1504
  ```
494
1505
  }
495
1506
  }
1507
+
1508
+ // Arm click recording on a registered view. NSButton-family controls are
1509
+ // armed at construction (nomen_control_arm_clicks) and already push to the
1510
+ // ring; this re-arms those harmlessly and covers every OTHER view through
1511
+ // an NSClickGestureRecognizer whose handler records the recognized view —
1512
+ // the Nomen handle — into the same ring. Idempotent per handle (the armed
1513
+ // set below — a desugar dispatch arm calls this every frame, and a fresh
1514
+ // recognizer per call would accumulate); the recognizer target class is
1515
+ // still built once per process.
1516
+ // ── Registered control events (focus / blur / input) ──
1517
+ //
1518
+ // The desugar's control-carried events beyond clicks (docs/HANDLERS.md):
1519
+ // `self.<field>.on_focus / on_blur / on_input(func () { ... })`. The
1520
+ // window is the focus authority, so the snapshot lives here: the
1521
+ // synthesized focus dispatcher calls `nomen_window_focus_poll` ONCE per
1522
+ // frame (prev/cur first responder — the field editor maps back to the
1523
+ // editing control), then each arm is a pure snapshot compare via
1524
+ // `nomen_control_take_focus` / `take_blur` — order-independent across
1525
+ // arms, and both sides of a handoff (A's blur, B's focus) fire in the
1526
+ // same frame. Input edges are per-control text compares
1527
+ // (`nomen_control_take_input`): the field editor writes through to the
1528
+ // control's stringValue while typing, so each keystroke is one edge.
1529
+
1530
+ // Refresh the focus snapshot (prev/cur first responder of `window`).
1531
+ // Idempotent per frame; the synthesized focus dispatcher calls it once
1532
+ // before the arms.
1533
+ pub func nomen_window_focus_poll = (uint64 window) {
1534
+ ```
1535
+ #arch: c
1536
+ #platform: macos
1537
+ #scope: file
1538
+ // Shared with the take_focus / take_blur drains (same TU): the snapshot
1539
+ // has external linkage and ONE definition (here); the drains declare it
1540
+ // extern.
1541
+ unsigned long long nomen_focus_prev = 0;
1542
+ unsigned long long nomen_focus_cur = 0;
1543
+ void *nomen_focus_window = 0;
1544
+ static id nomen_focus_responder(void *win) {
1545
+ id fr = ((id(*)(id, SEL))objc_msgSend)((id)win, sel_registerName("firstResponder"));
1546
+ // While a text control is being edited, the first responder is the
1547
+ // window's FIELD EDITOR — map it back to the editing control (the
1548
+ // editor's delegate) so focus edges name the control.
1549
+ if (((BOOL(*)(id, SEL, id))objc_msgSend)(fr, sel_registerName("isKindOfClass:"), (id)objc_getClass("NSTextView"))) {
1550
+ id d = ((id(*)(id, SEL))objc_msgSend)(fr, sel_registerName("delegate"));
1551
+ if (d) fr = d;
1552
+ }
1553
+ return fr;
1554
+ }
1555
+ ```
1556
+ ```
1557
+ #arch: aarch64_use_c
1558
+ #platform: macos
1559
+ #scope: file
1560
+ unsigned long long nomen_focus_prev = 0;
1561
+ unsigned long long nomen_focus_cur = 0;
1562
+ void *nomen_focus_window = 0;
1563
+ static id nomen_focus_responder(void *win) {
1564
+ id fr = ((id(*)(id, SEL))objc_msgSend)((id)win, sel_registerName("firstResponder"));
1565
+ if (((BOOL(*)(id, SEL, id))objc_msgSend)(fr, sel_registerName("isKindOfClass:"), (id)objc_getClass("NSTextView"))) {
1566
+ id d = ((id(*)(id, SEL))objc_msgSend)(fr, sel_registerName("delegate"));
1567
+ if (d) fr = d;
1568
+ }
1569
+ return fr;
1570
+ }
1571
+ ```
1572
+ ```
1573
+ #arch: c
1574
+ #platform: macos
1575
+ void *win = (void *)window;
1576
+ id fr = nomen_focus_responder(win);
1577
+ if (nomen_focus_window != win) {
1578
+ // A different window took the snapshot: re-baseline (no edge).
1579
+ nomen_focus_window = win;
1580
+ nomen_focus_cur = (unsigned long long)(void *)fr;
1581
+ nomen_focus_prev = nomen_focus_cur;
1582
+ } else {
1583
+ nomen_focus_prev = nomen_focus_cur;
1584
+ nomen_focus_cur = (unsigned long long)(void *)fr;
1585
+ }
1586
+ ```
1587
+ ```
1588
+ #arch: aarch64_use_c
1589
+ #platform: macos
1590
+ void *win = (void *)window;
1591
+ id fr = nomen_focus_responder(win);
1592
+ if (nomen_focus_window != win) {
1593
+ nomen_focus_window = win;
1594
+ nomen_focus_cur = (unsigned long long)(void *)fr;
1595
+ nomen_focus_prev = nomen_focus_cur;
1596
+ } else {
1597
+ nomen_focus_prev = nomen_focus_cur;
1598
+ nomen_focus_cur = (unsigned long long)(void *)fr;
1599
+ }
1600
+ ```
1601
+ ```
1602
+ #arch: c
1603
+ #platform: ios
1604
+ /* TODO: iOS (first responder via UITextField delegates) */
1605
+ ```
1606
+ ```
1607
+ #arch: aarch64_use_c
1608
+ #platform: ios
1609
+ /* TODO: iOS (see #arch: c) */
1610
+ ```
1611
+ }
1612
+
1613
+ // Whether the most recent pump saw a right-mouse-down (the same record
1614
+ // FrameEvents.click reports as 2). Read by Container.take_right_click —
1615
+ // the on_right_click drain (docs/HANDLERS.md). The pump stamps the kind
1616
+ // on EVERY call — a quiet pump stamps 0 — so the edge expires with the
1617
+ // frame: the drains see it exactly in the handle_events pass that follows
1618
+ // the pump, and no clearing handshake is needed. The record is
1619
+ // window-level (one per process, like click_x/click_y): every registered
1620
+ // arm sees the edge (broadcast), and the HANDLER disambiguates — read the
1621
+ // click through the dispatch window (win.click_x()/click_y()), translate
1622
+ // for scrollables (content_click), hit-test children (contains).
1623
+ pub func nomen_window_had_right_click = (out bool) {
1624
+ ```
1625
+ #arch: c
1626
+ #platform: macos
1627
+ // One definition lives in process_events' file-scope block (this file);
1628
+ // the extern keeps this function's TU-independent (split builds).
1629
+ extern int nomen_last_click_kind;
1630
+ return nomen_last_click_kind == 2;
1631
+ ```
1632
+ ```
1633
+ #arch: aarch64_use_c
1634
+ #platform: macos
1635
+ extern int nomen_last_click_kind;
1636
+ return nomen_last_click_kind == 2;
1637
+ ```
1638
+ ```
1639
+ #arch: c
1640
+ #platform: ios
1641
+ return 0;
1642
+ ```
1643
+ ```
1644
+ #arch: aarch64_use_c
1645
+ #platform: ios
1646
+ return 0;
1647
+ ```
1648
+ }
1649
+
1650
+ // Focus EDGE drain: true when `handle` GAINED first-responder status this
1651
+ // frame. Pure snapshot compare — no state update, so any number of
1652
+ // focus/blur arms can poll the same frame in any order.
1653
+ pub func nomen_control_take_focus = (uint64 handle, out bool) {
1654
+ ```
1655
+ #arch: c
1656
+ #platform: macos
1657
+ #scope: file
1658
+ extern unsigned long long nomen_focus_prev;
1659
+ extern unsigned long long nomen_focus_cur;
1660
+ extern void *nomen_focus_window;
1661
+ static id nomen_focus_responder(void *win);
1662
+ ```
1663
+ ```
1664
+ #arch: c
1665
+ #platform: macos
1666
+ int known = nomen_focus_window != 0;
1667
+ id fr = known ? nomen_focus_responder(nomen_focus_window) : 0;
1668
+ int gained =
1669
+ known && ((unsigned long long)(void *)fr == handle) && (nomen_focus_prev != handle);
1670
+ return gained;
1671
+ ```
1672
+ ```
1673
+ #arch: aarch64_use_c
1674
+ #platform: macos
1675
+ #scope: file
1676
+ extern unsigned long long nomen_focus_prev;
1677
+ extern unsigned long long nomen_focus_cur;
1678
+ extern void *nomen_focus_window;
1679
+ static id nomen_focus_responder(void *win);
1680
+ ```
1681
+ ```
1682
+ #arch: aarch64_use_c
1683
+ #platform: macos
1684
+ int known = nomen_focus_window != 0;
1685
+ id fr = known ? nomen_focus_responder(nomen_focus_window) : 0;
1686
+ int gained =
1687
+ known && ((unsigned long long)(void *)fr == handle) && (nomen_focus_prev != handle);
1688
+ return gained;
1689
+ ```
1690
+ ```
1691
+ #arch: c
1692
+ #platform: ios
1693
+ return 0;
1694
+ ```
1695
+ ```
1696
+ #arch: aarch64_use_c
1697
+ #platform: ios
1698
+ return 0;
1699
+ ```
1700
+ }
1701
+
1702
+ // Blur EDGE drain: true when `handle` LOST first-responder status this
1703
+ // frame (it was focused on the previous poll and is not now).
1704
+ pub func nomen_control_take_blur = (uint64 handle, out bool) {
1705
+ ```
1706
+ #arch: c
1707
+ #platform: macos
1708
+ #scope: file
1709
+ extern unsigned long long nomen_focus_prev;
1710
+ extern unsigned long long nomen_focus_cur;
1711
+ extern void *nomen_focus_window;
1712
+ static id nomen_focus_responder(void *win);
1713
+ ```
1714
+ ```
1715
+ #arch: c
1716
+ #platform: macos
1717
+ int known = nomen_focus_window != 0;
1718
+ id fr = known ? nomen_focus_responder(nomen_focus_window) : 0;
1719
+ int lost =
1720
+ known && (nomen_focus_prev == handle) && ((unsigned long long)(void *)fr != handle);
1721
+ return lost;
1722
+ ```
1723
+ ```
1724
+ #arch: aarch64_use_c
1725
+ #platform: macos
1726
+ #scope: file
1727
+ extern unsigned long long nomen_focus_prev;
1728
+ extern unsigned long long nomen_focus_cur;
1729
+ extern void *nomen_focus_window;
1730
+ static id nomen_focus_responder(void *win);
1731
+ ```
1732
+ ```
1733
+ #arch: aarch64_use_c
1734
+ #platform: macos
1735
+ int known = nomen_focus_window != 0;
1736
+ id fr = known ? nomen_focus_responder(nomen_focus_window) : 0;
1737
+ int lost =
1738
+ known && (nomen_focus_prev == handle) && ((unsigned long long)(void *)fr != handle);
1739
+ return lost;
1740
+ ```
1741
+ ```
1742
+ #arch: c
1743
+ #platform: ios
1744
+ return 0;
1745
+ ```
1746
+ ```
1747
+ #arch: aarch64_use_c
1748
+ #platform: ios
1749
+ return 0;
1750
+ ```
1751
+ }
1752
+
1753
+ // Text-INPUT edge drain: true when the control's text changed since the
1754
+ // last drain for this handle (the field editor writes through to the
1755
+ // control's stringValue while typing, so each keystroke is one edge).
1756
+ // Last-seen text is tracked per handle.
1757
+ pub func nomen_control_take_input = (uint64 handle, out bool) {
1758
+ ```
1759
+ #arch: c
1760
+ #platform: macos
1761
+ #scope: file
1762
+ struct nomen_input_seen {
1763
+ void *handle;
1764
+ char *last;
1765
+ };
1766
+ static struct nomen_input_seen nomen_input_seen[32];
1767
+ static int nomen_input_seen_count = 0;
1768
+ ```
1769
+ ```
1770
+ #arch: c
1771
+ #platform: macos
1772
+ struct nomen_input_seen *e = 0;
1773
+ for (int i = 0; i < nomen_input_seen_count; i++) {
1774
+ if (nomen_input_seen[i].handle == (void *)handle) {
1775
+ e = &nomen_input_seen[i];
1776
+ break;
1777
+ }
1778
+ }
1779
+ if (!e && nomen_input_seen_count < 32) {
1780
+ e = &nomen_input_seen[nomen_input_seen_count++];
1781
+ e->handle = (void *)handle;
1782
+ e->last = 0;
1783
+ }
1784
+ if (!e) return 0;
1785
+ // stringValue returns the live text (the field editor writes through to
1786
+ // it while typing); UTF8String goes through the returned NSString.
1787
+ id s = ((id(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("stringValue"));
1788
+ const char *cur = ((const char *(*)(id, SEL))objc_msgSend)(s, sel_registerName("UTF8String"));
1789
+ // First sight of a handle: record the baseline silently — no edge.
1790
+ if (!e->last) {
1791
+ e->last = strdup(cur);
1792
+ return 0;
1793
+ }
1794
+ int changed = strcmp(e->last, cur) != 0;
1795
+ if (changed) {
1796
+ free(e->last);
1797
+ e->last = strdup(cur);
1798
+ }
1799
+ return changed;
1800
+ ```
1801
+ ```
1802
+ #arch: aarch64_use_c
1803
+ #platform: macos
1804
+ #scope: file
1805
+ struct nomen_input_seen {
1806
+ void *handle;
1807
+ char *last;
1808
+ };
1809
+ static struct nomen_input_seen nomen_input_seen[32];
1810
+ static int nomen_input_seen_count = 0;
1811
+ ```
1812
+ ```
1813
+ #arch: aarch64_use_c
1814
+ #platform: macos
1815
+ struct nomen_input_seen *e = 0;
1816
+ for (int i = 0; i < nomen_input_seen_count; i++) {
1817
+ if (nomen_input_seen[i].handle == (void *)handle) {
1818
+ e = &nomen_input_seen[i];
1819
+ break;
1820
+ }
1821
+ }
1822
+ if (!e && nomen_input_seen_count < 32) {
1823
+ e = &nomen_input_seen[nomen_input_seen_count++];
1824
+ e->handle = (void *)handle;
1825
+ e->last = 0;
1826
+ }
1827
+ if (!e) return 0;
1828
+ // stringValue returns the live text (the field editor writes through to
1829
+ // it while typing); UTF8String goes through the returned NSString.
1830
+ id s = ((id(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("stringValue"));
1831
+ const char *cur = ((const char *(*)(id, SEL))objc_msgSend)(s, sel_registerName("UTF8String"));
1832
+ // First sight of a handle: record the baseline silently — no edge.
1833
+ if (!e->last) {
1834
+ e->last = strdup(cur);
1835
+ return 0;
1836
+ }
1837
+ int changed = strcmp(e->last, cur) != 0;
1838
+ if (changed) {
1839
+ free(e->last);
1840
+ e->last = strdup(cur);
1841
+ }
1842
+ return changed;
1843
+ ```
1844
+ ```
1845
+ #arch: c
1846
+ #platform: ios
1847
+ return 0;
1848
+ ```
1849
+ ```
1850
+ #arch: aarch64_use_c
1851
+ #platform: ios
1852
+ return 0;
1853
+ ```
1854
+ }
1855
+
1856
+
1857
+ pub func nomen_window_clicks_init = (uint64 handle) {
1858
+ ```
1859
+ #arch: c
1860
+ #platform: macos
1861
+ #scope: file
1862
+ // The armed set: handles this process already armed (a desugar dispatch
1863
+ // arm re-calls per frame; re-arming would stack recognizers).
1864
+ static unsigned long long nomen_armed_handles[128];
1865
+ static int nomen_armed_count = 0;
1866
+ // The shared gesture target (one class pair per process; the handler
1867
+ // reads the gesture's view — the Nomen handle — and pushes the same
1868
+ // ring the button family's target/action uses).
1869
+ static void nomen_view_click_gesture(id self, SEL cmd, id gesture) {
1870
+ (void)self; (void)cmd;
1871
+ id view = ((id(*)(id, SEL))objc_msgSend)(gesture, sel_registerName("view"));
1872
+ nomen_click_push((unsigned long long)(void *)view);
1873
+ }
1874
+ static void nomen_window_click_arm(unsigned long long handle) {
1875
+ for (int i = 0; i < nomen_armed_count; i++) {
1876
+ if (nomen_armed_handles[i] == handle) return;
1877
+ }
1878
+ if (nomen_armed_count < 128) nomen_armed_handles[nomen_armed_count++] = handle;
1879
+ Class Tc = objc_getClass("NomenViewClickTarget");
1880
+ if (!Tc) {
1881
+ Tc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenViewClickTarget", 0);
1882
+ if (Tc) {
1883
+ class_addMethod(Tc, sel_registerName("viewClickGesture:"), (IMP)nomen_view_click_gesture, "v@:@");
1884
+ objc_registerClassPair(Tc);
1885
+ }
1886
+ }
1887
+ if (!Tc) return;
1888
+ id target = ((id(*)(id, SEL))objc_msgSend)((id)Tc, sel_registerName("new"));
1889
+ id rec = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSClickGestureRecognizer"), sel_registerName("alloc"));
1890
+ rec = ((id(*)(id, SEL, id, SEL))objc_msgSend)(
1891
+ rec, sel_registerName("initWithTarget:action:"), (id)target, sel_registerName("viewClickGesture:"));
1892
+ ((void(*)(id, SEL, id))objc_msgSend)((id)handle, sel_registerName("addGestureRecognizer:"), (id)rec);
1893
+ }
1894
+ ```
1895
+ ```
1896
+ #arch: c
1897
+ #platform: macos
1898
+ nomen_window_click_arm(handle);
1899
+ ```
1900
+ ```
1901
+ #arch: aarch64_use_c
1902
+ #platform: macos
1903
+ #scope: file
1904
+ // The armed set: handles this process already armed (a desugar dispatch
1905
+ // arm re-calls per frame; re-arming would stack recognizers).
1906
+ static unsigned long long nomen_armed_handles[128];
1907
+ static int nomen_armed_count = 0;
1908
+ // Forward declaration: the ring lives in Control.nm's file-scope
1909
+ // block, whose carrier may hoist AFTER this one (build order, not
1910
+ // source order).
1911
+ static void nomen_click_push(unsigned long long h);
1912
+ static void nomen_view_click_gesture(id self, SEL cmd, id gesture) {
1913
+ (void)self; (void)cmd;
1914
+ id view = ((id(*)(id, SEL))objc_msgSend)(gesture, sel_registerName("view"));
1915
+ nomen_click_push((unsigned long long)(void *)view);
1916
+ }
1917
+ static void nomen_window_click_arm(unsigned long long handle) {
1918
+ for (int i = 0; i < nomen_armed_count; i++) {
1919
+ if (nomen_armed_handles[i] == handle) return;
1920
+ }
1921
+ if (nomen_armed_count < 128) nomen_armed_handles[nomen_armed_count++] = handle;
1922
+ Class Tc = objc_getClass("NomenViewClickTarget");
1923
+ if (!Tc) {
1924
+ Tc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenViewClickTarget", 0);
1925
+ if (Tc) {
1926
+ class_addMethod(Tc, sel_registerName("viewClickGesture:"), (IMP)nomen_view_click_gesture, "v@:@");
1927
+ objc_registerClassPair(Tc);
1928
+ }
1929
+ }
1930
+ if (!Tc) return;
1931
+ id target = ((id(*)(id, SEL))objc_msgSend)((id)Tc, sel_registerName("new"));
1932
+ id rec = ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSClickGestureRecognizer"), sel_registerName("alloc"));
1933
+ rec = ((id(*)(id, SEL, id, SEL))objc_msgSend)(
1934
+ rec, sel_registerName("initWithTarget:action:"), (id)target, sel_registerName("viewClickGesture:"));
1935
+ ((void(*)(id, SEL, id))objc_msgSend)((id)handle, sel_registerName("addGestureRecognizer:"), (id)rec);
1936
+ }
1937
+ ```
1938
+ ```
1939
+ #arch: aarch64_use_c
1940
+ #platform: macos
1941
+ nomen_window_click_arm(handle);
1942
+ ```
1943
+ ```
1944
+ #arch: c
1945
+ #platform: ios
1946
+ /* TODO: iOS (UITapGestureRecognizer shape; see nomen_control_arm_clicks) */
1947
+ ```
1948
+ ```
1949
+ #arch: aarch64_use_c
1950
+ #platform: ios
1951
+ /* TODO: iOS (UITapGestureRecognizer shape; see nomen_control_arm_clicks) */
1952
+ ```
1953
+ }