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
@@ -94,32 +94,47 @@ func length_val = (LayoutLength len, out int) {
94
94
  // methods below reference them (free functions aren't forward-inferred).
95
95
 
96
96
  func is_visible = (uint64 handle, out bool) {
97
- if handle == 0 { return true }
97
+ // The handle-0 guard lives INSIDE every arch block: a Nomen statement
98
+ // around `aarch64_use_c` blocks is rejected (the aarch64 backend replaces
99
+ // the whole body with the raw blocks and would silently drop it).
98
100
  ```
99
101
  #arch: c
100
102
  #platform: macos
103
+ if (handle == 0) return true;
101
104
  BOOL hidden = ((BOOL(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("isHidden"));
102
105
  return !hidden;
103
106
  ```
104
107
  ```
105
108
  #arch: aarch64_use_c
106
109
  #platform: macos
110
+ if (handle == 0) return true;
107
111
  BOOL hidden = ((BOOL(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("isHidden"));
108
112
  return !hidden;
109
113
  ```
110
114
  ```
111
115
  #arch: c
112
116
  #platform: ios
117
+ if (handle == 0) return true;
113
118
  return true;
114
119
  ```
115
120
  ```
116
121
  #arch: aarch64_use_c
117
122
  #platform: ios
123
+ if (handle == 0) return true;
118
124
  return true;
119
125
  ```
120
126
  }
121
127
 
122
- func apply_frame = (uint64 handle, int x, int y, int w, int h, int content_w, int content_h) {
128
+ func apply_frame = (
129
+ uint64 handle,
130
+ int x,
131
+ int y,
132
+ int w,
133
+ int h,
134
+ int content_w,
135
+ int content_h,
136
+ int flex
137
+ ) {
123
138
  ```
124
139
  #arch: c
125
140
  #platform: macos
@@ -129,12 +144,18 @@ func apply_frame = (uint64 handle, int x, int y, int w, int h, int content_w, in
129
144
  // vertical edge (top=NSViewMinYMargin 8 / bottom=NSViewMaxYMargin 32) to
130
145
  // respect the top-left-origin layout instead of AppKit's default
131
146
  // bottom-left pin, and let wide controls grow with the content width
132
- // (NSViewWidthSizable 2). Never set NSViewHeightSizable (16): control
133
- // heights come from the layout pass, not the window. AppKit's result
147
+ // (NSViewWidthSizable 2). A GROW-driven leaf is the exception: it is the
148
+ // layout's flexible region, so it gets the matching sizable bit (see the
149
+ // `flex` argument) and resizes live with the window; every other control's
150
+ // height comes from the layout pass, not the window. AppKit's result
134
151
  // matches what layout() computes, so the post-drag relayout only fixes up
135
152
  // cells AppKit can't (column reflow for sub-full-width children).
136
153
  unsigned long mask = (y * 2 < content_h) ? 8UL : 32UL;
137
154
  if (w * 10 >= content_w * 8) mask |= 2UL;
155
+ // flex: 1 = width-flexible (HStack grow), 2 = height-flexible (VStack
156
+ // grow) — the region AppKit resizes live during a window drag.
157
+ if (flex == 1) mask |= 2UL;
158
+ if (flex == 2) mask |= 16UL;
138
159
  ((void(*)(id, SEL, unsigned long))objc_msgSend)((id)handle, sel_registerName("setAutoresizingMask:"), mask);
139
160
  ((void(*)(id, SEL, CGRect))objc_msgSend)(
140
161
  (id)handle, sel_registerName("setFrame:"),
@@ -150,12 +171,18 @@ func apply_frame = (uint64 handle, int x, int y, int w, int h, int content_w, in
150
171
  // vertical edge (top=NSViewMinYMargin 8 / bottom=NSViewMaxYMargin 32) to
151
172
  // respect the top-left-origin layout instead of AppKit's default
152
173
  // bottom-left pin, and let wide controls grow with the content width
153
- // (NSViewWidthSizable 2). Never set NSViewHeightSizable (16): control
154
- // heights come from the layout pass, not the window. AppKit's result
174
+ // (NSViewWidthSizable 2). A GROW-driven leaf is the exception: it is the
175
+ // layout's flexible region, so it gets the matching sizable bit (see the
176
+ // `flex` argument) and resizes live with the window; every other control's
177
+ // height comes from the layout pass, not the window. AppKit's result
155
178
  // matches what layout() computes, so the post-drag relayout only fixes up
156
179
  // cells AppKit can't (column reflow for sub-full-width children).
157
180
  unsigned long mask = (y * 2 < content_h) ? 8UL : 32UL;
158
181
  if (w * 10 >= content_w * 8) mask |= 2UL;
182
+ // flex: 1 = width-flexible (HStack grow), 2 = height-flexible (VStack
183
+ // grow) — the region AppKit resizes live during a window drag.
184
+ if (flex == 1) mask |= 2UL;
185
+ if (flex == 2) mask |= 16UL;
159
186
  ((void(*)(id, SEL, unsigned long))objc_msgSend)((id)handle, sel_registerName("setAutoresizingMask:"), mask);
160
187
  ((void(*)(id, SEL, CGRect))objc_msgSend)(
161
188
  (id)handle, sel_registerName("setFrame:"),
@@ -191,10 +218,18 @@ func apply_frame = (uint64 handle, int x, int y, int w, int h, int content_w, in
191
218
  // conformance (`Button`/`CheckBox`/`TextBox`), so each leaf's `intrinsic_size`
192
219
  // doesn't need its own raw-block FFI.
193
220
  pub func native_intrinsic = (uint64 handle, out [int, int]) {
194
- if handle == 0 { return [0, 0] }
221
+ // The handle-0 guard lives INSIDE every arch block: a Nomen statement
222
+ // around `aarch64_use_c` blocks is rejected (the aarch64 backend replaces
223
+ // the whole body with the raw blocks and would silently drop it).
195
224
  ```
196
225
  #arch: c
197
226
  #platform: macos
227
+ if (handle == 0) {
228
+ struct _Tuple_int_int r;
229
+ r._0 = 0;
230
+ r._1 = 0;
231
+ return r;
232
+ }
198
233
  CGSize sz = ((CGSize(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("intrinsicContentSize"));
199
234
  int w = (sz.width < 0 || sz.width > 100000) ? 0 : (int)sz.width;
200
235
  int h = (sz.height < 0 || sz.height > 100000) ? 0 : (int)sz.height;
@@ -206,6 +241,12 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
206
241
  ```
207
242
  #arch: aarch64_use_c
208
243
  #platform: macos
244
+ if (handle == 0) {
245
+ struct _Tuple_int_int r;
246
+ r._0 = 0;
247
+ r._1 = 0;
248
+ return r;
249
+ }
209
250
  CGSize sz = ((CGSize(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("intrinsicContentSize"));
210
251
  int w = (sz.width < 0 || sz.width > 100000) ? 0 : (int)sz.width;
211
252
  int h = (sz.height < 0 || sz.height > 100000) ? 0 : (int)sz.height;
@@ -217,6 +258,12 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
217
258
  ```
218
259
  #arch: c
219
260
  #platform: ios
261
+ if (handle == 0) {
262
+ struct _Tuple_int_int r;
263
+ r._0 = 0;
264
+ r._1 = 0;
265
+ return r;
266
+ }
220
267
  CGSize sz = ((CGSize(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("intrinsicContentSize"));
221
268
  int w = (sz.width < 0 || sz.width > 100000) ? 0 : (int)sz.width;
222
269
  int h = (sz.height < 0 || sz.height > 100000) ? 0 : (int)sz.height;
@@ -228,6 +275,12 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
228
275
  ```
229
276
  #arch: aarch64_use_c
230
277
  #platform: ios
278
+ if (handle == 0) {
279
+ struct _Tuple_int_int r;
280
+ r._0 = 0;
281
+ r._1 = 0;
282
+ return r;
283
+ }
231
284
  CGSize sz = ((CGSize(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("intrinsicContentSize"));
232
285
  int w = (sz.width < 0 || sz.width > 100000) ? 0 : (int)sz.width;
233
286
  int h = (sz.height < 0 || sz.height > 100000) ? 0 : (int)sz.height;
@@ -238,6 +291,60 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
238
291
  ```
239
292
  }
240
293
 
294
+ // The NATIVE frame of a control handle as "x,y,w,h" (AppKit's answer, not the
295
+ // layout engine's intent — compare against `Container.fmt_frame` to verify the
296
+ // two agree). Returns "0,0,0,0" for handle 0.
297
+ pub func native_frame = (uint64 handle, out string) {
298
+ // The handle-0 guard lives INSIDE every arch block: a Nomen statement
299
+ // around `aarch64_use_c` blocks is rejected (the aarch64 backend replaces
300
+ // the whole body with the raw blocks and would silently drop it). The
301
+ // string RETURN builds the fat pair itself — the literal's length is 7.
302
+ ```
303
+ #arch: c
304
+ #platform: macos
305
+ if (handle == 0) return (nomen_string){ (char*)"0,0,0,0", 7 };
306
+ CGRect r = ((CGRect(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("frame"));
307
+ char buf[96];
308
+ snprintf(buf, sizeof(buf), "%ld,%ld,%ld,%ld",
309
+ (long)r.origin.x, (long)r.origin.y, (long)r.size.width, (long)r.size.height);
310
+ char *dup = strdup(buf);
311
+ return (nomen_string){ dup, (long)strlen(dup) };
312
+ ```
313
+ ```
314
+ #arch: aarch64_use_c
315
+ #platform: macos
316
+ if (handle == 0) return (nomen_string){ (char*)"0,0,0,0", 7 };
317
+ CGRect r = ((CGRect(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("frame"));
318
+ char buf[96];
319
+ snprintf(buf, sizeof(buf), "%ld,%ld,%ld,%ld",
320
+ (long)r.origin.x, (long)r.origin.y, (long)r.size.width, (long)r.size.height);
321
+ char *dup = strdup(buf);
322
+ return (nomen_string){ dup, (long)strlen(dup) };
323
+ ```
324
+ ```
325
+ #arch: c
326
+ #platform: ios
327
+ if (handle == 0) return (nomen_string){ (char*)"0,0,0,0", 7 };
328
+ CGRect r = ((CGRect(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("frame"));
329
+ char buf[96];
330
+ snprintf(buf, sizeof(buf), "%ld,%ld,%ld,%ld",
331
+ (long)r.origin.x, (long)r.origin.y, (long)r.size.width, (long)r.size.height);
332
+ char *dup = strdup(buf);
333
+ return (nomen_string){ dup, (long)strlen(dup) };
334
+ ```
335
+ ```
336
+ #arch: aarch64_use_c
337
+ #platform: ios
338
+ if (handle == 0) return (nomen_string){ (char*)"0,0,0,0", 7 };
339
+ CGRect r = ((CGRect(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("frame"));
340
+ char buf[96];
341
+ snprintf(buf, sizeof(buf), "%ld,%ld,%ld,%ld",
342
+ (long)r.origin.x, (long)r.origin.y, (long)r.size.width, (long)r.size.height);
343
+ char *dup = strdup(buf);
344
+ return (nomen_string){ dup, (long)strlen(dup) };
345
+ ```
346
+ }
347
+
241
348
  /**
242
349
  * A flexbox-style container that measures and arranges its child controls.
243
350
  *
@@ -251,7 +358,7 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
251
358
  * so a `Container` can also be driven polymorphically as a `Control` (passed to
252
359
  * a `Control`-taking function, stored in a `ClassBuffer<Control>`, etc.).
253
360
  **/
254
- pub mv struct Container : Control {
361
+ pub mv struct Container: Control {
255
362
  var kinds = Buffer<int>()
256
363
  // Per-leaf requested size, stored as a (kind, value) pair per axis. The
257
364
  // kind is one of LEN_* (auto/fixed/percent/fill); the value is the pixel
@@ -292,6 +399,13 @@ pub mv struct Container : Control {
292
399
  var pry = Buffer<int>()
293
400
  var prw = Buffer<int>()
294
401
  var prh = Buffer<int>()
402
+ // The constraints each node's cached size was measured under (the
403
+ // measure_node cache gate). A clean node re-uses its cached size only
404
+ // when the constraints are unchanged — otherwise a standalone
405
+ // `measure(bc)` after an external resize answered with the PREVIOUS
406
+ // constraint's width (the page-scroll shrink-per-resize bug).
407
+ var mw = Buffer<int>()
408
+ var mh = Buffer<int>()
295
409
  // Dirty-rect list: the node indices (leaves) whose frame changed in the
296
410
  // most recent `compute`/`layout`, in the order discovered. `dirty_count`
297
411
  // / `dirty_rect` expose it; `apply` repaints only these.
@@ -331,6 +445,8 @@ pub mv struct Container : Control {
331
445
  self.pry.grow_int(cap)
332
446
  self.prw.grow_int(cap)
333
447
  self.prh.grow_int(cap)
448
+ self.mw.grow_int(cap)
449
+ self.mh.grow_int(cap)
334
450
  self.dirty_idx.grow_int(cap)
335
451
  }
336
452
 
@@ -355,6 +471,8 @@ pub mv struct Container : Control {
355
471
  self.pry.store_int(0, -1)
356
472
  self.prw.store_int(0, -1)
357
473
  self.prh.store_int(0, -1)
474
+ self.mw.store_int(0, -1)
475
+ self.mh.store_int(0, -1)
358
476
  self.root = 0
359
477
  self.count = 1
360
478
  }
@@ -432,6 +550,8 @@ pub mv struct Container : Control {
432
550
  self.pry.store_int(idx, -1)
433
551
  self.prw.store_int(idx, -1)
434
552
  self.prh.store_int(idx, -1)
553
+ self.mw.store_int(idx, -1)
554
+ self.mh.store_int(idx, -1)
435
555
  self.count = self.count + 1
436
556
  return idx
437
557
  }
@@ -676,6 +796,84 @@ pub mv struct Container : Control {
676
796
  self.set_leaf_kind(idx, LEN_INTRINSIC, 0, LEN_INTRINSIC, 0)
677
797
  }
678
798
 
799
+ // Remove the leaf whose native handle matches, deleting its node from the
800
+ // tree: every per-node array is backward-shifted over the slot and parent
801
+ // indices above it are fixed up, so the node vanishes from measure,
802
+ // arrange, hit-test, and apply entirely. Call this BEFORE freeing the
803
+ // control itself (its #destroy, whenever the owner's scope ends) — a
804
+ // freed handle left in the tree would still be measured, hit-tested, and
805
+ // re-framed. The whole tree is marked dirty afterwards so the next
806
+ // layout reflows the rows the removed child leaves behind. Returns true
807
+ // if a matching leaf was found and removed.
808
+ pub func remove_leaf = (ref self, uint64 handle, out bool) {
809
+ var int needle = handle as int
810
+ var removed = false
811
+ var idx = 1
812
+ while idx < self.count; idx += 1 {
813
+ if self.kinds.load_int(idx) == KIND_LEAF {
814
+ if self.handles.load_int(idx) == needle {
815
+ removed = true
816
+ break
817
+ }
818
+ }
819
+ }
820
+ if !removed {
821
+ return false
822
+ }
823
+ // Backward-shift every per-node array over the removed slot. The
824
+ // cached frames (rx..prh) shift along with their nodes, so the tree
825
+ // stays internally consistent — only the structure changed. `idx`
826
+ // holds the removed slot here; remember it for the parent fix-up,
827
+ // then walk the shift destinations with it.
828
+ var int hole = idx
829
+ var int last = self.count - 1
830
+ while idx < last; idx += 1 {
831
+ self.kinds.store_int(idx, self.kinds.load_int(idx + 1))
832
+ self.w_kinds.store_int(idx, self.w_kinds.load_int(idx + 1))
833
+ self.w_vals.store_int(idx, self.w_vals.load_int(idx + 1))
834
+ self.h_kinds.store_int(idx, self.h_kinds.load_int(idx + 1))
835
+ self.h_vals.store_int(idx, self.h_vals.load_int(idx + 1))
836
+ self.gaps.store_int(idx, self.gaps.load_int(idx + 1))
837
+ self.pars.store_int(idx, self.pars.load_int(idx + 1))
838
+ self.spans.store_int(idx, self.spans.load_int(idx + 1))
839
+ self.cols.store_int(idx, self.cols.load_int(idx + 1))
840
+ self.handles.store_int(idx, self.handles.load_int(idx + 1))
841
+ self.pads.store_int(idx, self.pads.load_int(idx + 1))
842
+ self.grows.store_int(idx, self.grows.load_int(idx + 1))
843
+ self.shrinks.store_int(idx, self.shrinks.load_int(idx + 1))
844
+ self.aligns.store_int(idx, self.aligns.load_int(idx + 1))
845
+ self.dirty.store_int(idx, self.dirty.load_int(idx + 1))
846
+ self.measured_count.store_int(idx, self.measured_count.load_int(idx + 1))
847
+ self.rx.store_int(idx, self.rx.load_int(idx + 1))
848
+ self.ry.store_int(idx, self.ry.load_int(idx + 1))
849
+ self.rw.store_int(idx, self.rw.load_int(idx + 1))
850
+ self.rh.store_int(idx, self.rh.load_int(idx + 1))
851
+ self.prx.store_int(idx, self.prx.load_int(idx + 1))
852
+ self.pry.store_int(idx, self.pry.load_int(idx + 1))
853
+ self.prw.store_int(idx, self.prw.load_int(idx + 1))
854
+ self.prh.store_int(idx, self.prh.load_int(idx + 1))
855
+ self.mw.store_int(idx, self.mw.load_int(idx + 1))
856
+ self.mh.store_int(idx, self.mh.load_int(idx + 1))
857
+ }
858
+ self.count = last
859
+ // Fix parent indices that pointed above the removed slot. (A node
860
+ // parented AT the removed slot is impossible — leaves have no
861
+ // children.)
862
+ var k = 1
863
+ while k < self.count; k += 1 {
864
+ if self.pars.load_int(k) > hole + 1 {
865
+ self.pars.store_int(k, self.pars.load_int(k) - 1)
866
+ }
867
+ }
868
+ // The dirty-rect list holds node indices; the next layout's
869
+ // collect_dirty rebuilds it from scratch, so drop the stale entries.
870
+ self.dirty_n = 0
871
+ // The row structure changed: re-measure everything on the next
872
+ // compute/layout.
873
+ self.mark_dirty(self.root)
874
+ return true
875
+ }
876
+
679
877
  // Translate the legacy int (w, h) into a (kind, value) pair: 0 → auto
680
878
  // (no intrinsic; fills a bounded axis, stays 0 on an unbounded axis so
681
879
  // `grow` decides), non-zero → fixed.
@@ -824,7 +1022,7 @@ pub mv struct Container : Control {
824
1022
  // reuse its cached size instead of re-measuring the subtree. The cached
825
1023
  // height is already in rh[idx] from the previous pass, so the caller's
826
1024
  // `self.rh.load_int(ci)` picks it up without a re-measure.
827
- if self.dirty.load_int(idx) == 0 {
1025
+ if self.dirty.load_int(idx) == 0 && self.mw.load_int(idx) == max_w && self.mh.load_int(idx) == max_h {
828
1026
  var int cached = self.rw.load_int(idx)
829
1027
  return cached
830
1028
  }
@@ -879,6 +1077,8 @@ pub mv struct Container : Control {
879
1077
  if h < min_h { h = min_h }
880
1078
  self.rw.store_int(idx, w)
881
1079
  self.rh.store_int(idx, h)
1080
+ self.mw.store_int(idx, max_w)
1081
+ self.mh.store_int(idx, max_h)
882
1082
  self.dirty.store_int(idx, 0)
883
1083
  return w
884
1084
  }
@@ -905,6 +1105,8 @@ pub mv struct Container : Control {
905
1105
  if max_child_w > max_w { max_child_w = max_w }
906
1106
  self.rw.store_int(idx, max_child_w)
907
1107
  self.rh.store_int(idx, total_h)
1108
+ self.mw.store_int(idx, max_w)
1109
+ self.mh.store_int(idx, max_h)
908
1110
  self.dirty.store_int(idx, 0)
909
1111
  return max_child_w
910
1112
  }
@@ -931,6 +1133,8 @@ pub mv struct Container : Control {
931
1133
  if max_child_h > max_h { max_child_h = max_h }
932
1134
  self.rw.store_int(idx, total_w)
933
1135
  self.rh.store_int(idx, max_child_h)
1136
+ self.mw.store_int(idx, max_w)
1137
+ self.mh.store_int(idx, max_h)
934
1138
  self.dirty.store_int(idx, 0)
935
1139
  return total_w
936
1140
  }
@@ -955,6 +1159,8 @@ pub mv struct Container : Control {
955
1159
  if best_h > max_h { best_h = max_h }
956
1160
  self.rw.store_int(idx, best_w)
957
1161
  self.rh.store_int(idx, best_h)
1162
+ self.mw.store_int(idx, max_w)
1163
+ self.mh.store_int(idx, max_h)
958
1164
  self.dirty.store_int(idx, 0)
959
1165
  return best_w
960
1166
  }
@@ -988,6 +1194,8 @@ pub mv struct Container : Control {
988
1194
  if total_h > max_h { total_h = max_h }
989
1195
  self.rw.store_int(idx, total_w)
990
1196
  self.rh.store_int(idx, total_h)
1197
+ self.mw.store_int(idx, max_w)
1198
+ self.mh.store_int(idx, max_h)
991
1199
  self.dirty.store_int(idx, 0)
992
1200
  return total_w
993
1201
  }
@@ -1027,6 +1235,8 @@ pub mv struct Container : Control {
1027
1235
  if total_h > max_h { total_h = max_h }
1028
1236
  self.rw.store_int(idx, cols * col_w)
1029
1237
  self.rh.store_int(idx, total_h)
1238
+ self.mw.store_int(idx, max_w)
1239
+ self.mh.store_int(idx, max_h)
1030
1240
  self.dirty.store_int(idx, 0)
1031
1241
  return cols * col_w
1032
1242
  }
@@ -1330,6 +1540,26 @@ pub mv struct Container : Control {
1330
1540
  var int i = self.dirty_idx.load_int(k)
1331
1541
  var uint64 handle = self.handles.load_int(i) as uint64
1332
1542
  if handle != 0 {
1543
+ // A grow-driven leaf IS the layout's flexible region: in a
1544
+ // VStack it absorbs a vertical window resize, in an HStack a
1545
+ // horizontal one. Expose that as the matching autoresizing
1546
+ // mask so AppKit resizes it LIVE during the drag (the engine's
1547
+ // relayout can't run mid-drag — heights otherwise only change
1548
+ // when the delegate fires or the drag ends, leaving the
1549
+ // flexible region sitting still under the pointer).
1550
+ var flex = 0
1551
+ if self.grows.load_int(i) > 0 {
1552
+ var int parent = self.pars.load_int(i) - 1
1553
+ if parent != -1 {
1554
+ var int pkind = self.kinds.load_int(parent)
1555
+ if pkind == KIND_VSTACK {
1556
+ flex = 2
1557
+ }
1558
+ if pkind == KIND_HSTACK {
1559
+ flex = 1
1560
+ }
1561
+ }
1562
+ }
1333
1563
  apply_frame(
1334
1564
  handle,
1335
1565
  self.rx.load_int(i),
@@ -1338,6 +1568,7 @@ pub mv struct Container : Control {
1338
1568
  self.rh.load_int(i),
1339
1569
  content_w,
1340
1570
  content_h,
1571
+ flex,
1341
1572
  )
1342
1573
  }
1343
1574
  }
@@ -1469,6 +1700,20 @@ pub mv struct Container : Control {
1469
1700
  g_resize_grid = grid;
1470
1701
  g_resize_win = win;
1471
1702
  }
1703
+ // Close interception (Window.run consults the app's
1704
+ // close_requested): the delegate REFUSES the native close and sets
1705
+ // the pending flag; the Nomen loop consumes it next frame — the app
1706
+ // declines (flag cleared, window stays) or accepts (close() forces
1707
+ // past this via nomen_close_forced). External linkage: Window's
1708
+ // close() and close_pending() extern both flags.
1709
+ int nomen_close_pending = 0;
1710
+ int nomen_close_forced = 0;
1711
+ BOOL nomen_window_should_close_imp(id self, SEL cmd, id notification) {
1712
+ (void)self; (void)cmd; (void)notification;
1713
+ if (nomen_close_forced) return YES;
1714
+ nomen_close_pending = 1;
1715
+ return NO;
1716
+ }
1472
1717
  void nomen_window_did_resize(id self, SEL cmd, id notification) {
1473
1718
  (void)self; (void)cmd; (void)notification;
1474
1719
  if (g_resize_grid && g_resize_win) {
@@ -1497,6 +1742,20 @@ pub mv struct Container : Control {
1497
1742
  g_resize_grid = grid;
1498
1743
  g_resize_win = win;
1499
1744
  }
1745
+ // Close interception (Window.run consults the app's
1746
+ // close_requested): the delegate REFUSES the native close and sets
1747
+ // the pending flag; the Nomen loop consumes it next frame — the app
1748
+ // declines (flag cleared, window stays) or accepts (close() forces
1749
+ // past this via nomen_close_forced). External linkage: Window's
1750
+ // close() and close_pending() extern both flags.
1751
+ int nomen_close_pending = 0;
1752
+ int nomen_close_forced = 0;
1753
+ BOOL nomen_window_should_close_imp(id self, SEL cmd, id notification) {
1754
+ (void)self; (void)cmd; (void)notification;
1755
+ if (nomen_close_forced) return YES;
1756
+ nomen_close_pending = 1;
1757
+ return NO;
1758
+ }
1500
1759
  void nomen_window_did_resize(id self, SEL cmd, id notification) {
1501
1760
  (void)self; (void)cmd; (void)notification;
1502
1761
  if (g_resize_grid && g_resize_win) {
@@ -1513,6 +1772,9 @@ pub mv struct Container : Control {
1513
1772
  Dc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenResizeDelegate", 0);
1514
1773
  if (Dc) {
1515
1774
  class_addMethod(Dc, sel_registerName("windowDidResize:"), (IMP)nomen_window_did_resize, "v@:@");
1775
+ // Close interception: refuse the native close, set the
1776
+ // pending flag, let Window.run consult the app.
1777
+ class_addMethod(Dc, sel_registerName("windowShouldClose:"), (IMP)nomen_window_should_close_imp, "c@:@");
1516
1778
  objc_registerClassPair(Dc);
1517
1779
  }
1518
1780
  }
@@ -1531,6 +1793,57 @@ pub mv struct Container : Control {
1531
1793
  Dc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenResizeDelegate", 0);
1532
1794
  if (Dc) {
1533
1795
  class_addMethod(Dc, sel_registerName("windowDidResize:"), (IMP)nomen_window_did_resize, "v@:@");
1796
+ // Close interception: refuse the native close, set the
1797
+ // pending flag, let Window.run consult the app.
1798
+ class_addMethod(Dc, sel_registerName("windowShouldClose:"), (IMP)nomen_window_should_close_imp, "c@:@");
1799
+ objc_registerClassPair(Dc);
1800
+ }
1801
+ }
1802
+ if (Dc) {
1803
+ id delegate = ((id(*)(id, SEL))objc_msgSend)((id)Dc, sel_registerName("alloc"));
1804
+ delegate = ((id(*)(id, SEL))objc_msgSend)(delegate, sel_registerName("init"));
1805
+ ((void(*)(id, SEL, id))objc_msgSend)((id)win->handle, sel_registerName("setDelegate:"), delegate);
1806
+ }
1807
+ ```
1808
+ }
1809
+
1810
+ // Install the windowDidResize: delegate WITHOUT setting the target pair
1811
+ // (nomen_set_resize_target — call that first, e.g. from Window's
1812
+ // set_content). Window.run arms the delegate so the ROOT layout re-runs
1813
+ // live during AppKit's blocking resize drag; set_resize_callback is the
1814
+ // combined form for manual-loop apps.
1815
+ pub func arm_resize_delegate = (Window win) {
1816
+ ```
1817
+ #arch: c
1818
+ #platform: macos
1819
+ Class Dc = objc_getClass("NomenResizeDelegate");
1820
+ if (!Dc) {
1821
+ Dc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenResizeDelegate", 0);
1822
+ if (Dc) {
1823
+ class_addMethod(Dc, sel_registerName("windowDidResize:"), (IMP)nomen_window_did_resize, "v@:@");
1824
+ // Close interception: refuse the native close, set the
1825
+ // pending flag, let Window.run consult the app.
1826
+ class_addMethod(Dc, sel_registerName("windowShouldClose:"), (IMP)nomen_window_should_close_imp, "c@:@");
1827
+ objc_registerClassPair(Dc);
1828
+ }
1829
+ }
1830
+ if (Dc) {
1831
+ id delegate = ((id(*)(id, SEL))objc_msgSend)((id)Dc, sel_registerName("alloc"));
1832
+ delegate = ((id(*)(id, SEL))objc_msgSend)(delegate, sel_registerName("init"));
1833
+ ((void(*)(id, SEL, id))objc_msgSend)((id)win->handle, sel_registerName("setDelegate:"), delegate);
1834
+ }
1835
+ ```
1836
+ ```
1837
+ #arch: aarch64_use_c
1838
+ #platform: macos
1839
+ Class Dc = objc_getClass("NomenResizeDelegate");
1840
+ if (!Dc) {
1841
+ Dc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenResizeDelegate", 0);
1842
+ if (Dc) {
1843
+ class_addMethod(Dc, sel_registerName("windowDidResize:"), (IMP)nomen_window_did_resize, "v@:@");
1844
+ // Close interception: refuse the native close, set the
1845
+ // pending flag, let Window.run consult the app.
1846
+ class_addMethod(Dc, sel_registerName("windowShouldClose:"), (IMP)nomen_window_should_close_imp, "c@:@");
1534
1847
  objc_registerClassPair(Dc);
1535
1848
  }
1536
1849
  }
@@ -1542,6 +1855,428 @@ pub mv struct Container : Control {
1542
1855
  ```
1543
1856
  }
1544
1857
 
1858
+ // Register this grid as the document of a Scrollable, for Window.run's
1859
+ // relayout: on every content-rect change the framework re-runs the
1860
+ // document dance (measure unbounded → set_content_size → layout, plus a
1861
+ // one-time scroll_to_top) for every registered pair. `sv`/`doc` are the
1862
+ // scroll view's `handle`/`doc_handle()` — scalars, because a second
1863
+ // `ref` borrow of the same owner (self.page_grid AND self.page) would
1864
+ // trip the aliasing rule; see GUI.md ("The App run loop"). The grid (and
1865
+ // the Scrollable) must outlive the run — app fields. Call once, from
1866
+ // App.start.
1867
+ // Call once, from App.start. The file-scope registry lives HERE (in the
1868
+ // same file as this method and the thunk below): split builds emit only
1869
+ // referenced structs' methods, and every TU that emits set_document /
1870
+ // Window_relayout must also emit the registry + thunk they call —
1871
+ // putting them in Scrollable.nm broke aarch64 split links (undefined
1872
+ // symbols).
1873
+ pub func set_document = (ref self, uint64 sv, uint64 doc) {
1874
+ ```
1875
+ #arch: c
1876
+ #platform: macos
1877
+ #scope: file
1878
+ // The registered (scroll view, document, content grid) triples —
1879
+ // walked by nomen_scroll_relayout_all on every window resize. Raw
1880
+ // addresses: the grid and the scroll live in the app struct, whose
1881
+ // storage is stable for the whole run (the app is moved into
1882
+ // Window.run once).
1883
+ static unsigned long long nomen_scroll_reg_sv[8];
1884
+ static unsigned long long nomen_scroll_reg_doc[8];
1885
+ static void *nomen_scroll_reg_grid[8];
1886
+ static int nomen_scroll_reg_n = 0;
1887
+ void nomen_scroll_register(unsigned long long sv, unsigned long long doc, void *grid) {
1888
+ if (nomen_scroll_reg_n >= 8) return;
1889
+ nomen_scroll_reg_sv[nomen_scroll_reg_n] = sv;
1890
+ nomen_scroll_reg_doc[nomen_scroll_reg_n] = doc;
1891
+ nomen_scroll_reg_grid[nomen_scroll_reg_n] = grid;
1892
+ nomen_scroll_reg_n++;
1893
+ }
1894
+ // The dance trampoline — a free function defined at the bottom of
1895
+ // this file; prototyped so the walker can call it regardless of
1896
+ // emission order (file-scope blocks hoist above function bodies).
1897
+ extern void nomen_scroll_thunk(struct Container *, unsigned long long, unsigned long long, struct Window *, long);
1898
+ void nomen_scroll_relayout_all(void *win, long first) {
1899
+ for (int i = 0; i < nomen_scroll_reg_n; i++) {
1900
+ nomen_scroll_thunk(
1901
+ (struct Container *)nomen_scroll_reg_grid[i],
1902
+ nomen_scroll_reg_sv[i], nomen_scroll_reg_doc[i],
1903
+ (struct Window *)win, first);
1904
+ }
1905
+ }
1906
+ ```
1907
+ ```
1908
+ #arch: aarch64_use_c
1909
+ #platform: macos
1910
+ #scope: file
1911
+ static unsigned long long nomen_scroll_reg_sv[8];
1912
+ static unsigned long long nomen_scroll_reg_doc[8];
1913
+ static void *nomen_scroll_reg_grid[8];
1914
+ static int nomen_scroll_reg_n = 0;
1915
+ void nomen_scroll_register(unsigned long long sv, unsigned long long doc, void *grid) {
1916
+ if (nomen_scroll_reg_n >= 8) return;
1917
+ nomen_scroll_reg_sv[nomen_scroll_reg_n] = sv;
1918
+ nomen_scroll_reg_doc[nomen_scroll_reg_n] = doc;
1919
+ nomen_scroll_reg_grid[nomen_scroll_reg_n] = grid;
1920
+ nomen_scroll_reg_n++;
1921
+ }
1922
+ // The companion-emitted thunk (a free function, defined at the
1923
+ // bottom of this file) carries the asm label that maps its symbol
1924
+ // for the asm side; the label goes on this FIRST declaration too —
1925
+ // clang rejects an asm label applied after first use.
1926
+ extern void nomen_scroll_thunk(nm_Container *, unsigned long long, unsigned long long, nm_Window *, long) __asm__("nomen_scroll_thunk");
1927
+ void nomen_scroll_relayout_all(void *win, long first) {
1928
+ for (int i = 0; i < nomen_scroll_reg_n; i++) {
1929
+ nomen_scroll_thunk(
1930
+ (nm_Container *)nomen_scroll_reg_grid[i],
1931
+ nomen_scroll_reg_sv[i], nomen_scroll_reg_doc[i],
1932
+ (nm_Window *)win, first);
1933
+ }
1934
+ }
1935
+ ```
1936
+ ```
1937
+ #arch: c
1938
+ #platform: macos
1939
+ nomen_scroll_register(sv, doc, (void *)self);
1940
+ ```
1941
+ ```
1942
+ #arch: aarch64_use_c
1943
+ #platform: macos
1944
+ nomen_scroll_register(sv, doc, (void *)self);
1945
+ ```
1946
+ ```
1947
+ #arch: c
1948
+ #platform: ios
1949
+ /* TODO: iOS (see #init) */
1950
+ ```
1951
+ ```
1952
+ #arch: aarch64_use_c
1953
+ #platform: ios
1954
+ /* TODO: iOS (see #init) */
1955
+ ```
1956
+ }
1957
+
1958
+ // Register this grid as a PANE root: after every framework relayout pass
1959
+ // (first post-show + every resize — Window.relayout), the grid is re-laid
1960
+ // into its companion window (`pane_handle`). Pane hosts re-frame on their
1961
+ // own schedule — a Tabs page after the tab view is framed, a SplitView
1962
+ // pane after the split is re-framed — so the app cannot know the right
1963
+ // moment; registration hands the timing to the run loop (the same trade
1964
+ // set_document makes for Scrollable documents). `pane_handle` is the
1965
+ // companion window's `handle` — a scalar, because a second `ref` borrow
1966
+ // of the same owner (self.pane_grid AND self.pane_window) would trip the
1967
+ // aliasing rule (see set_document). The grid (and the pane window) must
1968
+ // outlive the run — app fields. Call once, from App.start. The file-scope
1969
+ // registry lives HERE (same file as the layout thunk the walker calls),
1970
+ // for the same split-link reason set_document records.
1971
+ pub func set_pane = (ref self, uint64 pane_handle) {
1972
+ ```
1973
+ #arch: c
1974
+ #platform: macos
1975
+ #scope: file
1976
+ // The registered (pane grid, pane window handle) pairs — walked by
1977
+ // nomen_pane_relayout_all after every relayout pass. Raw grid
1978
+ // addresses: the grid lives in the app struct, whose storage is
1979
+ // stable for the whole run. The window is stored as its handle and
1980
+ // re-wrapped per walk (the same stack Window the scroll thunk builds
1981
+ // over the doc handle) — a by-value Window param would point at
1982
+ // set_pane's own frame once the call returns.
1983
+ static void *nomen_pane_reg_grid[8];
1984
+ static unsigned long long nomen_pane_reg_win[8];
1985
+ static int nomen_pane_reg_n = 0;
1986
+ void nomen_pane_register(void *grid, unsigned long long handle) {
1987
+ if (nomen_pane_reg_n >= 8) return;
1988
+ nomen_pane_reg_grid[nomen_pane_reg_n] = grid;
1989
+ nomen_pane_reg_win[nomen_pane_reg_n] = handle;
1990
+ nomen_pane_reg_n++;
1991
+ }
1992
+ void nomen_pane_relayout_all(void) {
1993
+ for (int i = 0; i < nomen_pane_reg_n; i++) {
1994
+ extern void *_Window_traits[];
1995
+ extern void nomen_layout_thunk(struct Container *, struct Window *);
1996
+ struct Window pane_win;
1997
+ pane_win._vt = (void *)&_Window_traits;
1998
+ pane_win.handle = nomen_pane_reg_win[i];
1999
+ nomen_layout_thunk((struct Container *)nomen_pane_reg_grid[i], &pane_win);
2000
+ }
2001
+ }
2002
+ ```
2003
+ ```
2004
+ #arch: aarch64_use_c
2005
+ #platform: macos
2006
+ #scope: file
2007
+ static void *nomen_pane_reg_grid[8];
2008
+ static unsigned long long nomen_pane_reg_win[8];
2009
+ static int nomen_pane_reg_n = 0;
2010
+ void nomen_pane_register(void *grid, unsigned long long handle) {
2011
+ if (nomen_pane_reg_n >= 8) return;
2012
+ nomen_pane_reg_grid[nomen_pane_reg_n] = grid;
2013
+ nomen_pane_reg_win[nomen_pane_reg_n] = handle;
2014
+ nomen_pane_reg_n++;
2015
+ }
2016
+ void nomen_pane_relayout_all(void) {
2017
+ for (int i = 0; i < nomen_pane_reg_n; i++) {
2018
+ extern void *Window_traits;
2019
+ extern void nomen_layout_thunk(struct Container *, struct Window *);
2020
+ nm_Window pane_win;
2021
+ pane_win._vt = (void *)&Window_traits;
2022
+ pane_win.handle = nomen_pane_reg_win[i];
2023
+ nomen_layout_thunk((struct Container *)nomen_pane_reg_grid[i], (struct Window *)&pane_win);
2024
+ }
2025
+ }
2026
+ ```
2027
+ ```
2028
+ #arch: c
2029
+ #platform: macos
2030
+ nomen_pane_register((void *)self, pane_handle);
2031
+ ```
2032
+ ```
2033
+ #arch: aarch64_use_c
2034
+ #platform: macos
2035
+ nomen_pane_register((void *)self, pane_handle);
2036
+ ```
2037
+ ```
2038
+ #arch: c
2039
+ #platform: ios
2040
+ /* TODO: iOS (see #init) */
2041
+ ```
2042
+ ```
2043
+ #arch: aarch64_use_c
2044
+ #platform: ios
2045
+ /* TODO: iOS (see #init) */
2046
+ ```
2047
+ }
2048
+
2049
+ // The on_right_click desugar's drain (docs/HANDLERS.md): the synthesized
2050
+ // pump arm polls this per frame. The record is WINDOW-level — the same
2051
+ // edge FrameEvents.click reports as 2, stamped by every process_events
2052
+ // pump and stale the moment the next pump runs — so registration reads
2053
+ // "tell me when a right-click happens": every registered arm sees the
2054
+ // edge (broadcast — no consumption, so arms never starve each other),
2055
+ // and the HANDLER disambiguates: read the click through the dispatch
2056
+ // window (win.click_x()/click_y()), translate for scrollables
2057
+ // (content_click), hit-test children (contains). `self` is the routing
2058
+ // receiver only — the event is not container-scoped.
2059
+ pub func take_right_click = (self, out bool) {
2060
+ ```
2061
+ #arch: c
2062
+ #platform: macos
2063
+ extern unsigned char nomen_window_had_right_click(void);
2064
+ return nomen_window_had_right_click();
2065
+ ```
2066
+ ```
2067
+ #arch: aarch64_use_c
2068
+ #platform: macos
2069
+ extern unsigned char nomen_window_had_right_click(void) __asm__("nomen_window_had_right_click");
2070
+ return nomen_window_had_right_click();
2071
+ ```
2072
+ ```
2073
+ #arch: c
2074
+ #platform: ios
2075
+ return 0;
2076
+ ```
2077
+ ```
2078
+ #arch: aarch64_use_c
2079
+ #platform: ios
2080
+ return 0;
2081
+ ```
2082
+ }
2083
+
2084
+ // The on_mouse_down desugar's drain (docs/HANDLERS.md): the synthesized
2085
+ // pump arm polls this per frame. The exhaustive window-level edge —
2086
+ // EVERY mouse-down (left, right, middle, mouse 4+), not just the two
2087
+ // named buttons. Like take_right_click, the record is window-level and
2088
+ // broadcast (one edge per pump, every registered arm sees it); the
2089
+ // HANDLER filters and disambiguates: `win.mouse_button()` (1-based —
2090
+ // 1 left, 2 right, 3 middle, 4+ extras), `mouse_click_count()`
2091
+ // (double/triple), `mouse_modifiers()` (cmd/option/ctrl/shift bits),
2092
+ // `click_x()`/`click_y()` for the position. `on_right_click` is the
2093
+ // sugar for button 2.
2094
+ pub func take_mouse_down = (self, out bool) {
2095
+ ```
2096
+ #arch: c
2097
+ #platform: macos
2098
+ extern int nomen_last_click_button;
2099
+ return nomen_last_click_button != 0;
2100
+ ```
2101
+ ```
2102
+ #arch: aarch64_use_c
2103
+ #platform: macos
2104
+ extern int nomen_last_click_button;
2105
+ return nomen_last_click_button != 0;
2106
+ ```
2107
+ ```
2108
+ #arch: c
2109
+ #platform: ios
2110
+ return 0;
2111
+ ```
2112
+ ```
2113
+ #arch: aarch64_use_c
2114
+ #platform: ios
2115
+ return 0;
2116
+ ```
2117
+ }
2118
+
2119
+ // The on_double_click desugar's drain (docs/HANDLERS.md tier 2): the
2120
+ // sugar over the mouse-down record — left button (1) with a click count
2121
+ // of 2, the list-row "open" gesture. Window-level and broadcast, like
2122
+ // the rest of the family: every registered arm sees the edge, and the
2123
+ // HANDLER disambiguates — position via win.click_x()/click_y() for
2124
+ // hit-testing (a row-targeted refinement can come later through the
2125
+ // table family's native doubleAction). `on_mouse_down` remains the raw
2126
+ // tier (it sees the same double-click with the full record).
2127
+ pub func take_double_click = (self, out bool) {
2128
+ ```
2129
+ #arch: c
2130
+ #platform: macos
2131
+ extern int nomen_last_click_button;
2132
+ extern int nomen_last_click_count;
2133
+ return nomen_last_click_button == 1 && nomen_last_click_count == 2;
2134
+ ```
2135
+ ```
2136
+ #arch: aarch64_use_c
2137
+ #platform: macos
2138
+ extern int nomen_last_click_button;
2139
+ extern int nomen_last_click_count;
2140
+ return nomen_last_click_button == 1 && nomen_last_click_count == 2;
2141
+ ```
2142
+ ```
2143
+ #arch: c
2144
+ #platform: ios
2145
+ return 0;
2146
+ ```
2147
+ ```
2148
+ #arch: aarch64_use_c
2149
+ #platform: ios
2150
+ return 0;
2151
+ ```
2152
+ }
2153
+
2154
+ // The on_key_down / on_key_up desugars' drains (docs/HANDLERS.md): the
2155
+ // synthesized pump arm polls these per frame. The keyboard record is
2156
+ // window-level and broadcast (like the mouse records): every registered
2157
+ // arm sees every key, and the HANDLER filters — `win.key_code()`
2158
+ // (hardware keycode: Space = 49, Return = 36, arrows = 123–126, letters
2159
+ // a…z = 0–25), `win.key_modifiers()` (Command = 1048576, Option =
2160
+ // 524288, Control = 262144, Shift = 131072), `win.key_repeat()` (a held
2161
+ // key auto-repeating — filter for first-press-only shortcuts),
2162
+ // `win.key_up_code()`. These are SHORTCUT and raw-key events: text
2163
+ // entry does not flow here — a TextBox's typing arrives through
2164
+ // `on_input` (the field editor), which is the right event for editors.
2165
+ pub func take_key_down = (self, out bool) {
2166
+ ```
2167
+ #arch: c
2168
+ #platform: macos
2169
+ extern int nomen_last_key_code;
2170
+ return nomen_last_key_code != -1;
2171
+ ```
2172
+ ```
2173
+ #arch: aarch64_use_c
2174
+ #platform: macos
2175
+ extern int nomen_last_key_code;
2176
+ return nomen_last_key_code != -1;
2177
+ ```
2178
+ ```
2179
+ #arch: c
2180
+ #platform: ios
2181
+ return 0;
2182
+ ```
2183
+ ```
2184
+ #arch: aarch64_use_c
2185
+ #platform: ios
2186
+ return 0;
2187
+ ```
2188
+ }
2189
+
2190
+ pub func take_key_up = (self, out bool) {
2191
+ ```
2192
+ #arch: c
2193
+ #platform: macos
2194
+ extern int nomen_last_key_up_code;
2195
+ return nomen_last_key_up_code != -1;
2196
+ ```
2197
+ ```
2198
+ #arch: aarch64_use_c
2199
+ #platform: macos
2200
+ extern int nomen_last_key_up_code;
2201
+ return nomen_last_key_up_code != -1;
2202
+ ```
2203
+ ```
2204
+ #arch: c
2205
+ #platform: ios
2206
+ return 0;
2207
+ ```
2208
+ ```
2209
+ #arch: aarch64_use_c
2210
+ #platform: ios
2211
+ return 0;
2212
+ ```
2213
+ }
2214
+
2215
+ // The on_mouse_move desugar's drain (docs/HANDLERS.md): the synthesized
2216
+ // pump arm polls this per frame. True on any frame whose pump saw a
2217
+ // pointer move or drag; the position rides the dispatch window —
2218
+ // `win.mouse_x()` / `win.mouse_y()` (-1 on a frame with no movement).
2219
+ // Window-level and broadcast, like every record in the family. Hover,
2220
+ // cursors, and tooltips hit-test this against contains(); per-control
2221
+ // entered/exited is sugar over the same record.
2222
+ pub func take_mouse_move = (self, out bool) {
2223
+ ```
2224
+ #arch: c
2225
+ #platform: macos
2226
+ extern int nomen_last_mouse_move_x;
2227
+ return nomen_last_mouse_move_x != -1;
2228
+ ```
2229
+ ```
2230
+ #arch: aarch64_use_c
2231
+ #platform: macos
2232
+ extern int nomen_last_mouse_move_x;
2233
+ return nomen_last_mouse_move_x != -1;
2234
+ ```
2235
+ ```
2236
+ #arch: c
2237
+ #platform: ios
2238
+ return 0;
2239
+ ```
2240
+ ```
2241
+ #arch: aarch64_use_c
2242
+ #platform: ios
2243
+ return 0;
2244
+ ```
2245
+ }
2246
+
2247
+ // The on_resize desugar's drain (docs/HANDLERS.md): fires on the frame
2248
+ // whose content rect changed. The framework has ALREADY re-laid-out the
2249
+ // registered roots — the handler re-lays-out app-owned panes (tab
2250
+ // pages, split panes). The new size rides the dispatch window via
2251
+ // `win.resize_width()` / `win.resize_height()`; FrameEvents.resized /
2252
+ // width / height carry the same record for `frame`. The record is
2253
+ // stamped by Window.run's resize detection (same edge the frame form
2254
+ // reports) — window-level, broadcast like the rest of the family.
2255
+ pub func take_resize = (self, out bool) {
2256
+ ```
2257
+ #arch: c
2258
+ #platform: macos
2259
+ extern int nomen_last_resized;
2260
+ return nomen_last_resized != 0;
2261
+ ```
2262
+ ```
2263
+ #arch: aarch64_use_c
2264
+ #platform: macos
2265
+ extern int nomen_last_resized;
2266
+ return nomen_last_resized != 0;
2267
+ ```
2268
+ ```
2269
+ #arch: c
2270
+ #platform: ios
2271
+ return 0;
2272
+ ```
2273
+ ```
2274
+ #arch: aarch64_use_c
2275
+ #platform: ios
2276
+ return 0;
2277
+ ```
2278
+ }
2279
+
1545
2280
  // Hit test: is point (cx, cy) (top-left origin, as returned by click_x/
1546
2281
  // click_y) inside the leaf with the given handle?
1547
2282
  pub func contains = (self, uint64 handle, int cx, int cy, out bool) {
@@ -1638,6 +2373,105 @@ pub func nomen_layout_thunk = (ref Container grid, Window win) {
1638
2373
  grid.layout(win)
1639
2374
  }
1640
2375
 
2376
+ // The grid-side measure for the document dance, as a C-linkage free
2377
+ // function (pure Nomen — asm-emitted on aarch64) so the thunk's raw bodies
2378
+ // can call it: constraints are the window's content width minus the grid's
2379
+ // own padding (the `layout_page` formula), height unbounded.
2380
+ pub func nomen_scroll_measure = (ref Container grid, int max_w, out [int, int]) {
2381
+ var bc = BoxConstraints()
2382
+ bc.max_width = max_w
2383
+ const Size cs = grid.measure(bc)
2384
+ return [cs.width, cs.height]
2385
+ }
2386
+
2387
+ // The per-pair document dance, called from nomen_scroll_relayout_all (the
2388
+ // registration's C walker — see set_document above): measure the grid
2389
+ // unbounded-height at the window's content width, size the document view,
2390
+ // lay the grid out into the document, and — once, on the framework's first
2391
+ // post-show pass — scroll to the top. Pointer/handle params: free functions
2392
+ // are exported for C linkage on both backends, and the walker holds raw
2393
+ // addresses + the scroll's handles (scalars). FULLY RAW bodies: an
2394
+ // aarch64_use_c function routes to the C companion using only its raw
2395
+ // blocks, so any Nomen statement here would be silently dropped
2396
+ // (FOLLOWUP.md) — the measure goes through nomen_scroll_measure instead.
2397
+ pub func nomen_scroll_thunk = (
2398
+ ref Container grid,
2399
+ uint64 sv,
2400
+ uint64 doc,
2401
+ ref Window win,
2402
+ int first
2403
+ ) {
2404
+ ```
2405
+ #arch: c
2406
+ #platform: macos
2407
+ extern struct _Tuple_int_int nomen_scroll_measure(struct Container *, long);
2408
+ id ssv = (id)sv;
2409
+ id sdoc = (id)doc;
2410
+ id win_cv = ((id(*)(id, SEL))objc_msgSend)((id)win->handle, sel_registerName("contentView"));
2411
+ CGRect win_frame = ((CGRect(*)(id, SEL))objc_msgSend)(win_cv, sel_registerName("frame"));
2412
+ struct _Tuple_int_int cs = nomen_scroll_measure(grid, (long)win_frame.size.width - grid->padding * 2);
2413
+ id clip = ((id(*)(id, SEL))objc_msgSend)(ssv, sel_registerName("contentView"));
2414
+ CGRect clip_bounds = ((CGRect(*)(id, SEL))objc_msgSend)(clip, sel_registerName("bounds"));
2415
+ double frame_h = (double)cs._1;
2416
+ if (frame_h < clip_bounds.size.height) frame_h = clip_bounds.size.height;
2417
+ ((void(*)(id, SEL, CGSize))objc_msgSend)(sdoc, sel_registerName("setFrameSize:"),
2418
+ ((CGSize(*)(double, double))CGSizeMake)((double)cs._0, frame_h));
2419
+ if (first == 1) {
2420
+ // The document view is unflipped and starts showing its bottom-left
2421
+ // corner — the one-time scroll_to_top (the clip's bounds origin sits
2422
+ // `doc_h - clip_h` up from the bottom).
2423
+ CGPoint origin = ((CGPoint(*)(double, double))CGPointMake)(0, frame_h - clip_bounds.size.height);
2424
+ ((void(*)(id, SEL, CGPoint))objc_msgSend)(clip, sel_registerName("setBoundsOrigin:"), origin);
2425
+ ((void(*)(id, SEL, id))objc_msgSend)(ssv, sel_registerName("reflectScrolledClipView:"), clip);
2426
+ }
2427
+ // Lay the grid out into the document view: a stack Window value over the
2428
+ // doc handle (layout only reads it — no retain needed).
2429
+ extern void *_Window_traits[];
2430
+ extern void nomen_layout_thunk(struct Container *, struct Window *);
2431
+ struct Window doc_win;
2432
+ doc_win._vt = (void *)&_Window_traits;
2433
+ doc_win.handle = doc;
2434
+ nomen_layout_thunk((struct Container *)grid, &doc_win);
2435
+ ```
2436
+ ```
2437
+ #arch: aarch64_use_c
2438
+ #platform: macos
2439
+ extern nm__Tuple_int_int nomen_scroll_measure(struct Container *, long);
2440
+ id ssv = (id)sv;
2441
+ id sdoc = (id)doc;
2442
+ id win_cv = ((id(*)(id, SEL))objc_msgSend)((id)win->handle, sel_registerName("contentView"));
2443
+ CGRect win_frame = ((CGRect(*)(id, SEL))objc_msgSend)(win_cv, sel_registerName("frame"));
2444
+ nm__Tuple_int_int cs = nomen_scroll_measure(grid, (long)win_frame.size.width - grid->padding * 2);
2445
+ id clip = ((id(*)(id, SEL))objc_msgSend)(ssv, sel_registerName("contentView"));
2446
+ CGRect clip_bounds = ((CGRect(*)(id, SEL))objc_msgSend)(clip, sel_registerName("bounds"));
2447
+ double frame_h = (double)cs._1;
2448
+ if (frame_h < clip_bounds.size.height) frame_h = clip_bounds.size.height;
2449
+ ((void(*)(id, SEL, CGSize))objc_msgSend)(sdoc, sel_registerName("setFrameSize:"),
2450
+ ((CGSize(*)(double, double))CGSizeMake)((double)cs._0, frame_h));
2451
+ if (first == 1) {
2452
+ CGPoint origin = ((CGPoint(*)(double, double))CGPointMake)(0, frame_h - clip_bounds.size.height);
2453
+ ((void(*)(id, SEL, CGPoint))objc_msgSend)(clip, sel_registerName("setBoundsOrigin:"), origin);
2454
+ ((void(*)(id, SEL, id))objc_msgSend)(ssv, sel_registerName("reflectScrolledClipView:"), clip);
2455
+ }
2456
+ extern void *Window_traits;
2457
+ extern void nomen_layout_thunk(struct Container *, struct Window *);
2458
+ nm_Window doc_win;
2459
+ doc_win._vt = (void *)&Window_traits;
2460
+ doc_win.handle = doc;
2461
+ nomen_layout_thunk((struct Container *)grid, (struct Window *)&doc_win);
2462
+ ```
2463
+ ```
2464
+ #arch: c
2465
+ #platform: ios
2466
+ /* TODO: iOS (see #init) */
2467
+ ```
2468
+ ```
2469
+ #arch: aarch64_use_c
2470
+ #platform: ios
2471
+ /* TODO: iOS (see #init) */
2472
+ ```
2473
+ }
2474
+
1641
2475
  /**
1642
2476
  * Creates a vertical stack container with the given spacing
1643
2477
  **/