nomen-lang 0.7.1 → 0.9.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 (80) hide show
  1. package/audit_runtime.c +12 -2
  2. package/core/System/Arena.nm +8 -1
  3. package/core/System/Buffer.nm +397 -8
  4. package/core/System/ClassBuffer.nm +156 -0
  5. package/core/System/Controls/Alert.nm +326 -0
  6. package/core/System/Controls/App.nm +147 -0
  7. package/core/System/Controls/Button.nm +119 -1
  8. package/core/System/Controls/CheckBox.nm +25 -1
  9. package/core/System/Controls/ColorPicker.nm +307 -0
  10. package/core/System/Controls/ComboBox.nm +430 -0
  11. package/core/System/Controls/Container.nm +843 -9
  12. package/core/System/Controls/Control.nm +443 -0
  13. package/core/System/Controls/DatePicker.nm +288 -0
  14. package/core/System/Controls/Form.nm +70 -0
  15. package/core/System/Controls/GroupBox.nm +267 -0
  16. package/core/System/Controls/ImageView.nm +311 -0
  17. package/core/System/Controls/LevelIndicator.nm +305 -0
  18. package/core/System/Controls/ListView.nm +633 -0
  19. package/core/System/Controls/Menu.nm +428 -0
  20. package/core/System/Controls/NumberInput.nm +56 -0
  21. package/core/System/Controls/OtpInput.nm +50 -0
  22. package/core/System/Controls/OutlineView.nm +598 -0
  23. package/core/System/Controls/Pagination.nm +142 -0
  24. package/core/System/Controls/Popover.nm +296 -0
  25. package/core/System/Controls/Progress.nm +348 -0
  26. package/core/System/Controls/Radio.nm +292 -0
  27. package/core/System/Controls/Scrollable.nm +584 -0
  28. package/core/System/Controls/SearchField.nm +326 -0
  29. package/core/System/Controls/SecureField.nm +344 -0
  30. package/core/System/Controls/Segmented.nm +380 -0
  31. package/core/System/Controls/Select.nm +340 -0
  32. package/core/System/Controls/Separator.nm +198 -0
  33. package/core/System/Controls/Sheet.nm +329 -0
  34. package/core/System/Controls/Slider.nm +315 -0
  35. package/core/System/Controls/Spinner.nm +287 -0
  36. package/core/System/Controls/SplitView.nm +509 -0
  37. package/core/System/Controls/Stepper.nm +349 -0
  38. package/core/System/Controls/Switch.nm +259 -0
  39. package/core/System/Controls/Table.nm +833 -0
  40. package/core/System/Controls/Tabs.nm +500 -0
  41. package/core/System/Controls/Text.nm +108 -11
  42. package/core/System/Controls/TextBox.nm +113 -2
  43. package/core/System/Controls/TextView.nm +329 -0
  44. package/core/System/Controls/Toggle.nm +346 -0
  45. package/core/System/Controls/Toolbar.nm +501 -0
  46. package/core/System/Controls/Tooltip.nm +76 -0
  47. package/core/System/Controls/Window.nm +1548 -91
  48. package/core/System/LinkedList.nm +8 -1
  49. package/core/System/List.nm +97 -7
  50. package/core/System/Map.nm +191 -18
  51. package/core/System/Mutex.nm +4 -4
  52. package/core/System/Random.nm +3 -4
  53. package/core/System/Set.nm +101 -13
  54. package/core/System/StableList.nm +371 -0
  55. package/core/System/Stream/Directory.nm +2 -2
  56. package/core/System/Stream/File.nm +2 -2
  57. package/core/System/Stream/Http.nm +2 -2
  58. package/core/System/String.nm +10 -5
  59. package/core/System/StringBuilder.nm +213 -2
  60. package/core/System/Text/CharIndex.nm +9 -9
  61. package/core/System/Text/Json.nm +60 -46
  62. package/core/System/Text/JsonTree.nm +120 -92
  63. package/core/System/Text/Regex.nm +23 -18
  64. package/core/System/Text/Utf8.nm +5 -5
  65. package/core/System/float.nm +36 -22
  66. package/core/System/int.nm +80 -28
  67. package/core/System/int16.nm +11 -0
  68. package/core/System/int32.nm +11 -0
  69. package/core/System/int64.nm +11 -0
  70. package/core/System/int8.nm +11 -0
  71. package/core/System/uint.nm +11 -0
  72. package/core/System/uint16.nm +11 -0
  73. package/core/System/uint32.nm +11 -0
  74. package/core/System/uint64.nm +11 -0
  75. package/core/System/uint8.nm +11 -0
  76. package/core/docs/System/StringBuilder.md +6 -0
  77. package/core/docs/System/Text/JsonTree.md +3 -2
  78. package/dist/index.mjs +38332 -30604
  79. package/package.json +1 -1
  80. package/src/bench_loop.nm +9 -9
@@ -58,6 +58,30 @@ pub struct ClassBuffer<T> {
58
58
  ```
59
59
  }
60
60
 
61
+ // Address of slot `i` (library-only): the ELEMENT BORROW primitive for
62
+ // trait/class elements — `ref T x = list.at(i)` (docs/HANDLERS.md Stage
63
+ // 4) reaches here through the List wrapper when the element is a trait
64
+ // (the items buffer is a ClassBuffer). ClassBuffer slots are 8-byte
65
+ // handles, so the slot's CONTENT is the instance pointer: return the
66
+ // stored word, not the slot's address. Library-internal: bounds are the
67
+ // caller's obligation (the List wrapper constrains them).
68
+ internal func slot_ptr = (ref self, int i, out ptr T) {
69
+ ```
70
+ #arch: c
71
+ // The slot's address: through the alias, reads and writes hit the
72
+ // stored handle word. void* converts to the caller's `T*` spelling.
73
+ return (void *)&((unsigned long long *)self->data)[i];
74
+ ```
75
+ ```
76
+ #arch: aarch64
77
+ // 8-byte slots: the address of slot i — loading through it yields
78
+ // the stored handle.
79
+ ldr x8, [x19, #8]
80
+ lsl x9, x1, #3
81
+ add x0, x9, x8
82
+ ```
83
+ }
84
+
61
85
  // Generic (T-typed) counterparts of the _int primitives. ClassBuffer only
62
86
  // ever holds 8-byte class pointers (T_SIZE is 8), so these are
63
87
  // behaviorally identical to their _int counterparts — they exist so
@@ -94,6 +118,15 @@ pub struct ClassBuffer<T> {
94
118
  ```
95
119
  }
96
120
 
121
+ // Labeled delegation point for raw-body callers in other containers
122
+ // (StableList's chunked storage calls this): `load` is `inline` — spliced
123
+ // at Nomen call sites, so no label exists for a raw C call or `bl` to
124
+ // target. Mirror of Buffer.load_from (class slots are pointer words —
125
+ // the pointer load IS the borrow form).
126
+ internal func load_from = (self, int i: i >= 0 && i < self.cap, out T) {
127
+ return self.load(i)
128
+ }
129
+
97
130
  // Store the owned class pointer `val` into slot `i`, which MUST be fresh
98
131
  // (calloc'd, or zeroed by mv/move_int). Takes OWNERSHIP of `val`
99
132
  // (`mv`): the slot's #destroy reclaims the instance, so the caller must
@@ -117,6 +150,42 @@ pub struct ClassBuffer<T> {
117
150
  ```
118
151
  }
119
152
 
153
+ // Initialize this buffer as a VIEW over a foreign slab (INTERNAL — the
154
+ // chunked-container primitive): `data` becomes `slab` and `cap` becomes
155
+ // `fill`, and the buffer owns NOTHING — the slab keeps its real owner
156
+ // (never grown, never freed by the view). Mirror of Buffer.init_view;
157
+ // class slots are pointer words.
158
+ internal func init_view = (ref self, uint64 slab, int fill: fill >= 0) {
159
+ self.data = slab
160
+ self.cap = fill
161
+ }
162
+
163
+ // Clear a view initialized by init_view (see Buffer.clear_view): the
164
+ // holder's own field destroy then reclaims nothing.
165
+ internal func clear_view = (ref self) {
166
+ self.data = 0
167
+ self.cap = 0
168
+ }
169
+
170
+ // Store the owned class pointer `val` into slot `slot` of a foreign slab
171
+ // at `slab` (fill `fill`) through this buffer as a transient view —
172
+ // INTERNAL (StableList's directory chunks, class elements): init the
173
+ // view, run the typed `store` (the ownership-transferring slot store),
174
+ // then clear the view so the holder's teardown reclaims nothing. The
175
+ // holder passes a ClassBuffer FIELD, never a local (a local owning buffer
176
+ // is auto-destroyed at scope exit and would free the borrowed slab).
177
+ internal func store_in_slab = (
178
+ ref self,
179
+ uint64 slab,
180
+ int fill: fill >= 0,
181
+ int slot: slot >= 0,
182
+ mv T val
183
+ ) {
184
+ self.init_view(slab, fill)
185
+ self.store(slot, val)
186
+ self.clear_view()
187
+ }
188
+
120
189
  // Move a class pointer out of a slot: return it and zero the slot so the
121
190
  // buffer no longer references it. Declared `mv out T` so the borrow
122
191
  // checker treats the result as OWNED — the slot is relinquished, so the
@@ -254,6 +323,40 @@ pub struct ClassBuffer<T> {
254
323
  ```
255
324
  }
256
325
 
326
+ // Reclaim slot i's owned class pointer (destroy + free) WITHOUT writing
327
+ // the slot — the destroy half of replace_int, without the store, zeroing
328
+ // the handle so a re-destroy is a no-op. The caller (Map.set_slot_ptr's
329
+ // replace path — the value-side sibling of `values.replace`) overwrites
330
+ // the slot immediately afterwards. Contract: the slot must be
331
+ // overwritten before it is read or destroyed again.
332
+ internal func destroy_at = (ref self, int i: i >= 0 && i < self.cap) {
333
+ ```
334
+ #arch: c
335
+ long *slots = (long*)(unsigned long long)self->data;
336
+ if (slots[i]) {
337
+ T_destroy((void*)slots[i]);
338
+ free((void*)slots[i]);
339
+ slots[i] = 0;
340
+ }
341
+ ```
342
+ ```
343
+ #arch: aarch64
344
+ stp x20, x21, [sp, #-16]!
345
+ ldr x20, [x19, #8]
346
+ lsl x3, x1, #3
347
+ add x20, x20, x3
348
+ ldr x0, [x20]
349
+ cbz x0, .Lcb_destroy_at_done
350
+ mov x21, x0
351
+ bl T_destroy
352
+ mov x0, x21
353
+ bl _free
354
+ str xzr, [x20]
355
+ .Lcb_destroy_at_done:
356
+ ldp x20, x21, [sp], #16
357
+ ```
358
+ }
359
+
257
360
  // T-typed alias of replace_int: free the outgoing class pointer (destroy +
258
361
  // free) then store the new one. Used by List.set so the uniform API
259
362
  // preserves ownership for class elements.
@@ -339,6 +442,59 @@ pub struct ClassBuffer<T> {
339
442
  ```
340
443
  }
341
444
 
445
+ // Bulk transfer for backward-shift deletion — the ClassBuffer
446
+ // counterpart of Buffer.move_range (same contract: every slot in
447
+ // [dst, dst + count) must be DEAD — zeroed by a move-out or otherwise
448
+ // unowned — because the block move overwrites without reclaiming; the
449
+ // deletion algorithms zero and relocate in lockstep). Slots are 8-byte
450
+ // instance pointers, so the block is one memmove + one memset of the
451
+ // source slots the destination never covers — List.remove_at_or_panic's
452
+ // whole-tail shift instead of per-element `shift` calls.
453
+ internal inline func move_range = (
454
+ ref self,
455
+ int dst: dst >= 0,
456
+ int src: src >= dst,
457
+ int count: count >= 0 && src + count <= self.cap
458
+ ) {
459
+ ```
460
+ #arch: c
461
+ if (count > 0) {
462
+ long *slots = (long*)(unsigned long long)self->data;
463
+ memmove((void*)&slots[dst], (void*)&slots[src],
464
+ (unsigned long long)count * sizeof(long));
465
+ memset((void*)&slots[dst + count], 0,
466
+ (unsigned long long)(src - dst) * sizeof(long));
467
+ }
468
+ ```
469
+ ```
470
+ #arch: aarch64
471
+ // x19 = self, x1 = dst, x2 = src, x3 = count. x0-x18 die across the
472
+ // bl calls, so the block math parks on the stack around _memmove.
473
+ cbz x3, .Lcb_move_range_T_end
474
+ ldr x4, [x19, #8]
475
+ lsl x5, x1, #3
476
+ add x6, x4, x5
477
+ lsl x5, x2, #3
478
+ add x7, x4, x5
479
+ lsl x9, x3, #3
480
+ sub x10, x2, x1
481
+ lsl x10, x10, #3
482
+ stp x6, x9, [sp, #-16]!
483
+ str x10, [sp, #-16]!
484
+ mov x0, x6
485
+ mov x1, x7
486
+ mov x2, x9
487
+ bl _memmove
488
+ ldr x10, [sp], #16
489
+ ldp x6, x9, [sp], #16
490
+ add x0, x6, x9
491
+ mov x1, #0
492
+ mov x2, x10
493
+ bl _memset
494
+ .Lcb_move_range_T_end:
495
+ ```
496
+ }
497
+
342
498
  // A non-mv (ptr, len) slice [start, end) over this buffer's class
343
499
  // pointers, returned as a `view T` (T is a class, so each element is an
344
500
  // 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
@@ -0,0 +1,326 @@
1
+ /**
2
+ * A system alert dialog (NSAlert)
3
+ *
4
+ * An OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 4 — the shape menu): the
5
+ * single field is a platform handle, so the value is movable and its
6
+ * `#destroy` releases what the handle names. The constructor's `self` is
7
+ * the inline instance (the `.` accessor); methods keep the pointer form.
8
+ *
9
+ * Window-coordinated: `run` enters NSAlert's own modal loop (it blocks the
10
+ * caller and pumps its own events, so it works with the manual loop), and
11
+ * returns the clicked button's index (first button = 0). iOS defers —
12
+ * UIAlertController needs a presenting view controller (TODO stubs; see
13
+ * GUI_PLAN Tier 1 #19).
14
+ **/
15
+ pub mv struct Alert: Control {
16
+ var uint64 handle = 0
17
+
18
+ // The `#handle` hook synthesizes `NameHandle` — a copyable, non-owning
19
+ // twin (same public bare-self methods, no destroy) so closures can
20
+ // capture this control without moving it. See GUI.md, "Handles".
21
+ #handle = () { }
22
+
23
+ pub func #init = (self) {
24
+ ```
25
+ #arch: c
26
+ #platform: macos
27
+ id alert = ((id(*)(id, SEL))objc_msgSend)(
28
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSAlert"), sel_registerName("alloc")),
29
+ sel_registerName("init"));
30
+ self.handle = (uint64_t)alert;
31
+ ```
32
+ ```
33
+ #arch: aarch64_use_c
34
+ #platform: macos
35
+ id alert = ((id(*)(id, SEL))objc_msgSend)(
36
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSAlert"), sel_registerName("alloc")),
37
+ sel_registerName("init"));
38
+ self->handle = (uint64_t)alert;
39
+ ```
40
+ ```
41
+ #arch: c
42
+ #platform: ios
43
+ /* TODO: iOS — UIAlertController needs a presenting view controller
44
+ * (see GUI_PLAN Tier 1 #19; defer the iOS block). */
45
+ ```
46
+ ```
47
+ #arch: aarch64_use_c
48
+ #platform: ios
49
+ /* TODO: iOS — UIAlertController needs a presenting view controller
50
+ * (see GUI_PLAN Tier 1 #19; defer the iOS block). */
51
+ ```
52
+ }
53
+
54
+ // The alert's headline (bold first line).
55
+ pub func set_title = (self, string title) {
56
+ ```
57
+ #arch: c
58
+ #platform: macos
59
+ ((void(*)(id, SEL, id))objc_msgSend)(
60
+ (id)self->handle, sel_registerName("setMessageText:"),
61
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
62
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
63
+ ```
64
+ ```
65
+ #arch: aarch64_use_c
66
+ #platform: macos
67
+ ((void(*)(id, SEL, id))objc_msgSend)(
68
+ (id)self->handle, sel_registerName("setMessageText:"),
69
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
70
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
71
+ ```
72
+ ```
73
+ #arch: c
74
+ #platform: ios
75
+ /* TODO: iOS (see #init) */
76
+ ```
77
+ ```
78
+ #arch: aarch64_use_c
79
+ #platform: ios
80
+ /* TODO: iOS (see #init) */
81
+ ```
82
+ }
83
+
84
+ pub func title = (self, out string) {
85
+ ```
86
+ #arch: c
87
+ #platform: macos
88
+ id str = ((id(*)(id, SEL))objc_msgSend)(
89
+ (id)self->handle, sel_registerName("messageText"));
90
+ if (!str) return (nomen_string){ strdup(""), 0 };
91
+ const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
92
+ str, sel_registerName("UTF8String"));
93
+ return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
94
+ ```
95
+ ```
96
+ #arch: aarch64_use_c
97
+ #platform: macos
98
+ id str = ((id(*)(id, SEL))objc_msgSend)(
99
+ (id)self->handle, sel_registerName("messageText"));
100
+ if (!str) return (nomen_string){ strdup(""), 0 };
101
+ const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
102
+ str, sel_registerName("UTF8String"));
103
+ return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
104
+ ```
105
+ ```
106
+ #arch: c
107
+ #platform: ios
108
+ /* TODO: iOS (see #init) */
109
+ return (nomen_string){ strdup(""), 0 };
110
+ ```
111
+ ```
112
+ #arch: aarch64_use_c
113
+ #platform: ios
114
+ /* TODO: iOS (see #init) */
115
+ return (nomen_string){ strdup(""), 0 };
116
+ ```
117
+ }
118
+
119
+ // The explanatory second line.
120
+ pub func set_message = (self, string message) {
121
+ ```
122
+ #arch: c
123
+ #platform: macos
124
+ ((void(*)(id, SEL, id))objc_msgSend)(
125
+ (id)self->handle, sel_registerName("setInformativeText:"),
126
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
127
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), message.ptr));
128
+ ```
129
+ ```
130
+ #arch: aarch64_use_c
131
+ #platform: macos
132
+ ((void(*)(id, SEL, id))objc_msgSend)(
133
+ (id)self->handle, sel_registerName("setInformativeText:"),
134
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
135
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), message.ptr));
136
+ ```
137
+ ```
138
+ #arch: c
139
+ #platform: ios
140
+ /* TODO: iOS (see #init) */
141
+ ```
142
+ ```
143
+ #arch: aarch64_use_c
144
+ #platform: ios
145
+ /* TODO: iOS (see #init) */
146
+ ```
147
+ }
148
+
149
+ pub func message = (self, out string) {
150
+ ```
151
+ #arch: c
152
+ #platform: macos
153
+ id str = ((id(*)(id, SEL))objc_msgSend)(
154
+ (id)self->handle, sel_registerName("informativeText"));
155
+ if (!str) return (nomen_string){ strdup(""), 0 };
156
+ const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
157
+ str, sel_registerName("UTF8String"));
158
+ return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
159
+ ```
160
+ ```
161
+ #arch: aarch64_use_c
162
+ #platform: macos
163
+ id str = ((id(*)(id, SEL))objc_msgSend)(
164
+ (id)self->handle, sel_registerName("informativeText"));
165
+ if (!str) return (nomen_string){ strdup(""), 0 };
166
+ const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
167
+ str, sel_registerName("UTF8String"));
168
+ return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
169
+ ```
170
+ ```
171
+ #arch: c
172
+ #platform: ios
173
+ /* TODO: iOS (see #init) */
174
+ return (nomen_string){ strdup(""), 0 };
175
+ ```
176
+ ```
177
+ #arch: aarch64_use_c
178
+ #platform: ios
179
+ /* TODO: iOS (see #init) */
180
+ return (nomen_string){ strdup(""), 0 };
181
+ ```
182
+ }
183
+
184
+ // Append a button (left to right). The FIRST button is the default
185
+ // (Return); the clicked one's index is what `run` returns. A fresh
186
+ // NSAlert carries one default button ("OK") — the first
187
+ // `add_button` REPLACES it, so the count stays what the app adds.
188
+ pub func add_button = (self, string title) {
189
+ ```
190
+ #arch: c
191
+ #platform: macos
192
+ ((void(*)(id, SEL, id))objc_msgSend)(
193
+ (id)self->handle, sel_registerName("addButtonWithTitle:"),
194
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
195
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
196
+ ```
197
+ ```
198
+ #arch: aarch64_use_c
199
+ #platform: macos
200
+ ((void(*)(id, SEL, id))objc_msgSend)(
201
+ (id)self->handle, sel_registerName("addButtonWithTitle:"),
202
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
203
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
204
+ ```
205
+ ```
206
+ #arch: c
207
+ #platform: ios
208
+ /* TODO: iOS (see #init) */
209
+ ```
210
+ ```
211
+ #arch: aarch64_use_c
212
+ #platform: ios
213
+ /* TODO: iOS (see #init) */
214
+ ```
215
+ }
216
+
217
+ // How many buttons the alert carries.
218
+ pub func button_count = (self, out int) {
219
+ ```
220
+ #arch: c
221
+ #platform: macos
222
+ id buttons = ((id(*)(id, SEL))objc_msgSend)(
223
+ (id)self->handle, sel_registerName("buttons"));
224
+ return (int)((long(*)(id, SEL))objc_msgSend)(buttons, sel_registerName("count"));
225
+ ```
226
+ ```
227
+ #arch: aarch64_use_c
228
+ #platform: macos
229
+ id buttons = ((id(*)(id, SEL))objc_msgSend)(
230
+ (id)self->handle, sel_registerName("buttons"));
231
+ return (int)((long(*)(id, SEL))objc_msgSend)(buttons, sel_registerName("count"));
232
+ ```
233
+ ```
234
+ #arch: c
235
+ #platform: ios
236
+ /* TODO: iOS (see #init) */
237
+ return 0;
238
+ ```
239
+ ```
240
+ #arch: aarch64_use_c
241
+ #platform: ios
242
+ /* TODO: iOS (see #init) */
243
+ return 0;
244
+ ```
245
+ }
246
+
247
+ // Show the alert modally and return the clicked button's index (0 =
248
+ // first button). BLOCKS in NSAlert's own modal loop until dismissed —
249
+ // call it from the event thread when the manual loop can afford to
250
+ // pause (NSAlert pumps its own events, so the UI stays live).
251
+ pub func run = (self, out int) {
252
+ ```
253
+ #arch: c
254
+ #platform: macos
255
+ long code = ((long(*)(id, SEL))objc_msgSend)(
256
+ (id)self->handle, sel_registerName("runModal"));
257
+ return (int)(code - 1000); // NSModalResponseOK = 1000 + button index
258
+ ```
259
+ ```
260
+ #arch: aarch64_use_c
261
+ #platform: macos
262
+ long code = ((long(*)(id, SEL))objc_msgSend)(
263
+ (id)self->handle, sel_registerName("runModal"));
264
+ return (int)(code - 1000); // NSModalResponseOK = 1000 + button index
265
+ ```
266
+ ```
267
+ #arch: c
268
+ #platform: ios
269
+ /* TODO: iOS (see #init) */
270
+ return 0;
271
+ ```
272
+ ```
273
+ #arch: aarch64_use_c
274
+ #platform: ios
275
+ /* TODO: iOS (see #init) */
276
+ return 0;
277
+ ```
278
+ }
279
+
280
+ // ── Control trait conformance ──
281
+ // An alert is a modal, not an in-window view: it measures to nothing and
282
+ // the layout engine never frames it. The conformance exists so an Alert
283
+ // can sit in a Control-typed slot without special-casing.
284
+ pub func set_frame = (ref self, int x, int y, int width, int height) {
285
+ // No-op: modals are positioned by the window server.
286
+ }
287
+
288
+ pub func intrinsic_size = (self, out Size) {
289
+ var s = Size()
290
+ return s
291
+ }
292
+
293
+ pub func measure = (self, BoxConstraints constraints, out Size) {
294
+ var s = Size()
295
+ return s
296
+ }
297
+
298
+ func #destroy = () {
299
+ ```
300
+ #arch: c
301
+ #platform: macos
302
+ if (self->handle) {
303
+ ((void(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("release"));
304
+ self->handle = 0;
305
+ }
306
+ ```
307
+ ```
308
+ #arch: aarch64_use_c
309
+ #platform: macos
310
+ if (self->handle) {
311
+ ((void(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("release"));
312
+ self->handle = 0;
313
+ }
314
+ ```
315
+ ```
316
+ #arch: c
317
+ #platform: ios
318
+ /* TODO: iOS (see #init) */
319
+ ```
320
+ ```
321
+ #arch: aarch64_use_c
322
+ #platform: ios
323
+ /* TODO: iOS (see #init) */
324
+ ```
325
+ }
326
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * The App contract: the object Window.run drives
3
+ *
4
+ * The WinForms/Swift shape for a manual-loop toolkit: instead of pumping
5
+ * `Window.process_events` and re-checking sizes in a `while` loop, the app
6
+ * declares a struct conforming to `App`, moves it into `win.run(mv app)`,
7
+ * and the WINDOW owns everything else — the event pump, the autorelease
8
+ * pool, resize detection, and the relayout of the hosted content (the root
9
+ * `Container` from `win.set_content` plus every `Scrollable.set_content`
10
+ * document). The app implements four hooks:
11
+ *
12
+ * - `start` — called once, after the app has been moved into its final
13
+ * storage but before the window is shown. Register the layout roots here
14
+ * (`win.set_content(ref grid)`, `scroll.set_content(ref grid)`) and do
15
+ * any wiring that needs stable addresses — including click handlers
16
+ * (`self.btn.on_click(func () { ... })`).
17
+ * - `frame` — called every iteration of the run loop with a `FrameEvents`
18
+ * value: the full input record for this frame (click kind/coordinates,
19
+ * `mouse_button`/`mouse_click_count`/`mouse_modifiers` — every button,
20
+ * `scroll_dx`/`scroll_dy`, the keyboard record
21
+ * `key_code`/`key_modifiers`/`key_repeat`/`key_up_code`, the pointer
22
+ * position `mouse_x`/`mouse_y`, and frame time `dt`), and `resized`
23
+ * + the new size when the window's content rect changed. Per-frame value
24
+ * polling (slider → progress mirrors), coordinate-driven input that no
25
+ * event names, and app-owned pane relayouts live here — registered
26
+ * click, right-click, mouse-down, scroll, key, move, and resize handlers
27
+ * dispatch earlier, in `handle_events` (the `.on_click` /
28
+ * `.on_right_click` / `.on_mouse_down` / `.on_scroll` / `.on_key_down` /
29
+ * `.on_key_up` / `.on_mouse_move` / `.on_resize` desugars), and close
30
+ * requests consult `close_requested`.
31
+ * - `handle_events` — the per-frame event pump: drains every registered
32
+ * control event (clicks, focus/blur, text input). Declared here as a
33
+ * default-empty method; the `.on_<event>(closure)` desugars
34
+ * (docs/HANDLERS.md) synthesize the override on conformers with
35
+ * registrations, so user code only writes the registration and never
36
+ * sees the machinery. New control events extend the pump's arms — the
37
+ * App contract is fixed.
38
+ * - `run_handler` — the up-call protocol (Stage 3): a composite with an
39
+ * enrolled event calls back `owner.run_handler(id, ref win)` from its
40
+ * `tick` (the app forwards itself down once per tick). Default-empty;
41
+ * the desugar synthesizes the override dispatching ids to the app's
42
+ * `.on_<event>` handlers.
43
+ *
44
+ * Handlers are METHODS on the app (the `ref self` receiver), so app state —
45
+ * every control field — is reachable without lambda captures (capturing a
46
+ * control MOVES it; a capture-free method reference moves nothing). The
47
+ * `.on_click` desugar is that shape with registration ergonomics: the
48
+ * closure you write IS hoisted into a hidden method, and the dispatch
49
+ * borrow of `self` is taken by the run loop per frame.
50
+ **/
51
+ pub trait App {
52
+ // Called once before the window is shown, after the app is in its
53
+ // final storage. Register layout roots here.
54
+ func start = (ref self, ref Window win) {
55
+ }
56
+
57
+ // Called every frame until the window closes.
58
+ func frame = (ref self, ref Window win, FrameEvents ev) {
59
+ }
60
+
61
+ // The per-frame event pump: drains every registered control event —
62
+ // clicks, focus/blur, text input — through arms the `.on_<event>`
63
+ // desugar (docs/HANDLERS.md) synthesizes on conformers with
64
+ // registrations: one hidden method per closure, one drain arm per
65
+ // registered control, one event-source prologue per family.
66
+ // `Window.run` calls it before `frame`. New control events are new arms
67
+ // in this one slot — the App contract never grows with the event
68
+ // catalog. Conformers without registrations inherit this empty default
69
+ // (trait defaults have precedent: `Enumerable.length`).
70
+ func handle_events = (ref self, ref Window win) {
71
+ }
72
+
73
+ // The up-call protocol (docs/HANDLERS.md Stage 3): a composite the app
74
+ // has enrolled events on calls back into this — `owner.run_handler(id,
75
+ // ref win)` from its `tick` — one vtable hop into the desugar's
76
+ // synthesized override, whose arms dispatch the id to the app's handler.
77
+ // The borrow flows down per call; nothing outlives it. Default-empty,
78
+ // like `handle_events`.
79
+ func run_handler = (ref self, int id, ref Window win) {
80
+ }
81
+
82
+ // Close interception (the unsaved-changes pattern): run consults this
83
+ // on the frame after the user requests close (the red button — the
84
+ // delegate refuses the native close and sets a pending flag). Return
85
+ // false to refuse: the window stays, the flag is cleared, and a later
86
+ // close request consults again. Programmatic `win.close()` is FORCED
87
+ // and bypasses this — handlers closing their own window never trip the
88
+ // interception. Default: allow.
89
+ func close_requested = (ref self, ref Window win, out bool) {
90
+ return true
91
+ }
92
+ }
93
+
94
+ /**
95
+ * One frame's input state, passed to App.frame
96
+ *
97
+ * Pure scalars, so the by-value parameter copies safely (no destroy).
98
+ * Click coordinates are the window's content coordinates, top-left origin
99
+ * (the same values `Window.click_x`/`click_y` report) — translate through
100
+ * `content_click` before hit-testing controls hosted on a Scrollable.
101
+ **/
102
+ pub mv struct FrameEvents {
103
+ // 0 = no mouse-down this frame, 1 = left, 2 = right (edge-triggered:
104
+ // only the frame that pumped the event reports it).
105
+ pub var click = 0
106
+ pub var int click_x = -1
107
+ pub var int click_y = -1
108
+ // The full mouse record for this frame (0 = no mouse-down): the 1-based
109
+ // button number (1 left, 2 right, 3 middle, 4+ extra buttons), the
110
+ // AppKit click count (2 = double-click), and the device-independent
111
+ // modifier flags (Command = 0x100000, Option = 0x80000, Control =
112
+ // 0x40000, Shift = 0x20000). The same values the `win.mouse_button()` /
113
+ // `mouse_click_count()` / `mouse_modifiers()` getters report to
114
+ // `on_mouse_down` handlers. `click` remains the left/right compat view.
115
+ pub var mouse_button = 0
116
+ pub var mouse_click_count = 0
117
+ pub var mouse_modifiers = 0
118
+ // This frame's accumulated scroll-wheel deltas (dy positive scrolling
119
+ // up/away, dx positive right; trackpad frames sum every small event).
120
+ // `Scrollable.on_scroll` is the registered-handler form.
121
+ pub var scroll_dx = 0
122
+ pub var scroll_dy = 0
123
+ // The keyboard record for this frame (-1 = no key this frame): the
124
+ // hardware keycode of the last key-down (Space = 49, Return = 36,
125
+ // arrows = 123–126, letters a…z = 0–25), the modifiers at key-down,
126
+ // whether it was an auto-repeat, and the keycode of the last key-up.
127
+ // `on_key_down` / `on_key_up` are the registered-handler forms; text
128
+ // ENTRY flows through `on_input` (the field editor), not here.
129
+ pub var int key_code = -1
130
+ pub var key_modifiers = 0
131
+ pub var key_repeat = false
132
+ pub var int key_up_code = -1
133
+ // The pointer-MOVE record for this frame (-1 = no movement): the
134
+ // current pointer position. `on_mouse_move` is the registered-handler
135
+ // form; hover/tooltips hit-test this against Container.contains.
136
+ pub var int mouse_x = -1
137
+ pub var int mouse_y = -1
138
+ // Milliseconds since the previous frame (0 on the first) — the
139
+ // animation clock. `win.dt()` reports the same value.
140
+ pub var dt = 0
141
+ // True when the window's content rect changed since the last frame —
142
+ // the framework has already re-laid-out the registered roots; re-layout
143
+ // App-owned panes (tab pages, split panes) here.
144
+ pub var resized = false
145
+ pub var width = 0
146
+ pub var height = 0
147
+ }