nomen-lang 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/audit_runtime.c +12 -2
  2. package/core/System/Arena.nm +8 -1
  3. package/core/System/Buffer.nm +397 -8
  4. package/core/System/ClassBuffer.nm +156 -0
  5. package/core/System/Controls/Alert.nm +326 -0
  6. package/core/System/Controls/App.nm +147 -0
  7. package/core/System/Controls/Button.nm +119 -1
  8. package/core/System/Controls/CheckBox.nm +25 -1
  9. package/core/System/Controls/ColorPicker.nm +307 -0
  10. package/core/System/Controls/ComboBox.nm +430 -0
  11. package/core/System/Controls/Container.nm +843 -9
  12. package/core/System/Controls/Control.nm +443 -0
  13. package/core/System/Controls/DatePicker.nm +288 -0
  14. package/core/System/Controls/Form.nm +70 -0
  15. package/core/System/Controls/GroupBox.nm +267 -0
  16. package/core/System/Controls/ImageView.nm +311 -0
  17. package/core/System/Controls/LevelIndicator.nm +305 -0
  18. package/core/System/Controls/ListView.nm +633 -0
  19. package/core/System/Controls/Menu.nm +428 -0
  20. package/core/System/Controls/NumberInput.nm +56 -0
  21. package/core/System/Controls/OtpInput.nm +50 -0
  22. package/core/System/Controls/OutlineView.nm +598 -0
  23. package/core/System/Controls/Pagination.nm +142 -0
  24. package/core/System/Controls/Popover.nm +296 -0
  25. package/core/System/Controls/Progress.nm +348 -0
  26. package/core/System/Controls/Radio.nm +292 -0
  27. package/core/System/Controls/Scrollable.nm +584 -0
  28. package/core/System/Controls/SearchField.nm +326 -0
  29. package/core/System/Controls/SecureField.nm +344 -0
  30. package/core/System/Controls/Segmented.nm +380 -0
  31. package/core/System/Controls/Select.nm +340 -0
  32. package/core/System/Controls/Separator.nm +198 -0
  33. package/core/System/Controls/Sheet.nm +329 -0
  34. package/core/System/Controls/Slider.nm +315 -0
  35. package/core/System/Controls/Spinner.nm +287 -0
  36. package/core/System/Controls/SplitView.nm +509 -0
  37. package/core/System/Controls/Stepper.nm +349 -0
  38. package/core/System/Controls/Switch.nm +259 -0
  39. package/core/System/Controls/Table.nm +833 -0
  40. package/core/System/Controls/Tabs.nm +500 -0
  41. package/core/System/Controls/Text.nm +108 -11
  42. package/core/System/Controls/TextBox.nm +113 -2
  43. package/core/System/Controls/TextView.nm +329 -0
  44. package/core/System/Controls/Toggle.nm +346 -0
  45. package/core/System/Controls/Toolbar.nm +501 -0
  46. package/core/System/Controls/Tooltip.nm +76 -0
  47. package/core/System/Controls/Window.nm +1548 -91
  48. package/core/System/LinkedList.nm +8 -1
  49. package/core/System/List.nm +97 -7
  50. package/core/System/Map.nm +191 -18
  51. package/core/System/Mutex.nm +4 -4
  52. package/core/System/Random.nm +3 -4
  53. package/core/System/Set.nm +101 -13
  54. package/core/System/StableList.nm +371 -0
  55. package/core/System/Stream/Directory.nm +2 -2
  56. package/core/System/Stream/File.nm +2 -2
  57. package/core/System/Stream/Http.nm +2 -2
  58. package/core/System/String.nm +10 -5
  59. package/core/System/StringBuilder.nm +213 -2
  60. package/core/System/Text/CharIndex.nm +9 -9
  61. package/core/System/Text/Json.nm +60 -46
  62. package/core/System/Text/JsonTree.nm +120 -92
  63. package/core/System/Text/Regex.nm +23 -18
  64. package/core/System/Text/Utf8.nm +5 -5
  65. package/core/System/float.nm +36 -22
  66. package/core/System/int.nm +80 -28
  67. package/core/System/int16.nm +11 -0
  68. package/core/System/int32.nm +11 -0
  69. package/core/System/int64.nm +11 -0
  70. package/core/System/int8.nm +11 -0
  71. package/core/System/uint.nm +11 -0
  72. package/core/System/uint16.nm +11 -0
  73. package/core/System/uint32.nm +11 -0
  74. package/core/System/uint64.nm +11 -0
  75. package/core/System/uint8.nm +11 -0
  76. package/core/docs/System/StringBuilder.md +6 -0
  77. package/core/docs/System/Text/JsonTree.md +3 -2
  78. package/dist/index.mjs +38332 -30604
  79. package/package.json +1 -1
  80. package/src/bench_loop.nm +9 -9
@@ -0,0 +1,428 @@
1
+ /**
2
+ * A context menu attachable to any control (NSMenu via `view.menu`)
3
+ *
4
+ * An OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 4 — the shape menu): the
5
+ * single field is a platform handle, so the value is movable and its
6
+ * `#destroy` releases what the handle names. The constructor's `self` is
7
+ * the inline instance (the `.` accessor); methods keep the pointer form.
8
+ *
9
+ * Right-clicking an attached control shows the menu natively — AppKit runs
10
+ * its own modal tracking loop, so the manual event loop is untouched. Each
11
+ * item carries a tag (its index) and a shared target whose action records
12
+ * the tag in a C global; `clicked_item` polls it (edge-triggered: the read
13
+ * resets it to -1, "none"), and `on_item_select` is the registered form —
14
+ * the drain consumes the selection, the handler reads it through
15
+ * `selected_item`.
16
+ * iOS presentation differs (UIContextMenu-
17
+ * Interaction needs a scene-attached view — see GUI_PLAN Tier 2; the iOS
18
+ * blocks are TODO stubs).
19
+ **/
20
+ pub mv struct Menu: Control {
21
+ var uint64 handle = 0
22
+
23
+ // File-scope helper: the shared item target. The action records the
24
+ // clicked item's tag (shared — one Menu per process, like List's row
25
+ // storage).
26
+ pub func #init = (self) {
27
+ ```
28
+ #arch: c
29
+ #platform: macos
30
+ #scope: file
31
+ static long g_menu_last_tag = -1;
32
+ static id g_menu_target = 0;
33
+
34
+ static void nomen_menu_item_action(id self, SEL cmd, id sender) {
35
+ (void)self; (void)cmd;
36
+ g_menu_last_tag = ((long(*)(id, SEL))objc_msgSend)(sender, sel_registerName("tag"));
37
+ }
38
+
39
+ static id nomen_menu_target(void) {
40
+ Class Mt = objc_getClass("NomenMenuTarget");
41
+ if (!Mt) {
42
+ Mt = objc_allocateClassPair(objc_getClass("NSObject"), "NomenMenuTarget", 0);
43
+ if (Mt) {
44
+ class_addMethod(Mt, sel_registerName("nomenMenuItemAction:"),
45
+ (IMP)nomen_menu_item_action, "v@:@");
46
+ objc_registerClassPair(Mt);
47
+ }
48
+ }
49
+ if (!g_menu_target) {
50
+ g_menu_target = ((id(*)(id, SEL))objc_msgSend)(Mt, sel_registerName("new"));
51
+ }
52
+ return g_menu_target;
53
+ }
54
+
55
+ // Edge-triggered read: the poll consumes the tag (resets to -1).
56
+ long nomen_menu_last_tag(void) {
57
+ long tag = g_menu_last_tag;
58
+ g_menu_last_tag = -1;
59
+ return tag;
60
+ }
61
+
62
+ // The on_item_select drain's consume-and-stash: the drain consumes
63
+ // the selected tag exactly like the poll, and STASHES it for the
64
+ // handler body to read through Menu.selected_item (the drain runs
65
+ // first in its pump arm, so the stash is fresh when the handler
66
+ // body runs).
67
+ long g_menu_selected_tag = -1;
68
+ unsigned char nomen_menu_take_select(void) {
69
+ long tag = nomen_menu_last_tag();
70
+ if (tag < 0) return 0;
71
+ g_menu_selected_tag = tag;
72
+ return 1;
73
+ }
74
+ long nomen_menu_selected_tag(void) {
75
+ return g_menu_selected_tag;
76
+ }
77
+ ```
78
+ ```
79
+ #arch: aarch64_use_c
80
+ #platform: macos
81
+ #scope: file
82
+ static long g_menu_last_tag = -1;
83
+ static id g_menu_target = 0;
84
+
85
+ static void nomen_menu_item_action(id self, SEL cmd, id sender) {
86
+ (void)self; (void)cmd;
87
+ g_menu_last_tag = ((long(*)(id, SEL))objc_msgSend)(sender, sel_registerName("tag"));
88
+ }
89
+
90
+ static id nomen_menu_target(void) {
91
+ Class Mt = objc_getClass("NomenMenuTarget");
92
+ if (!Mt) {
93
+ Mt = objc_allocateClassPair(objc_getClass("NSObject"), "NomenMenuTarget", 0);
94
+ if (Mt) {
95
+ class_addMethod(Mt, sel_registerName("nomenMenuItemAction:"),
96
+ (IMP)nomen_menu_item_action, "v@:@");
97
+ objc_registerClassPair(Mt);
98
+ }
99
+ }
100
+ if (!g_menu_target) {
101
+ g_menu_target = ((id(*)(id, SEL))objc_msgSend)(Mt, sel_registerName("new"));
102
+ }
103
+ return g_menu_target;
104
+ }
105
+
106
+ // Edge-triggered read: the poll consumes the tag (resets to -1).
107
+ long nomen_menu_last_tag(void) {
108
+ long tag = g_menu_last_tag;
109
+ g_menu_last_tag = -1;
110
+ return tag;
111
+ }
112
+
113
+ // The on_item_select drain's consume-and-stash (see the c twin
114
+ // above).
115
+ long g_menu_selected_tag = -1;
116
+ unsigned char nomen_menu_take_select(void) {
117
+ long tag = nomen_menu_last_tag();
118
+ if (tag < 0) return 0;
119
+ g_menu_selected_tag = tag;
120
+ return 1;
121
+ }
122
+ long nomen_menu_selected_tag(void) {
123
+ return g_menu_selected_tag;
124
+ }
125
+ ```
126
+ ```
127
+ #arch: c
128
+ #platform: macos
129
+ id menu = ((id(*)(id, SEL))objc_msgSend)(
130
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSMenu"), sel_registerName("alloc")),
131
+ sel_registerName("init"));
132
+ self.handle = (uint64_t)menu;
133
+ ```
134
+ ```
135
+ #arch: aarch64_use_c
136
+ #platform: macos
137
+ id menu = ((id(*)(id, SEL))objc_msgSend)(
138
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSMenu"), sel_registerName("alloc")),
139
+ sel_registerName("init"));
140
+ self->handle = (uint64_t)menu;
141
+ ```
142
+ ```
143
+ #arch: c
144
+ #platform: ios
145
+ /* TODO: iOS — UIContextMenuInteraction needs a scene-attached view
146
+ * (see GUI_PLAN Tier 2; defer). */
147
+ ```
148
+ ```
149
+ #arch: aarch64_use_c
150
+ #platform: ios
151
+ /* TODO: iOS (see #init) */
152
+ ```
153
+ }
154
+
155
+ // Append a clickable item. Its tag (and the `clicked_item` value it
156
+ // reports) is its ENTRY index in the menu — separators consume an index
157
+ // too, so `add_item` after `add_separator` reports the entry count.
158
+ pub func add_item = (self, string title) {
159
+ ```
160
+ #arch: c
161
+ #platform: macos
162
+ id menu = (id)self->handle;
163
+ long tag = ((long(*)(id, SEL))objc_msgSend)(menu, sel_registerName("numberOfItems"));
164
+ id item = ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(
165
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")),
166
+ sel_registerName("initWithTitle:action:keyEquivalent:"),
167
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
168
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr),
169
+ sel_registerName("nomenMenuItemAction:"),
170
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
171
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), ""));
172
+ ((void(*)(id, SEL, long))objc_msgSend)(item, sel_registerName("setTag:"), tag);
173
+ ((void(*)(id, SEL, id))objc_msgSend)(item, sel_registerName("setTarget:"), nomen_menu_target());
174
+ ((void(*)(id, SEL, id))objc_msgSend)(menu, sel_registerName("addItem:"), item);
175
+ ((void(*)(id, SEL))objc_msgSend)(item, sel_registerName("release"));
176
+ ```
177
+ ```
178
+ #arch: aarch64_use_c
179
+ #platform: macos
180
+ id menu = (id)self->handle;
181
+ long tag = ((long(*)(id, SEL))objc_msgSend)(menu, sel_registerName("numberOfItems"));
182
+ id item = ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(
183
+ ((id(*)(id, SEL))objc_msgSend)((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")),
184
+ sel_registerName("initWithTitle:action:keyEquivalent:"),
185
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
186
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr),
187
+ sel_registerName("nomenMenuItemAction:"),
188
+ ((id(*)(id, SEL, const char*))objc_msgSend)(
189
+ (id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), ""));
190
+ ((void(*)(id, SEL, long))objc_msgSend)(item, sel_registerName("setTag:"), tag);
191
+ ((void(*)(id, SEL, id))objc_msgSend)(item, sel_registerName("setTarget:"), nomen_menu_target());
192
+ ((void(*)(id, SEL, id))objc_msgSend)(menu, sel_registerName("addItem:"), item);
193
+ ((void(*)(id, SEL))objc_msgSend)(item, sel_registerName("release"));
194
+ ```
195
+ ```
196
+ #arch: c
197
+ #platform: ios
198
+ /* TODO: iOS (see #init) */
199
+ ```
200
+ ```
201
+ #arch: aarch64_use_c
202
+ #platform: ios
203
+ /* TODO: iOS (see #init) */
204
+ ```
205
+ }
206
+
207
+ // Append a separator line.
208
+ pub func add_separator = (self) {
209
+ ```
210
+ #arch: c
211
+ #platform: macos
212
+ id sep = ((id(*)(id, SEL))objc_msgSend)(
213
+ (id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem"));
214
+ ((void(*)(id, SEL, id))objc_msgSend)((id)self->handle, sel_registerName("addItem:"), sep);
215
+ ```
216
+ ```
217
+ #arch: aarch64_use_c
218
+ #platform: macos
219
+ id sep = ((id(*)(id, SEL))objc_msgSend)(
220
+ (id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem"));
221
+ ((void(*)(id, SEL, id))objc_msgSend)((id)self->handle, sel_registerName("addItem:"), sep);
222
+ ```
223
+ ```
224
+ #arch: c
225
+ #platform: ios
226
+ /* TODO: iOS (see #init) */
227
+ ```
228
+ ```
229
+ #arch: aarch64_use_c
230
+ #platform: ios
231
+ /* TODO: iOS (see #init) */
232
+ ```
233
+ }
234
+
235
+ // Show this menu on right-click of the control with the given handle.
236
+ pub func attach = (self, uint64 handle) {
237
+ ```
238
+ #arch: c
239
+ #platform: macos
240
+ ((void(*)(id, SEL, id))objc_msgSend)((id)handle, sel_registerName("setMenu:"), (id)self->handle);
241
+ ```
242
+ ```
243
+ #arch: aarch64_use_c
244
+ #platform: macos
245
+ ((void(*)(id, SEL, id))objc_msgSend)((id)handle, sel_registerName("setMenu:"), (id)self->handle);
246
+ ```
247
+ ```
248
+ #arch: c
249
+ #platform: ios
250
+ /* TODO: iOS (see #init) */
251
+ ```
252
+ ```
253
+ #arch: aarch64_use_c
254
+ #platform: ios
255
+ /* TODO: iOS (see #init) */
256
+ ```
257
+ }
258
+
259
+ // The number of ENTRIES (items + separators).
260
+ pub func item_count = (self, out int) {
261
+ ```
262
+ #arch: c
263
+ #platform: macos
264
+ return (int)((long(*)(id, SEL))objc_msgSend)(
265
+ (id)self->handle, sel_registerName("numberOfItems"));
266
+ ```
267
+ ```
268
+ #arch: aarch64_use_c
269
+ #platform: macos
270
+ return (int)((long(*)(id, SEL))objc_msgSend)(
271
+ (id)self->handle, sel_registerName("numberOfItems"));
272
+ ```
273
+ ```
274
+ #arch: c
275
+ #platform: ios
276
+ /* TODO: iOS (see #init) */
277
+ return 0;
278
+ ```
279
+ ```
280
+ #arch: aarch64_use_c
281
+ #platform: ios
282
+ /* TODO: iOS (see #init) */
283
+ return 0;
284
+ ```
285
+ }
286
+
287
+ // The tag of the most recently clicked item since the last read
288
+ // (edge-triggered; -1 = none). Poll it in the manual loop.
289
+ pub func clicked_item = (self, out int) {
290
+ ```
291
+ #arch: c
292
+ #platform: macos
293
+ extern long nomen_menu_last_tag(void);
294
+ long tag = nomen_menu_last_tag();
295
+ return (int)tag;
296
+ ```
297
+ ```
298
+ #arch: aarch64_use_c
299
+ #platform: macos
300
+ extern long nomen_menu_last_tag(void);
301
+ long tag = nomen_menu_last_tag();
302
+ return (int)tag;
303
+ ```
304
+ ```
305
+ #arch: c
306
+ #platform: ios
307
+ /* TODO: iOS (see #init) */
308
+ return -1;
309
+ ```
310
+ ```
311
+ #arch: aarch64_use_c
312
+ #platform: ios
313
+ /* TODO: iOS (see #init) */
314
+ return -1;
315
+ ```
316
+ }
317
+
318
+ // The on_item_select desugar's drain (docs/HANDLERS.md): the synthesized
319
+ // pump arm polls this per frame. The selection is CONSUMED by the drain
320
+ // — like the poll, one record has one consumer, so a selection fires an
321
+ // arm exactly once — and the consumed tag is stashed for the handler
322
+ // body, which reads it through selected_item. The poll and the drain
323
+ // share one record: a selection consumed by one never reaches the other.
324
+ pub func take_item_select = (self, out bool) {
325
+ ```
326
+ #arch: c
327
+ #platform: macos
328
+ extern unsigned char nomen_menu_take_select(void);
329
+ return nomen_menu_take_select();
330
+ ```
331
+ ```
332
+ #arch: aarch64_use_c
333
+ #platform: macos
334
+ extern unsigned char nomen_menu_take_select(void);
335
+ return nomen_menu_take_select();
336
+ ```
337
+ ```
338
+ #arch: c
339
+ #platform: ios
340
+ /* TODO: iOS (see #init) */
341
+ return false;
342
+ ```
343
+ ```
344
+ #arch: aarch64_use_c
345
+ #platform: ios
346
+ /* TODO: iOS (see #init) */
347
+ return false;
348
+ ```
349
+ }
350
+
351
+ // The tag the last take_item_select consumed (-1 before the first
352
+ // selection) — the handler body's payload getter. The control-scoped
353
+ // analog of the dispatch-window getters (win.click_x() et al): the
354
+ // record is the menu's own, so the payload rides the menu, not the
355
+ // window.
356
+ pub func selected_item = (self, out int) {
357
+ ```
358
+ #arch: c
359
+ #platform: macos
360
+ extern long nomen_menu_selected_tag(void);
361
+ return (int)nomen_menu_selected_tag();
362
+ ```
363
+ ```
364
+ #arch: aarch64_use_c
365
+ #platform: macos
366
+ extern long nomen_menu_selected_tag(void);
367
+ return (int)nomen_menu_selected_tag();
368
+ ```
369
+ ```
370
+ #arch: c
371
+ #platform: ios
372
+ /* TODO: iOS (see #init) */
373
+ return -1;
374
+ ```
375
+ ```
376
+ #arch: aarch64_use_c
377
+ #platform: ios
378
+ /* TODO: iOS (see #init) */
379
+ return -1;
380
+ ```
381
+ }
382
+
383
+ // ── Control trait conformance ──
384
+ // A menu is not laid out: it has no frame of its own (it presents at the
385
+ // cursor under the attached control's right-click).
386
+ pub func set_frame = (ref self, int x, int y, int width, int height) {
387
+ // No-op: the menu presents at the cursor.
388
+ }
389
+
390
+ pub func intrinsic_size = (self, out Size) {
391
+ var s = Size()
392
+ return s
393
+ }
394
+
395
+ pub func measure = (self, BoxConstraints constraints, out Size) {
396
+ var s = Size()
397
+ return s
398
+ }
399
+
400
+ func #destroy = () {
401
+ ```
402
+ #arch: c
403
+ #platform: macos
404
+ if (self->handle) {
405
+ ((void(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("release"));
406
+ self->handle = 0;
407
+ }
408
+ ```
409
+ ```
410
+ #arch: aarch64_use_c
411
+ #platform: macos
412
+ if (self->handle) {
413
+ ((void(*)(id, SEL))objc_msgSend)((id)self->handle, sel_registerName("release"));
414
+ self->handle = 0;
415
+ }
416
+ ```
417
+ ```
418
+ #arch: c
419
+ #platform: ios
420
+ /* TODO: iOS (see #init) */
421
+ ```
422
+ ```
423
+ #arch: aarch64_use_c
424
+ #platform: ios
425
+ /* TODO: iOS (see #init) */
426
+ ```
427
+ }
428
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * A number field with stepper nudges — TextBox + Stepper composed
3
+ *
4
+ * PURE COMPOSITION (GUI_PLAN Tier 3 "reachable earlier"): no new native
5
+ * control, no drawing — a TextBox for the text and a Stepper for the
6
+ * nudges, wired side by side into the caller's grid (the box fills the
7
+ * row, the stepper takes its intrinsic width).
8
+ *
9
+ * `set_value` drives both controls: the stepper holds the value, the text
10
+ * mirrors it (whole values as ints, fractions via the float's %f
11
+ * rendering). Stepper clicks update the stepper — but free-typed text is
12
+ * NOT parsed back in v1 (no string→float round-trip yet), so editing the
13
+ * text only changes the display until the next nudge. Hit-test the box and
14
+ * stepper through their handles (fields read like every control's
15
+ * `handle`).
16
+ **/
17
+ pub mv struct NumberInput {
18
+ var TextBox box
19
+ var Stepper step
20
+
21
+ pub func #init = (self, Window window) {
22
+ self.box = TextBox(window)
23
+ self.step = Stepper(window)
24
+ }
25
+
26
+ // The stepper's range and nudge size.
27
+ pub func set_range = (ref self, float min, float max, float increment) {
28
+ self.step.set_min(min)
29
+ self.step.set_max(max)
30
+ self.step.set_increment(increment)
31
+ }
32
+
33
+ // Drive BOTH controls: the stepper holds the value, the text mirrors it
34
+ // (the float's %f rendering).
35
+ pub func set_value = (ref self, float v) {
36
+ self.step.set_value(v)
37
+ var text = v.to_string()
38
+ self.box.set_text(text)
39
+ }
40
+
41
+ // The current value (the stepper's — typed text is not parsed back in
42
+ // v1; the next nudge overwrites the text).
43
+ pub func value = (self, out float) {
44
+ return self.step.value()
45
+ }
46
+
47
+ // Wire the pair into the caller's grid under `parent`: an hstack with
48
+ // the box filling the row and the stepper at its intrinsic width.
49
+ // Hit-testing works through the usual grid.contains on the fields'
50
+ // handles.
51
+ pub func add_to = (ref self, ref Container grid, int parent, int span) {
52
+ var int hs = grid.add_hstack(parent, 6, span)
53
+ grid.add_to(hs, self.box.handle, 0, 24, 1, 1)
54
+ grid.add_to_intrinsic(hs, self.step.handle, 1)
55
+ }
56
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A one-time-code field — five SecureFields composed
3
+ *
4
+ * PURE COMPOSITION (GUI_PLAN Tier 3 "reachable earlier"): no new native
5
+ * control, no drawing — five single-character SecureFields wired in a row
6
+ * into the caller's grid. `code` concatenates whatever the five fields
7
+ * hold (incomplete codes read as-is — no per-field auto-advance in v1;
8
+ * fields are hit-tested through their handles like every control's).
9
+ **/
10
+ pub mv struct OtpInput {
11
+ var SecureField f0
12
+ var SecureField f1
13
+ var SecureField f2
14
+ var SecureField f3
15
+ var SecureField f4
16
+
17
+ pub func #init = (self, Window window) {
18
+ self.f0 = SecureField(window)
19
+ self.f1 = SecureField(window)
20
+ self.f2 = SecureField(window)
21
+ self.f3 = SecureField(window)
22
+ self.f4 = SecureField(window)
23
+ }
24
+
25
+ // Wire the five fields into the caller's grid under `parent`: an hstack
26
+ // of fixed-width boxes.
27
+ pub func add_to = (ref self, ref Container grid, int parent, int span) {
28
+ var int hs = grid.add_hstack(parent, 8, span)
29
+ grid.add_to(hs, self.f0.handle, 36, 28, 0, 1)
30
+ grid.add_to(hs, self.f1.handle, 36, 28, 0, 1)
31
+ grid.add_to(hs, self.f2.handle, 36, 28, 0, 1)
32
+ grid.add_to(hs, self.f3.handle, 36, 28, 0, 1)
33
+ grid.add_to(hs, self.f4.handle, 36, 28, 0, 1)
34
+ }
35
+
36
+ // The concatenated contents of the five fields, in order.
37
+ pub func code = (self, out string) {
38
+ return self.f0.get_text() + self.f1.get_text() + self.f2.get_text()
39
+ +self.f3.get_text() + self.f4.get_text()
40
+ }
41
+
42
+ // Empty every field.
43
+ pub func clear = (ref self) {
44
+ self.f0.set_text("")
45
+ self.f1.set_text("")
46
+ self.f2.set_text("")
47
+ self.f3.set_text("")
48
+ self.f4.set_text("")
49
+ }
50
+ }