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.
- package/NOMEN_AGENTS.md +235 -0
- package/core/System/Ansi.nm +150 -0
- package/core/System/Array.nm +358 -0
- package/core/System/BigInt.nm +887 -0
- package/core/System/Buffer.nm +526 -0
- package/core/System/Channel.nm +198 -0
- package/core/System/ClassBuffer.nm +229 -0
- package/core/System/Console.nm +211 -0
- package/core/System/Controls/Button.nm +138 -0
- package/core/System/Controls/CheckBox.nm +158 -0
- package/core/System/Controls/Container.nm +1562 -0
- package/core/System/Controls/Control.nm +28 -0
- package/core/System/Controls/Geometry.nm +101 -0
- package/core/System/Controls/Layout.nm +228 -0
- package/core/System/Controls/LayoutParams.nm +50 -0
- package/core/System/Controls/Text.nm +129 -0
- package/core/System/Controls/TextBox.nm +157 -0
- package/core/System/Controls/Window.nm +280 -0
- package/core/System/Disposable.nm +8 -0
- package/core/System/Enumerable.nm +10 -0
- package/core/System/Equatable.nm +10 -0
- package/core/System/Graph.nm +68 -0
- package/core/System/Hashable.nm +13 -0
- package/core/System/Init.nm +21 -0
- package/core/System/LinkedList.nm +64 -0
- package/core/System/List.nm +44 -0
- package/core/System/Map.nm +153 -0
- package/core/System/Math.nm +54 -0
- package/core/System/Mutex.nm +78 -0
- package/core/System/Nursery.nm +27 -0
- package/core/System/Sendable.nm +9 -0
- package/core/System/Set.nm +97 -0
- package/core/System/Stream/Directory.nm +176 -0
- package/core/System/Stream/File.nm +432 -0
- package/core/System/Stream/Http.nm +453 -0
- package/core/System/String.nm +154 -0
- package/core/System/StringBuilder.nm +249 -0
- package/core/System/Stringable.nm +8 -0
- package/core/System/Task.nm +263 -0
- package/core/System/Test.nm +241 -0
- package/core/System/Text/Json.nm +377 -0
- package/core/System/Text/JsonTree.nm +187 -0
- package/core/System/Text/Regex.nm +655 -0
- package/core/System/Time.nm +112 -0
- package/core/System/Tree.nm +70 -0
- package/core/System/Viewable.nm +27 -0
- package/core/System/bool.nm +66 -0
- package/core/System/char.nm +42 -0
- package/core/System/float.nm +43 -0
- package/core/System/int.nm +54 -0
- package/core/System/int64.nm +42 -0
- package/core/System/int8.nm +42 -0
- package/core/System/uint.nm +42 -0
- package/core/System/uint64.nm +42 -0
- package/core/System/uint8.nm +42 -0
- package/core/docs/System/Ansi.md +40 -0
- package/core/docs/System/Array.md +16 -0
- package/core/docs/System/BigInt.md +30 -0
- package/core/docs/System/Buffer.md +31 -0
- package/core/docs/System/Channel.md +10 -0
- package/core/docs/System/ClassBuffer.md +15 -0
- package/core/docs/System/Console.md +13 -0
- package/core/docs/System/Controls/Button.md +12 -0
- package/core/docs/System/Controls/CheckBox.md +15 -0
- package/core/docs/System/Controls/Container.md +82 -0
- package/core/docs/System/Controls/Control.md +5 -0
- package/core/docs/System/Controls/Geometry.md +26 -0
- package/core/docs/System/Controls/Layout.md +37 -0
- package/core/docs/System/Controls/LayoutParams.md +13 -0
- package/core/docs/System/Controls/Text.md +12 -0
- package/core/docs/System/Controls/TextBox.md +13 -0
- package/core/docs/System/Controls/Window.md +20 -0
- package/core/docs/System/Disposable.md +5 -0
- package/core/docs/System/Enumerable.md +5 -0
- package/core/docs/System/Graph.md +17 -0
- package/core/docs/System/Init.md +9 -0
- package/core/docs/System/LinkedList.md +16 -0
- package/core/docs/System/List.md +13 -0
- package/core/docs/System/Map.md +14 -0
- package/core/docs/System/Math.md +11 -0
- package/core/docs/System/Mutex.md +10 -0
- package/core/docs/System/Nursery.md +5 -0
- package/core/docs/System/Sendable.md +5 -0
- package/core/docs/System/Set.md +14 -0
- package/core/docs/System/Stream/Directory.md +12 -0
- package/core/docs/System/Stream/File.md +21 -0
- package/core/docs/System/String.md +14 -0
- package/core/docs/System/StringBuilder.md +12 -0
- package/core/docs/System/Stringable.md +5 -0
- package/core/docs/System/Task.md +15 -0
- package/core/docs/System/Text/Json.md +12 -0
- package/core/docs/System/Text/JsonTree.md +21 -0
- package/core/docs/System/Text/Regex.md +12 -0
- package/core/docs/System/Time.md +11 -0
- package/core/docs/System/Tree.md +18 -0
- package/core/docs/System/Viewable.md +5 -0
- package/core/docs/System/bool.md +9 -0
- package/core/docs/System/char.md +9 -0
- package/core/docs/System/float.md +9 -0
- package/core/docs/System/int.md +10 -0
- package/core/docs/System/int64.md +9 -0
- package/core/docs/System/int8.md +9 -0
- package/core/docs/System/uint.md +9 -0
- package/core/docs/System/uint64.md +9 -0
- package/core/docs/System/uint8.md +9 -0
- package/core/package.jsonc +4 -0
- package/dist/index.mjs +417 -5490
- package/package.json +33 -30
- package/src/args.ts +171 -0
- package/src/index.ts +53 -104
- package/src/init.ts +128 -0
- package/src/test.ts +6 -2
- package/test/fixtures/build/calc.test +0 -0
- package/test/fixtures/build/calc.test.c +0 -969
- package/test/fixtures/build/main.h +0 -170
- package/test/fixtures/calc.test.nm +0 -32
- package/tsconfig.json +0 -21
- 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
|
+
}
|