nomen-lang 0.0.7 → 0.0.8

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 (118) hide show
  1. package/NOMEN_AGENTS.md +235 -0
  2. package/core/System/Ansi.nm +150 -0
  3. package/core/System/Array.nm +358 -0
  4. package/core/System/BigInt.nm +887 -0
  5. package/core/System/Buffer.nm +526 -0
  6. package/core/System/Channel.nm +198 -0
  7. package/core/System/ClassBuffer.nm +229 -0
  8. package/core/System/Console.nm +211 -0
  9. package/core/System/Controls/Button.nm +138 -0
  10. package/core/System/Controls/CheckBox.nm +158 -0
  11. package/core/System/Controls/Container.nm +1562 -0
  12. package/core/System/Controls/Control.nm +28 -0
  13. package/core/System/Controls/Geometry.nm +101 -0
  14. package/core/System/Controls/Layout.nm +228 -0
  15. package/core/System/Controls/LayoutParams.nm +50 -0
  16. package/core/System/Controls/Text.nm +129 -0
  17. package/core/System/Controls/TextBox.nm +157 -0
  18. package/core/System/Controls/Window.nm +280 -0
  19. package/core/System/Disposable.nm +8 -0
  20. package/core/System/Enumerable.nm +10 -0
  21. package/core/System/Equatable.nm +10 -0
  22. package/core/System/Graph.nm +68 -0
  23. package/core/System/Hashable.nm +13 -0
  24. package/core/System/Init.nm +21 -0
  25. package/core/System/LinkedList.nm +64 -0
  26. package/core/System/List.nm +44 -0
  27. package/core/System/Map.nm +153 -0
  28. package/core/System/Math.nm +54 -0
  29. package/core/System/Mutex.nm +78 -0
  30. package/core/System/Nursery.nm +27 -0
  31. package/core/System/Sendable.nm +9 -0
  32. package/core/System/Set.nm +97 -0
  33. package/core/System/Stream/Directory.nm +176 -0
  34. package/core/System/Stream/File.nm +432 -0
  35. package/core/System/Stream/Http.nm +453 -0
  36. package/core/System/String.nm +154 -0
  37. package/core/System/StringBuilder.nm +249 -0
  38. package/core/System/Stringable.nm +8 -0
  39. package/core/System/Task.nm +263 -0
  40. package/core/System/Test.nm +241 -0
  41. package/core/System/Text/Json.nm +377 -0
  42. package/core/System/Text/JsonTree.nm +187 -0
  43. package/core/System/Text/Regex.nm +655 -0
  44. package/core/System/Time.nm +112 -0
  45. package/core/System/Tree.nm +70 -0
  46. package/core/System/Viewable.nm +27 -0
  47. package/core/System/bool.nm +66 -0
  48. package/core/System/char.nm +42 -0
  49. package/core/System/float.nm +43 -0
  50. package/core/System/int.nm +54 -0
  51. package/core/System/int64.nm +42 -0
  52. package/core/System/int8.nm +42 -0
  53. package/core/System/uint.nm +42 -0
  54. package/core/System/uint64.nm +42 -0
  55. package/core/System/uint8.nm +42 -0
  56. package/core/docs/System/Ansi.md +40 -0
  57. package/core/docs/System/Array.md +16 -0
  58. package/core/docs/System/BigInt.md +30 -0
  59. package/core/docs/System/Buffer.md +31 -0
  60. package/core/docs/System/Channel.md +10 -0
  61. package/core/docs/System/ClassBuffer.md +15 -0
  62. package/core/docs/System/Console.md +13 -0
  63. package/core/docs/System/Controls/Button.md +12 -0
  64. package/core/docs/System/Controls/CheckBox.md +15 -0
  65. package/core/docs/System/Controls/Container.md +82 -0
  66. package/core/docs/System/Controls/Control.md +5 -0
  67. package/core/docs/System/Controls/Geometry.md +26 -0
  68. package/core/docs/System/Controls/Layout.md +37 -0
  69. package/core/docs/System/Controls/LayoutParams.md +13 -0
  70. package/core/docs/System/Controls/Text.md +12 -0
  71. package/core/docs/System/Controls/TextBox.md +13 -0
  72. package/core/docs/System/Controls/Window.md +20 -0
  73. package/core/docs/System/Disposable.md +5 -0
  74. package/core/docs/System/Enumerable.md +5 -0
  75. package/core/docs/System/Graph.md +17 -0
  76. package/core/docs/System/Init.md +9 -0
  77. package/core/docs/System/LinkedList.md +16 -0
  78. package/core/docs/System/List.md +13 -0
  79. package/core/docs/System/Map.md +14 -0
  80. package/core/docs/System/Math.md +11 -0
  81. package/core/docs/System/Mutex.md +10 -0
  82. package/core/docs/System/Nursery.md +5 -0
  83. package/core/docs/System/Sendable.md +5 -0
  84. package/core/docs/System/Set.md +14 -0
  85. package/core/docs/System/Stream/Directory.md +12 -0
  86. package/core/docs/System/Stream/File.md +21 -0
  87. package/core/docs/System/String.md +14 -0
  88. package/core/docs/System/StringBuilder.md +12 -0
  89. package/core/docs/System/Stringable.md +5 -0
  90. package/core/docs/System/Task.md +15 -0
  91. package/core/docs/System/Text/Json.md +12 -0
  92. package/core/docs/System/Text/JsonTree.md +21 -0
  93. package/core/docs/System/Text/Regex.md +12 -0
  94. package/core/docs/System/Time.md +11 -0
  95. package/core/docs/System/Tree.md +18 -0
  96. package/core/docs/System/Viewable.md +5 -0
  97. package/core/docs/System/bool.md +9 -0
  98. package/core/docs/System/char.md +9 -0
  99. package/core/docs/System/float.md +9 -0
  100. package/core/docs/System/int.md +10 -0
  101. package/core/docs/System/int64.md +9 -0
  102. package/core/docs/System/int8.md +9 -0
  103. package/core/docs/System/uint.md +9 -0
  104. package/core/docs/System/uint64.md +9 -0
  105. package/core/docs/System/uint8.md +9 -0
  106. package/core/package.jsonc +4 -0
  107. package/dist/index.mjs +417 -5490
  108. package/package.json +33 -30
  109. package/src/args.ts +171 -0
  110. package/src/index.ts +53 -104
  111. package/src/init.ts +128 -0
  112. package/src/test.ts +6 -2
  113. package/test/fixtures/build/calc.test +0 -0
  114. package/test/fixtures/build/calc.test.c +0 -969
  115. package/test/fixtures/build/main.h +0 -170
  116. package/test/fixtures/calc.test.nm +0 -32
  117. package/tsconfig.json +0 -21
  118. package/vite.config.ts +0 -7
@@ -0,0 +1,1562 @@
1
+ // A handle-based layout container. Users create a Grid/VStack/HStack, add child
2
+ // controls by their native handle, and call `grid.layout(win)` — the container
3
+ // measures, arranges, and applies frames (flipping the y axis against the
4
+ // window's content height) so callers never compute coordinates themselves.
5
+ //
6
+ // All node data lives in flat Buffer<int> arrays (structure-of-arrays). Because
7
+ // this is trusted core library code, the runtime-indexed Buffer accesses here do
8
+ // not trip the "constraint cannot be verified" rule that bites user code.
9
+ //
10
+ // Children are leaves (native controls). A leaf stores its native handle and a
11
+ // per-axis requested size as a (kind, value) pair mirroring the `LayoutLength`
12
+ // enum in Geometry.nm (`.auto`/`.fixed(n)`/`.percent(p)`/`.fill`). The legacy
13
+ // `add`/`add_to` helpers take plain int `w`/`h` (0 → `.auto`, non-zero →
14
+ // `.fixed`); `add_len`/`add_to_len` take `LayoutLength` directly, so a child
15
+ // can ask for, say, `.percent(50)` of the cross axis or `.fill`. On a bounded
16
+ // (cross) axis, `.auto`/`.fill` fill the available space; on an unbounded
17
+ // (main) axis they're 0 so a `grow` weight then decides how much surplus the
18
+ // child absorbs. `shrink` is the mirror weight: when the children's total
19
+ // exceeds the main-axis content size, the deficit is shared between shrinkable
20
+ // children weighted by `shrink` (default 0 = never shrink, the original
21
+ // behavior). Grid children additionally carry a column `span`. Children flow
22
+ // left-to-right, top-to-bottom; a child whose span won't fit in the remaining
23
+ // columns wraps to the next row. A hidden native control (isHidden) is skipped
24
+ // automatically, so toggling visibility and re-laying-out just works.
25
+
26
+ const KIND_LEAF = 0
27
+ const KIND_VSTACK = 1
28
+ const KIND_HSTACK = 2
29
+ const KIND_GRID = 3
30
+ const KIND_ZSTACK = 4
31
+ const KIND_BLOCK = 5
32
+
33
+ // Cross-axis alignment for a child within its parent stack. The indices match
34
+ // the `Alignment` enum case order in Geometry.nm (start, center, end, stretch).
35
+ // The default is stretch, which preserves the original "fill the cross axis"
36
+ // behavior every child had before grow/align landed.
37
+ pub const ALIGN_START = 0
38
+ pub const ALIGN_CENTER = 1
39
+ pub const ALIGN_END = 2
40
+ pub const ALIGN_STRETCH = 3
41
+
42
+ // Per-axis requested size for a leaf. The first four cases match the
43
+ // `LayoutLength` enum case order in Geometry.nm (auto, fixed, percent, fill);
44
+ // `LEN_INTRINSIC` is a v1-only extension (see below). The legacy `add`/`add_to`
45
+ // helpers translate their int `w`/`h` arguments into a (kind, value) pair:
46
+ // `0` → `.auto` (fills a bounded axis, stays 0 on an unbounded axis so `grow`
47
+ // decides), non-zero → `.fixed(w)`. `add_kind`/`add_to_kind` accept any case
48
+ // directly, including `.percent(p)` (p% of the available axis), `.fill`, and
49
+ // `.intrinsic`.
50
+ //
51
+ // `LEN_INTRINSIC` (v1-only; the trait-based `.auto` will fold intrinsic
52
+ // measurement into `auto`) asks the leaf's native control for its
53
+ // `intrinsicContentSize` on that axis, so a control can be added with no size
54
+ // hints and size to its content. The v1 keeps `LEN_AUTO` = fill (its
55
+ // long-standing default) rather than repurposing it, so existing `add(handle,
56
+ // 0, h, span)` callers and the live app are unchanged; opt into intrinsic via
57
+ // `add_intrinsic` / `add_to_intrinsic`, or per-axis via
58
+ // `add_kind(handle, LEN_FILL, 0, LEN_INTRINSIC, 0, span)` (fill the cross axis,
59
+ // intrinsic height). A handle of 0 (no native view) measures to 0, so the
60
+ // pure-math geometry tests exercise this path without native calls.
61
+ pub const LEN_AUTO = 0
62
+ pub const LEN_FIXED = 1
63
+ pub const LEN_PERCENT = 2
64
+ pub const LEN_FILL = 3
65
+ pub const LEN_INTRINSIC = 4
66
+
67
+ const INF = 100000000
68
+
69
+ // Translate a `LayoutLength` enum case into the internal (kind, value) int
70
+ // pair stored in the SoA buffers. `.auto` maps to LEN_AUTO (v1 semantics:
71
+ // fill the bounded axis — see the LEN_INTRINSIC note above), so `add_len`
72
+ // composes with the existing measure math unchanged.
73
+ func length_kind = (LayoutLength len, out int) {
74
+ var int k = match len {
75
+ case .auto -> LEN_AUTO
76
+ case .fixed(px) -> LEN_FIXED
77
+ case .percent(num) -> LEN_PERCENT
78
+ case .fill -> LEN_FILL
79
+ }
80
+ return k
81
+ }
82
+
83
+ func length_val = (LayoutLength len, out int) {
84
+ var int v = match len {
85
+ case .auto -> 0
86
+ case .fixed(px) -> px
87
+ case .percent(num) -> num
88
+ case .fill -> 0
89
+ }
90
+ return v
91
+ }
92
+
93
+ // Defined before the struct so their return types are resolved when the engine
94
+ // methods below reference them (free functions aren't forward-inferred).
95
+
96
+ func is_visible = (uint64 handle, out bool) {
97
+ if handle == 0 { return true }
98
+ ```
99
+ #arch: c, aarch64_use_c
100
+ #platform: macos
101
+ BOOL hidden = ((BOOL(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("isHidden"));
102
+ return !hidden;
103
+ ```
104
+ ```
105
+ #arch: c, aarch64_use_c
106
+ #platform: ios
107
+ return true;
108
+ ```
109
+ }
110
+
111
+ func apply_frame = (uint64 handle, int x, int y, int w, int h, int content_w, int content_h) {
112
+ ```
113
+ #arch: c, aarch64_use_c
114
+ #platform: macos
115
+ // AppKit runs its own blocking tracking loop during a live window resize,
116
+ // so the manual event loop can't re-layout mid-drag. Give the control an
117
+ // autoresizing mask so AppKit repositions it live: pin to the nearest
118
+ // vertical edge (top=NSViewMinYMargin 8 / bottom=NSViewMaxYMargin 32) to
119
+ // respect the top-left-origin layout instead of AppKit's default
120
+ // bottom-left pin, and let wide controls grow with the content width
121
+ // (NSViewWidthSizable 2). Never set NSViewHeightSizable (16): control
122
+ // heights come from the layout pass, not the window. AppKit's result
123
+ // matches what layout() computes, so the post-drag relayout only fixes up
124
+ // cells AppKit can't (column reflow for sub-full-width children).
125
+ unsigned long mask = (y * 2 < content_h) ? 8UL : 32UL;
126
+ if (w * 10 >= content_w * 8) mask |= 2UL;
127
+ ((void(*)(id, SEL, unsigned long))objc_msgSend)((id)handle, sel_registerName("setAutoresizingMask:"), mask);
128
+ ((void(*)(id, SEL, CGRect))objc_msgSend)(
129
+ (id)handle, sel_registerName("setFrame:"),
130
+ ((CGRect(*)(double, double, double, double))CGRectMake)(
131
+ (double)x, (double)(content_h - y - h), (double)w, (double)h));
132
+ ```
133
+ ```
134
+ #arch: c, aarch64_use_c
135
+ #platform: ios
136
+ ((void(*)(id, SEL, CGRect))objc_msgSend)(
137
+ (id)handle, sel_registerName("setFrame:"),
138
+ ((CGRect(*)(double, double, double, double))CGRectMake)(
139
+ (double)x, (double)y, (double)w, (double)h));
140
+ ```
141
+ }
142
+
143
+ // Query a native control's intrinsic content size, returning the full
144
+ // `[width, height]` in a single call. Used by the `LEN_INTRINSIC` leaf sizing
145
+ // kind so a control can be added with no size hints and size to its content
146
+ // (e.g. a `Text` measures to its rendered glyph size, a `Button` to its title
147
+ // + chrome). Returns `[0, 0]` for handle 0 (no native view, so the pure-math
148
+ // geometry tests exercise the wiring without native calls) and for views that
149
+ // report no intrinsic size (AppKit returns `NSNotFound` for a generic `NSView`).
150
+ // Also the shared backing query for the native leaf controls' `Control`
151
+ // conformance (`Button`/`CheckBox`/`TextBox`), so each leaf's `intrinsic_size`
152
+ // doesn't need its own raw-block FFI.
153
+ pub func native_intrinsic = (uint64 handle, out [int, int]) {
154
+ if handle == 0 { return [0, 0] }
155
+ ```
156
+ #arch: c, aarch64_use_c
157
+ #platform: macos
158
+ CGSize sz = ((CGSize(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("intrinsicContentSize"));
159
+ int w = (sz.width < 0 || sz.width > 100000) ? 0 : (int)sz.width;
160
+ int h = (sz.height < 0 || sz.height > 100000) ? 0 : (int)sz.height;
161
+ struct _Tuple_int_int r;
162
+ r._0 = w;
163
+ r._1 = h;
164
+ return r;
165
+ ```
166
+ ```
167
+ #arch: c, aarch64_use_c
168
+ #platform: ios
169
+ CGSize sz = ((CGSize(*)(id, SEL))objc_msgSend)((id)handle, sel_registerName("intrinsicContentSize"));
170
+ int w = (sz.width < 0 || sz.width > 100000) ? 0 : (int)sz.width;
171
+ int h = (sz.height < 0 || sz.height > 100000) ? 0 : (int)sz.height;
172
+ struct _Tuple_int_int r;
173
+ r._0 = w;
174
+ r._1 = h;
175
+ return r;
176
+ ```
177
+ }
178
+
179
+ /**
180
+ * A flexbox-style container that measures and arranges its child controls.
181
+ *
182
+ * Conforms to `Control`: `measure(BoxConstraints)` measures the whole subtree,
183
+ * `set_frame` arranges it into a rectangle, and `intrinsic_size` reports the
184
+ * unbounded-content size. The handle-based `add` API and the SoA engine are
185
+ * unchanged — the trait methods are thin entry points over the existing math,
186
+ * so a `Container` can also be driven polymorphically as a `Control` (passed to
187
+ * a `Control`-taking function, stored in a `ClassBuffer<Control>`, etc.).
188
+ **/
189
+ pub class Container : Control {
190
+ var kinds = Buffer<int>()
191
+ // Per-leaf requested size, stored as a (kind, value) pair per axis. The
192
+ // kind is one of LEN_* (auto/fixed/percent/fill); the value is the pixel
193
+ // count for `fixed` or the percent numerator for `percent` (0 otherwise).
194
+ // The legacy int-based `add`/`add_to` translate `w`/`h` into these pairs
195
+ // (non-zero → fixed, 0 → auto); `add_len`/`add_to_len` store any case.
196
+ var w_kinds = Buffer<int>()
197
+ var w_vals = Buffer<int>()
198
+ var h_kinds = Buffer<int>()
199
+ var h_vals = Buffer<int>()
200
+ var gaps = Buffer<int>() // container spacing
201
+ var pars = Buffer<int>() // parent index, encoded as parent + 1 (0 = root)
202
+ var spans = Buffer<int>() // grid column span
203
+ var cols = Buffer<int>() // grid column count (on grid nodes)
204
+ var handles = Buffer<int>() // leaf native handle (0 = none/container)
205
+ var pads = Buffer<int>() // per-node inset (padding) for Block
206
+ var grows = Buffer<int>() // flex weight: absorbs surplus on the main axis
207
+ var shrinks = Buffer<int>() // flex weight: absorbs deficit on the main axis
208
+ var aligns = Buffer<int>() // cross-axis alignment (ALIGN_*)
209
+ // Per-node dirty flag for incremental relayout: a dirty node (and its
210
+ // descendants) is re-measured on the next `compute`/`layout`; a clean node
211
+ // returns its cached size. `mark_dirty` sets a node, walks up to the root
212
+ // (ancestors must re-flow) and down through descendants (their constraints
213
+ // may change), so a clean node always has unchanged constraints.
214
+ var dirty = Buffer<int>()
215
+ // How many times a node's `measure` body actually ran — purely for tests
216
+ // that want to observe which subtrees were re-measured.
217
+ var measured_count = Buffer<int>()
218
+ var rx = Buffer<int>()
219
+ var ry = Buffer<int>()
220
+ var rw = Buffer<int>()
221
+ var rh = Buffer<int>()
222
+ // Previously-applied frame per node (leaf only), used by the dirty-rect
223
+ // tracker: a leaf enters the dirty list only when its resolved frame
224
+ // differs from this baseline. Initialised to -1 so the first layout marks
225
+ // every leaf dirty.
226
+ var prx = Buffer<int>()
227
+ var pry = Buffer<int>()
228
+ var prw = Buffer<int>()
229
+ var prh = Buffer<int>()
230
+ // Dirty-rect list: the node indices (leaves) whose frame changed in the
231
+ // most recent `compute`/`layout`, in the order discovered. `dirty_count`
232
+ // / `dirty_rect` expose it; `apply` repaints only these.
233
+ var dirty_idx = Buffer<int>()
234
+ var dirty_n = 0
235
+ var root = 0
236
+ var count = 0
237
+ var padding = 0
238
+ // Last available size passed to `compute`/`layout`. When it changes the
239
+ // whole tree is marked dirty (the external constraint changed); when it's
240
+ // the same, only explicitly `mark_dirty`'d subtrees are re-measured.
241
+ var int last_w = -1
242
+ var int last_h = -1
243
+
244
+ func init_buffers = (ref self, int cap) {
245
+ self.kinds.grow_int(cap)
246
+ self.w_kinds.grow_int(cap)
247
+ self.w_vals.grow_int(cap)
248
+ self.h_kinds.grow_int(cap)
249
+ self.h_vals.grow_int(cap)
250
+ self.gaps.grow_int(cap)
251
+ self.pars.grow_int(cap)
252
+ self.spans.grow_int(cap)
253
+ self.cols.grow_int(cap)
254
+ self.handles.grow_int(cap)
255
+ self.pads.grow_int(cap)
256
+ self.grows.grow_int(cap)
257
+ self.shrinks.grow_int(cap)
258
+ self.aligns.grow_int(cap)
259
+ self.dirty.grow_int(cap)
260
+ self.measured_count.grow_int(cap)
261
+ self.rx.grow_int(cap)
262
+ self.ry.grow_int(cap)
263
+ self.rw.grow_int(cap)
264
+ self.rh.grow_int(cap)
265
+ self.prx.grow_int(cap)
266
+ self.pry.grow_int(cap)
267
+ self.prw.grow_int(cap)
268
+ self.prh.grow_int(cap)
269
+ self.dirty_idx.grow_int(cap)
270
+ }
271
+
272
+ func make_root = (ref self, int kind, int gap, int col_count) {
273
+ self.init_buffers(32)
274
+ self.kinds.store_int(0, kind)
275
+ self.w_kinds.store_int(0, LEN_AUTO)
276
+ self.w_vals.store_int(0, 0)
277
+ self.h_kinds.store_int(0, LEN_AUTO)
278
+ self.h_vals.store_int(0, 0)
279
+ self.gaps.store_int(0, gap)
280
+ self.cols.store_int(0, col_count)
281
+ self.spans.store_int(0, 1)
282
+ self.pars.store_int(0, 0)
283
+ self.handles.store_int(0, 0)
284
+ self.pads.store_int(0, 0)
285
+ self.grows.store_int(0, 0)
286
+ self.shrinks.store_int(0, 0)
287
+ self.aligns.store_int(0, ALIGN_STRETCH)
288
+ self.dirty.store_int(0, 1)
289
+ self.prx.store_int(0, -1)
290
+ self.pry.store_int(0, -1)
291
+ self.prw.store_int(0, -1)
292
+ self.prh.store_int(0, -1)
293
+ self.root = 0
294
+ self.count = 1
295
+ }
296
+
297
+ func first_child = (self, int parent, out int) {
298
+ var int p = parent + 1
299
+ var i = 0
300
+ while i < self.count; i += 1 {
301
+ if self.pars.load_int(i) == p { return i }
302
+ }
303
+ return -1
304
+ }
305
+
306
+ func next_sibling = (self, int after, int parent, out int) {
307
+ var int p = parent + 1
308
+ var int i = after + 1
309
+ while i < self.count; i += 1 {
310
+ if self.pars.load_int(i) == p { return i }
311
+ }
312
+ return -1
313
+ }
314
+
315
+ // Core append: store a node (leaf or container) as a child of `parent`
316
+ // (a node index; use root_index() for the top level), returning the new
317
+ // node's index. All the public add* helpers route through here.
318
+ //
319
+ // `w_kind`/`w_val`/`h_kind`/`h_val` are the per-axis requested size as a
320
+ // (LEN_*, value) pair; only leaves use them (containers size to content),
321
+ // so the leaf helpers set them after this returns. `grow` is the flex
322
+ // weight (surplus on the main axis is shared weighted by it); `shrink` is
323
+ // the deficit weight; `align` is the cross-axis alignment (ALIGN_*).
324
+ // (The aarch64 backend now spills args past the 8-register ABI limit, so
325
+ // this signature no longer has to stay within 8 register params.)
326
+ func append_node = (
327
+ ref self,
328
+ int parent,
329
+ int kind,
330
+ int handle,
331
+ int gap,
332
+ int cols,
333
+ int span,
334
+ int grow,
335
+ int shrink,
336
+ int align,
337
+ out int
338
+ ) {
339
+ if self.kinds.cap < self.count + 1 {
340
+ self.init_buffers(self.count + 1)
341
+ }
342
+ var int idx = self.count
343
+ self.kinds.store_int(idx, kind)
344
+ self.w_kinds.store_int(idx, LEN_AUTO)
345
+ self.w_vals.store_int(idx, 0)
346
+ self.h_kinds.store_int(idx, LEN_AUTO)
347
+ self.h_vals.store_int(idx, 0)
348
+ self.gaps.store_int(idx, gap)
349
+ self.spans.store_int(idx, span)
350
+ self.cols.store_int(idx, cols)
351
+ self.handles.store_int(idx, handle)
352
+ self.grows.store_int(idx, grow)
353
+ self.shrinks.store_int(idx, shrink)
354
+ self.aligns.store_int(idx, align)
355
+ self.pars.store_int(idx, parent + 1)
356
+ self.pads.store_int(idx, 0)
357
+ self.dirty.store_int(idx, 1)
358
+ self.measured_count.store_int(idx, 0)
359
+ // A new child changes the parent's subtree, so mark the parent chain
360
+ // (and the new node itself) dirty so the next measure re-flows them.
361
+ var int p = parent
362
+ while p != -1 {
363
+ self.dirty.store_int(p, 1)
364
+ p = self.pars.load_int(p) - 1
365
+ }
366
+ self.prx.store_int(idx, -1)
367
+ self.pry.store_int(idx, -1)
368
+ self.prw.store_int(idx, -1)
369
+ self.prh.store_int(idx, -1)
370
+ self.count = self.count + 1
371
+ return idx
372
+ }
373
+
374
+ // Index of the root node — pass this as `parent` to add children to the
375
+ // top level (the plain `add` helper already does this for leaves).
376
+ pub func root_index = (self, out int) {
377
+ return self.root
378
+ }
379
+
380
+ // Add a leaf child (a native control by handle) under the root. `w`/`h` of
381
+ // 0 mean "fill" on the cross axis (or "no intrinsic opinion" on the main
382
+ // axis, where `grow` then absorbs the surplus); a non-zero `w`/`h` is an
383
+ // exact pixel size. `span` is the grid column span (use 1 for
384
+ // VStack/HStack). `grow` is the flex weight for sharing surplus space on
385
+ // the parent's main axis (0 = hold at intrinsic size); `align` is the
386
+ // cross-axis alignment (default ALIGN_STRETCH = 3, the original "fill the
387
+ // cross axis" behavior); `shrink` is the flex weight for sharing a deficit
388
+ // when the children's total exceeds the main-axis content size (0 = never
389
+ // shrink, the original behavior). For `.percent` / `.fill` / mixed sizing,
390
+ // use `add_len`.
391
+ pub func add = (
392
+ ref self,
393
+ uint64 handle,
394
+ int w,
395
+ int h,
396
+ int span,
397
+ int grow = 0,
398
+ int align = 3,
399
+ int shrink = 0
400
+ ) {
401
+ var int idx = self.append_node(
402
+ self.root,
403
+ KIND_LEAF,
404
+ handle as int,
405
+ 0,
406
+ 0,
407
+ span,
408
+ grow,
409
+ shrink,
410
+ align,
411
+ )
412
+ self.set_leaf_size(idx, w, h)
413
+ }
414
+
415
+ // Add a leaf child under an explicit `parent` (a node index returned by an
416
+ // add_vstack/add_hstack/add_grid/add_zstack call).
417
+ pub func add_to = (
418
+ ref self,
419
+ int parent,
420
+ uint64 handle,
421
+ int w,
422
+ int h,
423
+ int span,
424
+ int grow = 0,
425
+ int align = 3,
426
+ int shrink = 0
427
+ ) {
428
+ var int idx = self.append_node(
429
+ parent,
430
+ KIND_LEAF,
431
+ handle as int,
432
+ 0,
433
+ 0,
434
+ span,
435
+ grow,
436
+ shrink,
437
+ align,
438
+ )
439
+ self.set_leaf_size(idx, w, h)
440
+ }
441
+
442
+ // Add a leaf child under the root with an explicit per-axis size kind.
443
+ // `w_kind`/`h_kind` are `LEN_*` constants (LEN_AUTO/LEN_FIXED/LEN_PERCENT/
444
+ // LEN_FILL); `w_val`/`h_val` are the pixel count for LEN_FIXED or the
445
+ // percent numerator (0-100) for LEN_PERCENT — ignored for LEN_AUTO/
446
+ // LEN_FILL. This is the explicit-size escape hatch when neither `add`
447
+ // (which only expresses fixed/auto) nor a future LayoutLength-taking
448
+ // helper fits; e.g. `add_kind(0, LEN_PERCENT, 50, LEN_FIXED, 30, 1)`
449
+ // asks for 50% of the cross axis and a fixed 30px on the main axis.
450
+ // `span`/`grow`/`align`/`shrink` match `add`.
451
+ pub func add_kind = (
452
+ ref self,
453
+ uint64 handle,
454
+ int w_kind,
455
+ int w_val,
456
+ int h_kind,
457
+ int h_val,
458
+ int span,
459
+ int grow = 0,
460
+ int align = 3,
461
+ int shrink = 0
462
+ ) {
463
+ var int idx = self.append_node(
464
+ self.root,
465
+ KIND_LEAF,
466
+ handle as int,
467
+ 0,
468
+ 0,
469
+ span,
470
+ grow,
471
+ shrink,
472
+ align,
473
+ )
474
+ self.set_leaf_kind(idx, w_kind, w_val, h_kind, h_val)
475
+ }
476
+
477
+ // Add a leaf child with explicit per-axis size kinds under an explicit
478
+ // `parent` (a node index returned by an add_* container helper).
479
+ pub func add_to_kind = (
480
+ ref self,
481
+ int parent,
482
+ uint64 handle,
483
+ int w_kind,
484
+ int w_val,
485
+ int h_kind,
486
+ int h_val,
487
+ int span,
488
+ int grow = 0,
489
+ int align = 3,
490
+ int shrink = 0
491
+ ) {
492
+ var int idx = self.append_node(
493
+ parent,
494
+ KIND_LEAF,
495
+ handle as int,
496
+ 0,
497
+ 0,
498
+ span,
499
+ grow,
500
+ shrink,
501
+ align,
502
+ )
503
+ self.set_leaf_kind(idx, w_kind, w_val, h_kind, h_val)
504
+ }
505
+
506
+ // Add a leaf child under the root, passing `LayoutLength` values directly:
507
+ // `v.add_len(handle, LayoutLength.percent(50), LayoutLength.fixed(30), 1)`.
508
+ // The ergonomic form of `add_kind` — each axis's enum case is translated
509
+ // to the internal (kind, value) pair. `span`/`grow`/`align`/`shrink`
510
+ // match `add`.
511
+ pub func add_len = (
512
+ ref self,
513
+ uint64 handle,
514
+ LayoutLength w,
515
+ LayoutLength h,
516
+ int span,
517
+ int grow = 0,
518
+ int align = 3,
519
+ int shrink = 0
520
+ ) {
521
+ var int idx = self.append_node(
522
+ self.root,
523
+ KIND_LEAF,
524
+ handle as int,
525
+ 0,
526
+ 0,
527
+ span,
528
+ grow,
529
+ shrink,
530
+ align,
531
+ )
532
+ self.set_leaf_kind(idx, length_kind(w), length_val(w), length_kind(h), length_val(h))
533
+ }
534
+
535
+ // Add a leaf child with `LayoutLength` sizes under an explicit `parent`
536
+ // (a node index returned by an add_* container helper).
537
+ pub func add_to_len = (
538
+ ref self,
539
+ int parent,
540
+ uint64 handle,
541
+ LayoutLength w,
542
+ LayoutLength h,
543
+ int span,
544
+ int grow = 0,
545
+ int align = 3,
546
+ int shrink = 0
547
+ ) {
548
+ var int idx = self.append_node(
549
+ parent,
550
+ KIND_LEAF,
551
+ handle as int,
552
+ 0,
553
+ 0,
554
+ span,
555
+ grow,
556
+ shrink,
557
+ align,
558
+ )
559
+ self.set_leaf_kind(idx, length_kind(w), length_val(w), length_kind(h), length_val(h))
560
+ }
561
+
562
+ // Add a leaf that sizes to its native control's `intrinsicContentSize` on
563
+ // both axes — the "no size hints" form. Equivalent to
564
+ // `add_kind(handle, LEN_INTRINSIC, 0, LEN_INTRINSIC, 0, span, …)`. For
565
+ // per-axis intrinsic (e.g. fill the cross axis, intrinsic height) use
566
+ // `add_kind` directly. `span`/`grow`/`align`/`shrink` match `add`.
567
+ pub func add_intrinsic = (
568
+ ref self,
569
+ uint64 handle,
570
+ int span,
571
+ int grow = 0,
572
+ int align = 3,
573
+ int shrink = 0
574
+ ) {
575
+ var int idx = self.append_node(
576
+ self.root,
577
+ KIND_LEAF,
578
+ handle as int,
579
+ 0,
580
+ 0,
581
+ span,
582
+ grow,
583
+ shrink,
584
+ align,
585
+ )
586
+ self.set_leaf_kind(idx, LEN_INTRINSIC, 0, LEN_INTRINSIC, 0)
587
+ }
588
+
589
+ // `add_intrinsic` under an explicit `parent` (a node index returned by an
590
+ // add_* container helper).
591
+ pub func add_to_intrinsic = (
592
+ ref self,
593
+ int parent,
594
+ uint64 handle,
595
+ int span,
596
+ int grow = 0,
597
+ int align = 3,
598
+ int shrink = 0
599
+ ) {
600
+ var int idx = self.append_node(
601
+ parent,
602
+ KIND_LEAF,
603
+ handle as int,
604
+ 0,
605
+ 0,
606
+ span,
607
+ grow,
608
+ shrink,
609
+ align,
610
+ )
611
+ self.set_leaf_kind(idx, LEN_INTRINSIC, 0, LEN_INTRINSIC, 0)
612
+ }
613
+
614
+ // Translate the legacy int (w, h) into a (kind, value) pair: 0 → auto
615
+ // (no intrinsic; fills a bounded axis, stays 0 on an unbounded axis so
616
+ // `grow` decides), non-zero → fixed.
617
+ func set_leaf_size = (ref self, int idx, int w, int h) {
618
+ if w == 0 {
619
+ self.w_kinds.store_int(idx, LEN_AUTO)
620
+ self.w_vals.store_int(idx, 0)
621
+ } else {
622
+ self.w_kinds.store_int(idx, LEN_FIXED)
623
+ self.w_vals.store_int(idx, w)
624
+ }
625
+ if h == 0 {
626
+ self.h_kinds.store_int(idx, LEN_AUTO)
627
+ self.h_vals.store_int(idx, 0)
628
+ } else {
629
+ self.h_kinds.store_int(idx, LEN_FIXED)
630
+ self.h_vals.store_int(idx, h)
631
+ }
632
+ }
633
+
634
+ // Store the per-axis (kind, value) pair directly.
635
+ func set_leaf_kind = (ref self, int idx, int w_kind, int w_val, int h_kind, int h_val) {
636
+ self.w_kinds.store_int(idx, w_kind)
637
+ self.w_vals.store_int(idx, w_val)
638
+ self.h_kinds.store_int(idx, h_kind)
639
+ self.h_vals.store_int(idx, h_val)
640
+ }
641
+
642
+ // Add a nested container under `parent`; returns the new node's index so
643
+ // children can be added to it via add_to (or another nested container).
644
+ pub func add_vstack = (
645
+ ref self,
646
+ int parent,
647
+ int spacing,
648
+ int span,
649
+ int grow = 0,
650
+ int align = 3,
651
+ int shrink = 0,
652
+ out int
653
+ ) {
654
+ return self.append_node(parent, KIND_VSTACK, 0, spacing, 0, span, grow, shrink, align)
655
+ }
656
+
657
+ pub func add_hstack = (
658
+ ref self,
659
+ int parent,
660
+ int spacing,
661
+ int span,
662
+ int grow = 0,
663
+ int align = 3,
664
+ int shrink = 0,
665
+ out int
666
+ ) {
667
+ return self.append_node(parent, KIND_HSTACK, 0, spacing, 0, span, grow, shrink, align)
668
+ }
669
+
670
+ // Add a spacer: a flexible empty leaf (no native handle) that simply
671
+ // occupies layout space. With `grow > 0` it absorbs the surplus on the
672
+ // parent's main axis — the classic "push the rest of the row/column to the
673
+ // far edge" glue; with `shrink > 0` it yields space in a deficit. It sizes
674
+ // to 0 on an unbounded axis (so it has no intrinsic opinion) and fills the
675
+ // cross axis by default. Because it carries no handle, `apply` never calls
676
+ // `setFrame:` for it and `hit_test` reports it as "no control" (handle 0).
677
+ pub func add_spacer = (ref self, int span, int grow = 0, int shrink = 0, int align = 3) {
678
+ self.append_node(self.root, KIND_LEAF, 0, 0, 0, span, grow, shrink, align)
679
+ }
680
+
681
+ pub func add_spacer_to = (
682
+ ref self,
683
+ int parent,
684
+ int span,
685
+ int grow = 0,
686
+ int shrink = 0,
687
+ int align = 3
688
+ ) {
689
+ self.append_node(parent, KIND_LEAF, 0, 0, 0, span, grow, shrink, align)
690
+ }
691
+
692
+ pub func add_grid = (
693
+ ref self,
694
+ int parent,
695
+ int cols: cols > 0,
696
+ int spacing,
697
+ int span,
698
+ int grow = 0,
699
+ int align = 3,
700
+ int shrink = 0,
701
+ out int
702
+ ) {
703
+ return self.append_node(parent, KIND_GRID, 0, spacing, cols, span, grow, shrink, align)
704
+ }
705
+
706
+ pub func add_zstack = (
707
+ ref self,
708
+ int parent,
709
+ int span,
710
+ int grow = 0,
711
+ int align = 3,
712
+ int shrink = 0,
713
+ out int
714
+ ) {
715
+ return self.append_node(parent, KIND_ZSTACK, 0, 0, 0, span, grow, shrink, align)
716
+ }
717
+
718
+ // Add a Block: a single-child container with insets (padding) on all four
719
+ // sides. It forwards its content rect (frame minus 2·padding) to its one
720
+ // child via `add_to(block, …)`. `measure` subtracts the insets from the
721
+ // constraints and returns the child size plus the insets; `arrange` places
722
+ // the child inside the inset content rect. Like the other containers this
723
+ // is a node in the SoA tree, so it needs no trait model — it recurses
724
+ // through `first_child`/`next_sibling` like everything else.
725
+ pub func add_block = (
726
+ ref self,
727
+ int padding,
728
+ int span,
729
+ int grow = 0,
730
+ int align = 3,
731
+ int shrink = 0,
732
+ out int
733
+ ) {
734
+ var int idx = self.append_node(self.root, KIND_BLOCK, 0, 0, 0, span, grow, shrink, align)
735
+ self.pads.store_int(idx, padding)
736
+ return idx
737
+ }
738
+
739
+ pub func add_block_to = (
740
+ ref self,
741
+ int parent,
742
+ int padding,
743
+ int span,
744
+ int grow = 0,
745
+ int align = 3,
746
+ int shrink = 0,
747
+ out int
748
+ ) {
749
+ var int idx = self.append_node(parent, KIND_BLOCK, 0, 0, 0, span, grow, shrink, align)
750
+ self.pads.store_int(idx, padding)
751
+ return idx
752
+ }
753
+
754
+ // Measure a node against box constraints. Returns the resolved width and
755
+ // stores the resolved height in rh[idx] (and the width in rw[idx]).
756
+ func measure_node = (self, int idx, int min_w, int max_w, int min_h, int max_h, out int) {
757
+ var int kind = self.kinds.load_int(idx)
758
+ // Incremental relayout: a clean node's constraints are unchanged, so
759
+ // reuse its cached size instead of re-measuring the subtree. The cached
760
+ // height is already in rh[idx] from the previous pass, so the caller's
761
+ // `self.rh.load_int(ci)` picks it up without a re-measure.
762
+ if self.dirty.load_int(idx) == 0 {
763
+ var int cached = self.rw.load_int(idx)
764
+ return cached
765
+ }
766
+ self.measured_count.store_int(idx, self.measured_count.load_int(idx) + 1)
767
+
768
+ if kind == KIND_LEAF {
769
+ // Resolve the per-axis `LayoutLength` (kind, value) against the
770
+ // constraints: `.fixed` is exact, `.percent` is p% of the bounded
771
+ // max (0 on an unbounded axis), `.fill`/`.auto` fill a bounded axis
772
+ // and stay 0 on an unbounded one (so `grow` then absorbs surplus
773
+ // rather than a phantom INF), `.intrinsic` asks the native control
774
+ // for its `intrinsicContentSize` on that axis (0 for handle 0). All
775
+ // results are then clamped to the min/max range. (Nomen has no
776
+ // `else if` — the cases are `switch`es.)
777
+ var int wk = self.w_kinds.load_int(idx)
778
+ var int wv = self.w_vals.load_int(idx)
779
+ var int hk = self.h_kinds.load_int(idx)
780
+ var int hv = self.h_vals.load_int(idx)
781
+ var uint64 ih = self.handles.load_int(idx) as uint64
782
+
783
+ // A single intrinsic query returns both axes, so a `LEN_INTRINSIC`
784
+ // leaf makes at most one native call per dirty measure pass.
785
+ var intr_w = 0
786
+ var intr_h = 0
787
+ if wk == LEN_INTRINSIC || hk == LEN_INTRINSIC {
788
+ var [iw, ih2] = native_intrinsic(ih)
789
+ intr_w = iw
790
+ intr_h = ih2
791
+ }
792
+
793
+ var w = 0
794
+ switch {
795
+ case wk == LEN_FIXED -> w = wv
796
+ case wk == LEN_PERCENT -> if max_w < INF { w = max_w * wv / 100 }
797
+ case wk == LEN_FILL -> if max_w < INF { w = max_w }
798
+ case wk == LEN_AUTO -> if max_w < INF { w = max_w }
799
+ case wk == LEN_INTRINSIC -> w = intr_w
800
+ }
801
+
802
+ var h = 0
803
+ switch {
804
+ case hk == LEN_FIXED -> h = hv
805
+ case hk == LEN_PERCENT -> if max_h < INF { h = max_h * hv / 100 }
806
+ case hk == LEN_FILL -> if max_h < INF { h = max_h }
807
+ case hk == LEN_AUTO -> if max_h < INF { h = max_h }
808
+ case hk == LEN_INTRINSIC -> h = intr_h
809
+ }
810
+
811
+ if w > max_w { w = max_w }
812
+ if w < min_w { w = min_w }
813
+ if h > max_h { h = max_h }
814
+ if h < min_h { h = min_h }
815
+ self.rw.store_int(idx, w)
816
+ self.rh.store_int(idx, h)
817
+ self.dirty.store_int(idx, 0)
818
+ return w
819
+ }
820
+
821
+ if kind == KIND_VSTACK {
822
+ var total_h = 0
823
+ var max_child_w = 0
824
+ var n = 0
825
+ var int ci = self.first_child(idx)
826
+ while ci != -1 {
827
+ if is_visible(self.handles.load_int(ci) as uint64) {
828
+ var int cw = self.measure_node(ci, min_w, max_w, 0, INF)
829
+ var int ch = self.rh.load_int(ci)
830
+ total_h = total_h + ch
831
+ if cw > max_child_w { max_child_w = cw }
832
+ n += 1
833
+ }
834
+ ci = self.next_sibling(ci, idx)
835
+ }
836
+ if n > 1 { total_h = total_h + self.gaps.load_int(idx) * (n - 1) }
837
+ if total_h < min_h { total_h = min_h }
838
+ if total_h > max_h { total_h = max_h }
839
+ if max_child_w < min_w { max_child_w = min_w }
840
+ if max_child_w > max_w { max_child_w = max_w }
841
+ self.rw.store_int(idx, max_child_w)
842
+ self.rh.store_int(idx, total_h)
843
+ self.dirty.store_int(idx, 0)
844
+ return max_child_w
845
+ }
846
+
847
+ if kind == KIND_HSTACK {
848
+ var total_w = 0
849
+ var max_child_h = 0
850
+ var n = 0
851
+ var int ci = self.first_child(idx)
852
+ while ci != -1 {
853
+ if is_visible(self.handles.load_int(ci) as uint64) {
854
+ var int cw = self.measure_node(ci, 0, INF, min_h, max_h)
855
+ var int ch = self.rh.load_int(ci)
856
+ total_w = total_w + cw
857
+ if ch > max_child_h { max_child_h = ch }
858
+ n += 1
859
+ }
860
+ ci = self.next_sibling(ci, idx)
861
+ }
862
+ if n > 1 { total_w = total_w + self.gaps.load_int(idx) * (n - 1) }
863
+ if total_w < min_w { total_w = min_w }
864
+ if total_w > max_w { total_w = max_w }
865
+ if max_child_h < min_h { max_child_h = min_h }
866
+ if max_child_h > max_h { max_child_h = max_h }
867
+ self.rw.store_int(idx, total_w)
868
+ self.rh.store_int(idx, max_child_h)
869
+ self.dirty.store_int(idx, 0)
870
+ return total_w
871
+ }
872
+
873
+ if kind == KIND_ZSTACK {
874
+ var best_w = 0
875
+ var best_h = 0
876
+ var int ci = self.first_child(idx)
877
+ while ci != -1 {
878
+ if is_visible(self.handles.load_int(ci) as uint64) {
879
+ self.measure_node(ci, min_w, max_w, min_h, max_h)
880
+ var int cw = self.rw.load_int(ci)
881
+ var int ch = self.rh.load_int(ci)
882
+ if cw > best_w { best_w = cw }
883
+ if ch > best_h { best_h = ch }
884
+ }
885
+ ci = self.next_sibling(ci, idx)
886
+ }
887
+ if best_w < min_w { best_w = min_w }
888
+ if best_w > max_w { best_w = max_w }
889
+ if best_h < min_h { best_h = min_h }
890
+ if best_h > max_h { best_h = max_h }
891
+ self.rw.store_int(idx, best_w)
892
+ self.rh.store_int(idx, best_h)
893
+ self.dirty.store_int(idx, 0)
894
+ return best_w
895
+ }
896
+
897
+ // KIND_BLOCK: a single-child rect with insets. Subtract the padding from
898
+ // the constraints, measure the (first) child into the inner box, and
899
+ // return the child size plus the insets on both axes. A Block with no
900
+ // child resolves to just its padding (2·pad on each axis).
901
+ if kind == KIND_BLOCK {
902
+ var int pad = self.pads.load_int(idx)
903
+ var int inner_min_w = min_w - pad * 2
904
+ if inner_min_w < 0 { inner_min_w = 0 }
905
+ var int inner_max_w = max_w - pad * 2
906
+ if inner_max_w < 0 { inner_max_w = 0 }
907
+ var int inner_min_h = min_h - pad * 2
908
+ if inner_min_h < 0 { inner_min_h = 0 }
909
+ var int inner_max_h = max_h - pad * 2
910
+ if inner_max_h < 0 { inner_max_h = 0 }
911
+ var cw = 0
912
+ var ch = 0
913
+ var int ci = self.first_child(idx)
914
+ if ci != -1 {
915
+ cw = self.measure_node(ci, inner_min_w, inner_max_w, inner_min_h, inner_max_h)
916
+ ch = self.rh.load_int(ci)
917
+ }
918
+ var int total_w = cw + pad * 2
919
+ var int total_h = ch + pad * 2
920
+ if total_w < min_w { total_w = min_w }
921
+ if total_w > max_w { total_w = max_w }
922
+ if total_h < min_h { total_h = min_h }
923
+ if total_h > max_h { total_h = max_h }
924
+ self.rw.store_int(idx, total_w)
925
+ self.rh.store_int(idx, total_h)
926
+ self.dirty.store_int(idx, 0)
927
+ return total_w
928
+ }
929
+
930
+ // KIND_GRID: children flow into cells; each may span columns.
931
+ var int cols = self.cols.load_int(idx)
932
+ var int col_w = max_w / cols
933
+ var total_h = 0
934
+ var row_h = 0
935
+ var col_cursor = 0
936
+ var any = 0
937
+ var int ci = self.first_child(idx)
938
+ while ci != -1 {
939
+ if is_visible(self.handles.load_int(ci) as uint64) {
940
+ var int span = self.spans.load_int(ci)
941
+ if span < 1 { span = 1 }
942
+ if span > cols { span = cols }
943
+ if col_cursor + span > cols {
944
+ total_h = total_h + row_h
945
+ if any > 0 { total_h = total_h + self.gaps.load_int(idx) }
946
+ row_h = 0
947
+ col_cursor = 0
948
+ }
949
+ var int cell_w = span * col_w
950
+ self.measure_node(ci, cell_w, cell_w, 0, INF)
951
+ var int ch = self.rh.load_int(ci)
952
+ if ch > row_h { row_h = ch }
953
+ col_cursor = col_cursor + span
954
+ any += 1
955
+ }
956
+ ci = self.next_sibling(ci, idx)
957
+ }
958
+ if any > 0 {
959
+ total_h = total_h + row_h
960
+ }
961
+ if total_h < min_h { total_h = min_h }
962
+ if total_h > max_h { total_h = max_h }
963
+ self.rw.store_int(idx, cols * col_w)
964
+ self.rh.store_int(idx, total_h)
965
+ self.dirty.store_int(idx, 0)
966
+ return cols * col_w
967
+ }
968
+
969
+ func arrange_node = (self, int idx, int x, int y, int w, int h) {
970
+ self.rx.store_int(idx, x)
971
+ self.ry.store_int(idx, y)
972
+ self.rw.store_int(idx, w)
973
+ self.rh.store_int(idx, h)
974
+
975
+ var int kind = self.kinds.load_int(idx)
976
+ if kind == KIND_LEAF { return }
977
+
978
+ if kind == KIND_VSTACK {
979
+ var int gap = self.gaps.load_int(idx)
980
+ // Tally children's measured heights, total grow weight, and total
981
+ // shrink weight so we can share any surplus (grow) or deficit
982
+ // (shrink) on the main (vertical) axis between flex children.
983
+ var total_h = 0
984
+ var total_grow = 0
985
+ var total_shrink = 0
986
+ var n = 0
987
+ var int ci = self.first_child(idx)
988
+ while ci != -1 {
989
+ if is_visible(self.handles.load_int(ci) as uint64) {
990
+ total_h = total_h + self.rh.load_int(ci)
991
+ var int g = self.grows.load_int(ci)
992
+ if g > 0 { total_grow = total_grow + g }
993
+ var int s = self.shrinks.load_int(ci)
994
+ if s > 0 { total_shrink = total_shrink + s }
995
+ n += 1
996
+ }
997
+ ci = self.next_sibling(ci, idx)
998
+ }
999
+ if n > 1 { total_h = total_h + gap * (n - 1) }
1000
+ var int surplus = h - total_h
1001
+ // Surplus (grow) and deficit (shrink) are mutually exclusive — at
1002
+ // most one of `surplus > 0` / `surplus < 0` holds — so they're two
1003
+ // independent top-level ifs rather than an else-if chain.
1004
+ if total_grow > 0 && surplus > 0 {
1005
+ var int ci2 = self.first_child(idx)
1006
+ while ci2 != -1 {
1007
+ if is_visible(self.handles.load_int(ci2) as uint64) {
1008
+ var int g = self.grows.load_int(ci2)
1009
+ if g > 0 {
1010
+ self.rh.store_int(ci2, self.rh.load_int(ci2) + surplus * g / total_grow)
1011
+ }
1012
+ }
1013
+ ci2 = self.next_sibling(ci2, idx)
1014
+ }
1015
+ }
1016
+ // Distribute the deficit weighted by `shrink`. Each shrinkable child
1017
+ // loses `deficit * shrink / total_shrink` pixels, floored at 0 so
1018
+ // an over-weighted sibling can't drag another negative.
1019
+ if total_shrink > 0 && surplus < 0 {
1020
+ var int deficit = 0 - surplus
1021
+ var int ci2 = self.first_child(idx)
1022
+ while ci2 != -1 {
1023
+ if is_visible(self.handles.load_int(ci2) as uint64) {
1024
+ var int s = self.shrinks.load_int(ci2)
1025
+ if s > 0 {
1026
+ var int new_h = self.rh.load_int(ci2) - deficit * s / total_shrink
1027
+ if new_h < 0 { new_h = 0 }
1028
+ self.rh.store_int(ci2, new_h)
1029
+ }
1030
+ }
1031
+ ci2 = self.next_sibling(ci2, idx)
1032
+ }
1033
+ }
1034
+ // Place children top-to-bottom; cross (width) axis per alignment.
1035
+ var int running_y = y
1036
+ var int ci3 = self.first_child(idx)
1037
+ while ci3 != -1 {
1038
+ if is_visible(self.handles.load_int(ci3) as uint64) {
1039
+ var int ch = self.rh.load_int(ci3)
1040
+ var int cw = self.rw.load_int(ci3)
1041
+ var int cx = x
1042
+ var int align = self.aligns.load_int(ci3)
1043
+ if align == ALIGN_STRETCH {
1044
+ cw = w
1045
+ }
1046
+ if align == ALIGN_CENTER {
1047
+ cx = x + (w - cw) / 2
1048
+ }
1049
+ if align == ALIGN_END {
1050
+ cx = x + w - cw
1051
+ }
1052
+ self.arrange_node(ci3, cx, running_y, cw, ch)
1053
+ running_y = running_y + ch + gap
1054
+ }
1055
+ ci3 = self.next_sibling(ci3, idx)
1056
+ }
1057
+ return
1058
+ }
1059
+
1060
+ if kind == KIND_HSTACK {
1061
+ var int gap = self.gaps.load_int(idx)
1062
+ // Tally children's measured widths, total grow weight, and total
1063
+ // shrink weight so we can share any surplus (grow) or deficit
1064
+ // (shrink) on the main (horizontal) axis between flex children.
1065
+ var total_w = 0
1066
+ var total_grow = 0
1067
+ var total_shrink = 0
1068
+ var n = 0
1069
+ var int ci = self.first_child(idx)
1070
+ while ci != -1 {
1071
+ if is_visible(self.handles.load_int(ci) as uint64) {
1072
+ total_w = total_w + self.rw.load_int(ci)
1073
+ var int g = self.grows.load_int(ci)
1074
+ if g > 0 { total_grow = total_grow + g }
1075
+ var int s = self.shrinks.load_int(ci)
1076
+ if s > 0 { total_shrink = total_shrink + s }
1077
+ n += 1
1078
+ }
1079
+ ci = self.next_sibling(ci, idx)
1080
+ }
1081
+ if n > 1 { total_w = total_w + gap * (n - 1) }
1082
+ var int surplus = w - total_w
1083
+ // Surplus (grow) and deficit (shrink) are mutually exclusive — see
1084
+ // the VStack branch above for the rationale.
1085
+ if total_grow > 0 && surplus > 0 {
1086
+ var int ci2 = self.first_child(idx)
1087
+ while ci2 != -1 {
1088
+ if is_visible(self.handles.load_int(ci2) as uint64) {
1089
+ var int g = self.grows.load_int(ci2)
1090
+ if g > 0 {
1091
+ self.rw.store_int(ci2, self.rw.load_int(ci2) + surplus * g / total_grow)
1092
+ }
1093
+ }
1094
+ ci2 = self.next_sibling(ci2, idx)
1095
+ }
1096
+ }
1097
+ // Mirror of the VStack path: distribute the deficit weighted by
1098
+ // `shrink`, floored at 0.
1099
+ if total_shrink > 0 && surplus < 0 {
1100
+ var int deficit = 0 - surplus
1101
+ var int ci2 = self.first_child(idx)
1102
+ while ci2 != -1 {
1103
+ if is_visible(self.handles.load_int(ci2) as uint64) {
1104
+ var int s = self.shrinks.load_int(ci2)
1105
+ if s > 0 {
1106
+ var int new_w = self.rw.load_int(ci2) - deficit * s / total_shrink
1107
+ if new_w < 0 { new_w = 0 }
1108
+ self.rw.store_int(ci2, new_w)
1109
+ }
1110
+ }
1111
+ ci2 = self.next_sibling(ci2, idx)
1112
+ }
1113
+ }
1114
+ var int running_x = x
1115
+ var int ci3 = self.first_child(idx)
1116
+ while ci3 != -1 {
1117
+ if is_visible(self.handles.load_int(ci3) as uint64) {
1118
+ var int cw = self.rw.load_int(ci3)
1119
+ var int ch = self.rh.load_int(ci3)
1120
+ var int cy = y
1121
+ var int align = self.aligns.load_int(ci3)
1122
+ if align == ALIGN_STRETCH {
1123
+ ch = h
1124
+ }
1125
+ if align == ALIGN_CENTER {
1126
+ cy = y + (h - ch) / 2
1127
+ }
1128
+ if align == ALIGN_END {
1129
+ cy = y + h - ch
1130
+ }
1131
+ self.arrange_node(ci3, running_x, cy, cw, ch)
1132
+ running_x = running_x + cw + gap
1133
+ }
1134
+ ci3 = self.next_sibling(ci3, idx)
1135
+ }
1136
+ return
1137
+ }
1138
+
1139
+ if kind == KIND_ZSTACK {
1140
+ var int ci = self.first_child(idx)
1141
+ while ci != -1 {
1142
+ if is_visible(self.handles.load_int(ci) as uint64) {
1143
+ self.arrange_node(ci, x, y, w, h)
1144
+ }
1145
+ ci = self.next_sibling(ci, idx)
1146
+ }
1147
+ return
1148
+ }
1149
+
1150
+ // KIND_BLOCK: forward the content rect (frame minus insets) to the single
1151
+ // child. The node's own frame is already stored above.
1152
+ if kind == KIND_BLOCK {
1153
+ var int bpad = self.pads.load_int(idx)
1154
+ var int ci = self.first_child(idx)
1155
+ if ci != -1 {
1156
+ self.arrange_node(ci, x + bpad, y + bpad, w - bpad * 2, h - bpad * 2)
1157
+ }
1158
+ return
1159
+ }
1160
+
1161
+ // KIND_GRID
1162
+ var int cols = self.cols.load_int(idx)
1163
+ var int col_w = w / cols
1164
+ var int running_y = y
1165
+ var row_h = 0
1166
+ var col_cursor = 0
1167
+ var int ci = self.first_child(idx)
1168
+ while ci != -1 {
1169
+ if is_visible(self.handles.load_int(ci) as uint64) {
1170
+ var int span = self.spans.load_int(ci)
1171
+ if span < 1 { span = 1 }
1172
+ if span > cols { span = cols }
1173
+ if col_cursor + span > cols {
1174
+ running_y = running_y + row_h + self.gaps.load_int(idx)
1175
+ row_h = 0
1176
+ col_cursor = 0
1177
+ }
1178
+ var int child_x = x + col_cursor * col_w
1179
+ var int child_w = span * col_w
1180
+ var int child_h = self.rh.load_int(ci)
1181
+ self.arrange_node(ci, child_x, running_y, child_w, child_h)
1182
+ if child_h > row_h { row_h = child_h }
1183
+ col_cursor = col_cursor + span
1184
+ }
1185
+ ci = self.next_sibling(ci, idx)
1186
+ }
1187
+ }
1188
+
1189
+ // Recompute the dirty-rect list: any leaf whose resolved frame differs
1190
+ // from its previously-applied frame is appended to `dirty_idx` (in tree
1191
+ // order) and its baseline is advanced. Hidden leaves keep a stable
1192
+ // (0,0,0,0) frame, so toggling visibility and re-laying-out marks them
1193
+ // dirty exactly once, like any other frame change.
1194
+ func collect_dirty = (ref self) {
1195
+ self.dirty_n = 0
1196
+ var i = 0
1197
+ while i < self.count; i += 1 {
1198
+ if self.kinds.load_int(i) == KIND_LEAF {
1199
+ var int x = self.rx.load_int(i)
1200
+ var int y = self.ry.load_int(i)
1201
+ var int w = self.rw.load_int(i)
1202
+ var int h = self.rh.load_int(i)
1203
+ var int px = self.prx.load_int(i)
1204
+ var int py = self.pry.load_int(i)
1205
+ var int pw = self.prw.load_int(i)
1206
+ var int ph = self.prh.load_int(i)
1207
+ if x != px || y != py || w != pw || h != ph {
1208
+ if self.dirty_idx.cap < self.dirty_n + 1 {
1209
+ self.dirty_idx.grow_int(self.dirty_n + 1)
1210
+ }
1211
+ self.dirty_idx.store_int(self.dirty_n, i)
1212
+ self.dirty_n = self.dirty_n + 1
1213
+ self.prx.store_int(i, x)
1214
+ self.pry.store_int(i, y)
1215
+ self.prw.store_int(i, w)
1216
+ self.prh.store_int(i, h)
1217
+ }
1218
+ }
1219
+ }
1220
+ }
1221
+
1222
+ // Number of leaves whose frame changed in the most recent `compute`/
1223
+ // `layout` (the current dirty-rect list length).
1224
+ pub func dirty_count = (self, out int) {
1225
+ return self.dirty_n
1226
+ }
1227
+
1228
+ // The i-th dirty rectangle (new frame) as "x,y,w,h", where i is in
1229
+ // [0, dirty_count). The rect is the leaf's current resolved frame — the
1230
+ // region the compositor must repaint.
1231
+ pub func dirty_rect = (self, int i, out string) {
1232
+ var int idx = self.dirty_idx.load_int(i)
1233
+ return self.rx.load_int(idx).to_string() + "," + self.ry.load_int(idx).to_string() + "," + self.rw.load_int(idx).to_string() + "," + self.rh.load_int(idx).to_string()
1234
+ }
1235
+
1236
+ // Mark a node dirty for incremental relayout: the node, its ancestors (their
1237
+ // size may change as a result), and its descendants (their constraints flow
1238
+ // down from this node) are all re-measured on the next `compute`/`layout`.
1239
+ // A node left clean is known to have unchanged constraints, so its cached
1240
+ // size is reused.
1241
+ pub func mark_dirty = (ref self, int idx) {
1242
+ self.dirty.store_int(idx, 1)
1243
+ var int ci = self.first_child(idx)
1244
+ while ci != -1 {
1245
+ self.mark_dirty(ci)
1246
+ ci = self.next_sibling(ci, idx)
1247
+ }
1248
+ var int p = self.pars.load_int(idx) - 1
1249
+ while p != -1 {
1250
+ self.dirty.store_int(p, 1)
1251
+ p = self.pars.load_int(p) - 1
1252
+ }
1253
+ }
1254
+
1255
+ // How many times a node's `measure` body has actually run (incremental
1256
+ // relayout lets this stay put for clean subtrees). Test/observability aid.
1257
+ pub func measure_count = (self, int i, out int) {
1258
+ return self.measured_count.load_int(i)
1259
+ }
1260
+
1261
+ func apply = (self, int content_w, int content_h) {
1262
+ // Only repaint the dirty leaves — the compositor's dirty-rect win.
1263
+ var k = 0
1264
+ while k < self.dirty_n; k += 1 {
1265
+ var int i = self.dirty_idx.load_int(k)
1266
+ var uint64 handle = self.handles.load_int(i) as uint64
1267
+ if handle != 0 {
1268
+ apply_frame(
1269
+ handle,
1270
+ self.rx.load_int(i),
1271
+ self.ry.load_int(i),
1272
+ self.rw.load_int(i),
1273
+ self.rh.load_int(i),
1274
+ content_w,
1275
+ content_h,
1276
+ )
1277
+ }
1278
+ }
1279
+ }
1280
+
1281
+ // ── Control trait conformance ──
1282
+ // The trait entry points delegate to the SoA engine: `measure` runs the
1283
+ // recursive measure against the constraints and returns the resolved content
1284
+ // size; `set_frame` arranges the whole subtree into the given rectangle
1285
+ // (top-left origin); `intrinsic_size` measures against unbounded space. The
1286
+ // `compute`/`layout` helpers below layer window integration, the root
1287
+ // main-axis fill, dirty tracking, and native `apply` on top of this same
1288
+ // math. A `Container` passed as a `Control` dispatches through these.
1289
+
1290
+ pub func measure = (self, BoxConstraints constraints, out Size) {
1291
+ var int w = self.measure_node(
1292
+ self.root,
1293
+ constraints.min_width,
1294
+ constraints.max_width,
1295
+ constraints.min_height,
1296
+ constraints.max_height,
1297
+ )
1298
+ var int h = self.rh.load_int(self.root)
1299
+ var Size s = Size()
1300
+ s.width = w
1301
+ s.height = h
1302
+ return s
1303
+ }
1304
+
1305
+ pub func set_frame = (ref self, int x, int y, int width, int height) {
1306
+ self.arrange_node(self.root, x, y, width, height)
1307
+ }
1308
+
1309
+ pub func intrinsic_size = (self, out Size) {
1310
+ var int w = self.measure_node(self.root, 0, INF, 0, INF)
1311
+ var int h = self.rh.load_int(self.root)
1312
+ var Size s = Size()
1313
+ s.width = w
1314
+ s.height = h
1315
+ return s
1316
+ }
1317
+
1318
+ // Pure measure + arrange against a size (no native calls). For testing.
1319
+ // The root fills its main axis (VStack → height, HStack → width) so that
1320
+ // children with `grow > 0` have surplus to absorb; the cross axis stays at
1321
+ // the measured content size, preserving child alignment. Grid/ZStack roots
1322
+ // keep both axes at content.
1323
+ pub func compute = (ref self, int avail_w, int avail_h) {
1324
+ // External constraint changed → the whole tree must re-measure.
1325
+ if self.last_w != avail_w || self.last_h != avail_h {
1326
+ self.mark_dirty(self.root)
1327
+ self.last_w = avail_w
1328
+ self.last_h = avail_h
1329
+ }
1330
+ var int root_w = self.measure_node(self.root, 0, avail_w, 0, avail_h)
1331
+ var int root_h = self.rh.load_int(self.root)
1332
+ var int frame_w = root_w
1333
+ var int frame_h = root_h
1334
+ var int kind = self.kinds.load_int(self.root)
1335
+ if kind == KIND_VSTACK {
1336
+ frame_h = avail_h
1337
+ }
1338
+ if kind == KIND_HSTACK {
1339
+ frame_w = avail_w
1340
+ }
1341
+ self.arrange_node(self.root, 0, 0, frame_w, frame_h)
1342
+ self.collect_dirty()
1343
+ }
1344
+
1345
+ // Measure + arrange against the window's content rect (minus padding) and
1346
+ // apply frames to every leaf, flipping y against the content height. The
1347
+ // root fills its main axis (see `compute`).
1348
+ pub func layout = (ref self, Window win) {
1349
+ var int cw = win.content_width()
1350
+ var int ch = win.content_height()
1351
+ var int avail_w = cw - self.padding * 2
1352
+ var int avail_h = ch - self.padding * 2
1353
+ // External constraint changed → the whole tree must re-measure.
1354
+ if self.last_w != avail_w || self.last_h != avail_h {
1355
+ self.mark_dirty(self.root)
1356
+ self.last_w = avail_w
1357
+ self.last_h = avail_h
1358
+ }
1359
+ var int root_w = self.measure_node(self.root, 0, avail_w, 0, avail_h)
1360
+ var int root_h = self.rh.load_int(self.root)
1361
+ var int frame_w = root_w
1362
+ var int frame_h = root_h
1363
+ var int kind = self.kinds.load_int(self.root)
1364
+ if kind == KIND_VSTACK {
1365
+ frame_h = avail_h
1366
+ }
1367
+ if kind == KIND_HSTACK {
1368
+ frame_w = avail_w
1369
+ }
1370
+ self.arrange_node(self.root, self.padding, self.padding, frame_w, frame_h)
1371
+ self.collect_dirty()
1372
+ self.apply(cw, ch)
1373
+ }
1374
+
1375
+ // Install a windowDidResize: delegate so the layout re-runs *during* a
1376
+ // live window resize. AppKit runs its own blocking tracking loop for the
1377
+ // whole drag (our manual event loop is stuck inside sendEvent: and can't
1378
+ // re-layout mid-drag), but it calls windowDidResize: on the delegate at
1379
+ // each step — so re-running the layout there keeps every control,
1380
+ // including sub-full-width grid cells that autoresizing masks can't
1381
+ // express, sized and positioned live. Call once after the first layout().
1382
+ // The callback calls nomen_layout_thunk (a free function) rather than the
1383
+ // layout method directly: free functions are exported for C linkage on
1384
+ // both backends, methods are not.
1385
+ pub func set_resize_callback = (ref self, Window win) {
1386
+ ```
1387
+ #arch: c, aarch64_use_c
1388
+ #platform: macos
1389
+ #scope: file
1390
+ struct Container;
1391
+ struct Window;
1392
+ // nomen_layout_thunk is a free function (defined in Nomen below) that
1393
+ // forwards to Container.layout — exported for C linkage on both
1394
+ // backends, unlike the layout method itself. It takes the Container by
1395
+ // `ref` (mutable reference): as a `class`, `ref Container` lowers to
1396
+ // `struct Container **` (reference to the instance pointer), so the
1397
+ // callback passes the address of the stored instance pointer.
1398
+ extern void nomen_layout_thunk(struct Container**, struct Window*);
1399
+ static void *g_resize_grid = 0;
1400
+ static void *g_resize_win = 0;
1401
+ void nomen_set_resize_target(void *grid, void *win) {
1402
+ g_resize_grid = grid;
1403
+ g_resize_win = win;
1404
+ }
1405
+ void nomen_window_did_resize(id self, SEL cmd, id notification) {
1406
+ (void)self; (void)cmd; (void)notification;
1407
+ if (g_resize_grid && g_resize_win) {
1408
+ nomen_layout_thunk((struct Container**)&g_resize_grid, (struct Window*)g_resize_win);
1409
+ }
1410
+ }
1411
+ ```
1412
+ ```
1413
+ #arch: c, aarch64_use_c
1414
+ #platform: macos
1415
+ nomen_set_resize_target(self, win);
1416
+ Class Dc = objc_getClass("NomenResizeDelegate");
1417
+ if (!Dc) {
1418
+ Dc = objc_allocateClassPair(objc_getClass("NSObject"), "NomenResizeDelegate", 0);
1419
+ if (Dc) {
1420
+ class_addMethod(Dc, sel_registerName("windowDidResize:"), (IMP)nomen_window_did_resize, "v@:@");
1421
+ objc_registerClassPair(Dc);
1422
+ }
1423
+ }
1424
+ if (Dc) {
1425
+ id delegate = ((id(*)(id, SEL))objc_msgSend)((id)Dc, sel_registerName("alloc"));
1426
+ delegate = ((id(*)(id, SEL))objc_msgSend)(delegate, sel_registerName("init"));
1427
+ ((void(*)(id, SEL, id))objc_msgSend)((id)win->handle, sel_registerName("setDelegate:"), delegate);
1428
+ }
1429
+ ```
1430
+ }
1431
+
1432
+ // Hit test: is point (cx, cy) (top-left origin, as returned by click_x/
1433
+ // click_y) inside the leaf with the given handle?
1434
+ pub func contains = (self, uint64 handle, int cx, int cy, out bool) {
1435
+ var int needle = handle as int
1436
+ var i = 0
1437
+ while i < self.count; i += 1 {
1438
+ if self.kinds.load_int(i) == KIND_LEAF {
1439
+ if self.handles.load_int(i) == needle {
1440
+ var int x = self.rx.load_int(i)
1441
+ var int y = self.ry.load_int(i)
1442
+ var int w = self.rw.load_int(i)
1443
+ var int h = self.rh.load_int(i)
1444
+ if cx >= x {
1445
+ if cx <= x + w {
1446
+ if cy >= y {
1447
+ if cy <= y + h {
1448
+ return true
1449
+ }
1450
+ }
1451
+ }
1452
+ }
1453
+ }
1454
+ }
1455
+ }
1456
+ return false
1457
+ }
1458
+
1459
+ // Compositor hit test: walk the frame tree front-to-back and return the
1460
+ // frontmost leaf whose frame contains (cx, cy) (top-left origin, matching
1461
+ // `click_x`/`click_y` and `contains`). For every container we test children
1462
+ // in paint order (front-to-back) and keep the last hit — a later child paints
1463
+ // on top of earlier ones, so the frontmost claimant wins. Hidden leaves are
1464
+ // skipped, as in measure/arrange. Returns the leaf's native handle (0 = no
1465
+ // hit). This needs no platform code — it's pure rect math on the frame tree.
1466
+ pub func hit_test = (self, int cx, int cy, out uint64) {
1467
+ var int idx = self.hit_test_node(self.root, cx, cy)
1468
+ if idx == -1 { return 0 }
1469
+ return self.handles.load_int(idx) as uint64
1470
+ }
1471
+
1472
+ // Same as `hit_test` but returns the frontmost leaf's node index (root is 0;
1473
+ // -1 = no hit). Handy for callers that want the node rather than the handle
1474
+ // (e.g. to read the resolved frame via `fmt_frame`).
1475
+ pub func hit_test_index = (self, int cx, int cy, out int) {
1476
+ return self.hit_test_node(self.root, cx, cy)
1477
+ }
1478
+
1479
+ func hit_test_node = (self, int idx, int cx, int cy, out int) {
1480
+ var int kind = self.kinds.load_int(idx)
1481
+ if kind == KIND_LEAF {
1482
+ if is_visible(self.handles.load_int(idx) as uint64) {
1483
+ var int x = self.rx.load_int(idx)
1484
+ var int y = self.ry.load_int(idx)
1485
+ var int w = self.rw.load_int(idx)
1486
+ var int h = self.rh.load_int(idx)
1487
+ if cx >= x {
1488
+ if cx <= x + w {
1489
+ if cy >= y {
1490
+ if cy <= y + h {
1491
+ return idx
1492
+ }
1493
+ }
1494
+ }
1495
+ }
1496
+ }
1497
+ return -1
1498
+ }
1499
+ // Container: iterate children in paint order; keep the last (frontmost) hit.
1500
+ var int result = -1
1501
+ var int ci = self.first_child(idx)
1502
+ while ci != -1 {
1503
+ if is_visible(self.handles.load_int(ci) as uint64) {
1504
+ var int hit = self.hit_test_node(ci, cx, cy)
1505
+ if hit != -1 { result = hit }
1506
+ }
1507
+ ci = self.next_sibling(ci, idx)
1508
+ }
1509
+ return result
1510
+ }
1511
+
1512
+ // Format a child's resolved frame as "x,y,w,h" (root is 0; children start at 1).
1513
+ pub func fmt_frame = (self, int i, out string) {
1514
+ return self.rx.load_int(i).to_string() + "," + self.ry.load_int(i).to_string() + "," + self.rw.load_int(i).to_string() + "," + self.rh.load_int(i).to_string()
1515
+ }
1516
+ }
1517
+
1518
+ // Free-function wrapper around Container.layout so the C delegate callback
1519
+ // (nomen_window_did_resize) can trigger a live relayout: free functions are
1520
+ // exported for C linkage on both backends, methods are not.
1521
+ /**
1522
+ * Entry point the layout engine calls to lay out a Container's tree
1523
+ **/
1524
+ pub func nomen_layout_thunk = (ref Container grid, Window win) {
1525
+ grid.layout(win)
1526
+ }
1527
+
1528
+ /**
1529
+ * Creates a vertical stack container with the given spacing
1530
+ **/
1531
+ pub func VStack = (int spacing, out Container) {
1532
+ var c = Container()
1533
+ c.make_root(KIND_VSTACK, spacing, 0)
1534
+ return c
1535
+ }
1536
+
1537
+ /**
1538
+ * Creates a horizontal stack container with the given spacing
1539
+ **/
1540
+ pub func HStack = (int spacing, out Container) {
1541
+ var c = Container()
1542
+ c.make_root(KIND_HSTACK, spacing, 0)
1543
+ return c
1544
+ }
1545
+
1546
+ /**
1547
+ * Creates a Z-stack container (children layered on top of one another)
1548
+ **/
1549
+ pub func ZStack = (out Container) {
1550
+ var c = Container()
1551
+ c.make_root(KIND_ZSTACK, 0, 0)
1552
+ return c
1553
+ }
1554
+
1555
+ /**
1556
+ * Creates a grid container with `cols` columns and the given spacing
1557
+ **/
1558
+ pub func Grid = (int cols: cols > 0, int spacing, out Container) {
1559
+ var c = Container()
1560
+ c.make_root(KIND_GRID, spacing, cols)
1561
+ return c
1562
+ }