@effindomv2/fui-as 0.1.36 → 0.1.39

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 (119) hide show
  1. package/browser/routed-app-conventions.ts +1 -1
  2. package/browser/src/common-harness.ts +1 -27
  3. package/browser/src/index.ts +19 -26
  4. package/browser/src/routed-harness.ts +1 -194
  5. package/browser/src/shared-browser.ts +6 -0
  6. package/package.json +26 -16
  7. package/scripts/build.sh +51 -47
  8. package/scripts/framework-host-services.ts +1 -1
  9. package/scripts/generate-host-events.ts +15 -241
  10. package/scripts/generate-host-services.ts +13 -261
  11. package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
  12. package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
  13. package/scripts/hostgen/common.ts +73 -0
  14. package/scripts/hostgen/registry.ts +159 -0
  15. package/src/Fui.ts +70 -4
  16. package/src/FuiExports.ts +13 -1
  17. package/src/bindings/ui.ts +165 -8
  18. package/src/controls/Button.ts +98 -71
  19. package/src/controls/Checkbox.ts +23 -21
  20. package/src/controls/ComboBox.ts +1201 -0
  21. package/src/controls/ContextMenu.ts +279 -165
  22. package/src/controls/Dialog.ts +91 -17
  23. package/src/controls/Dropdown.ts +101 -333
  24. package/src/controls/NavLink.ts +29 -6
  25. package/src/controls/Popup.ts +10 -5
  26. package/src/controls/ProgressBar.ts +1 -2
  27. package/src/controls/RadioButton.ts +34 -13
  28. package/src/controls/RadioGroup.ts +32 -19
  29. package/src/controls/Slider.ts +21 -23
  30. package/src/controls/Switch.ts +34 -17
  31. package/src/controls/index.ts +2 -1
  32. package/src/controls/internal/ButtonPresenter.ts +5 -2
  33. package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
  34. package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
  35. package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
  36. package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
  37. package/src/controls/internal/PressableLabeledControl.ts +4 -7
  38. package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
  39. package/src/controls/internal/SelectablePopupList.ts +463 -0
  40. package/src/controls/internal/SliderPresenter.ts +5 -5
  41. package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
  42. package/src/controls/internal/TextInputCore.ts +307 -41
  43. package/src/controls/internal/TextInputPresenter.ts +2 -2
  44. package/src/controls/templating.ts +10 -0
  45. package/src/core/Application.ts +19 -9
  46. package/src/core/Bitmap.ts +219 -1
  47. package/src/core/ContextMenuManager.ts +128 -44
  48. package/src/core/DebugApi.ts +17 -2
  49. package/src/core/DragGesture.ts +8 -7
  50. package/src/core/EventRouter.ts +332 -12
  51. package/src/core/Fetch.ts +15 -7
  52. package/src/core/FetchFfi.ts +1 -15
  53. package/src/core/File.ts +75 -34
  54. package/src/core/FocusAdornerManager.ts +3 -3
  55. package/src/core/FocusVisibility.ts +7 -0
  56. package/src/core/FrameScheduler.ts +126 -0
  57. package/src/core/ImageSampling.ts +43 -0
  58. package/src/core/Logger.ts +2 -0
  59. package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
  60. package/src/core/Node.ts +1039 -97
  61. package/src/core/Platform.ts +16 -16
  62. package/src/core/SelectionHandleAdornerManager.ts +697 -0
  63. package/src/core/Theme.ts +34 -20
  64. package/src/core/ToolTipManager.ts +3 -3
  65. package/src/core/Typography.ts +411 -19
  66. package/src/core/Worker.ts +69 -33
  67. package/src/core/event_exports.ts +123 -5
  68. package/src/core/ffi.ts +5 -746
  69. package/src/core/generated/FetchHostAbi.ts +10 -0
  70. package/src/core/generated/FrameworkHostServices.ts +1 -1
  71. package/src/core/generated/HostAbi.ts +220 -0
  72. package/src/core/generated/UiAbi.ts +356 -0
  73. package/src/core/generated/UiEnums.ts +186 -0
  74. package/src/drawing/DrawCallback.ts +34 -0
  75. package/src/drawing/DrawContext.ts +189 -0
  76. package/src/drawing/Paint.ts +34 -0
  77. package/src/drawing/Path.ts +40 -0
  78. package/src/nodes/CustomDrawable.ts +61 -0
  79. package/src/nodes/FlexBox.ts +57 -11
  80. package/src/nodes/Grid.ts +5 -5
  81. package/src/nodes/Image.ts +20 -1
  82. package/src/nodes/RichText.ts +36 -25
  83. package/src/nodes/ScrollBar.ts +2 -1
  84. package/src/nodes/ScrollBox.ts +7 -2
  85. package/src/nodes/ScrollView.ts +17 -6
  86. package/src/nodes/Svg.ts +18 -1
  87. package/src/nodes/Text.ts +1 -1
  88. package/src/nodes/TextCore.ts +115 -20
  89. package/src/nodes/index.ts +3 -1
  90. package/src/text/DynamicTextLayout.ts +275 -0
  91. package/src/text/TextLayout.ts +308 -0
  92. package/src/worker/Worker.ts +3 -36
  93. package/src/worker/WorkerJob.ts +5 -9
  94. package/src/worker/ffi.ts +0 -6
  95. package/browser/src/common-harness/host-imports.ts +0 -447
  96. package/browser/src/common-harness/interop.ts +0 -39
  97. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  98. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  99. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  100. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  101. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  102. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  103. package/browser/src/common-harness/managed-harness.ts +0 -1451
  104. package/browser/src/common-harness/managed-history.ts +0 -168
  105. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  106. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  107. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  108. package/browser/src/common-harness/types.ts +0 -205
  109. package/browser/src/common-harness/ui-chrome.ts +0 -206
  110. package/browser/src/common-harness/ui-imports.ts +0 -553
  111. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  112. package/browser/src/file-processing-worker.ts +0 -89
  113. package/browser/src/host-events.ts +0 -118
  114. package/browser/src/host-services.ts +0 -483
  115. package/browser/src/persisted-ui-state.ts +0 -206
  116. package/browser/src/routed-app-conventions.ts +0 -292
  117. package/browser/src/worker-bootstrap.ts +0 -916
  118. package/browser/src/worker-manager.ts +0 -234
  119. package/browser/src/worker-types.ts +0 -68
@@ -0,0 +1,186 @@
1
+ // @ts-nocheck
2
+
3
+ // Generated by v2/abi/generate.ts strategy fui-as-enums from v2/ui/include/effindom_ui.h and v2/core/include/effindom.h.
4
+
5
+ // Includes SDK facade enum names backed by canonical ABI header values.
6
+
7
+ // SDK-only facade enums remain in core/ffi.ts until they have a canonical ABI source.
8
+
9
+ // Do not edit by hand.
10
+
11
+ export enum HandleValue {
12
+ Invalid = 0,
13
+ }
14
+
15
+ export enum NodeType {
16
+ FlexBox = 0,
17
+ Text = 1,
18
+ Image = 2,
19
+ Svg = 3,
20
+ ScrollView = 4,
21
+ Grid = 5,
22
+ }
23
+
24
+ export enum Unit {
25
+ Pixel = 0,
26
+ Auto = 1,
27
+ Percent = 2,
28
+ }
29
+
30
+ export enum GridUnit {
31
+ Pixel = 0,
32
+ Auto = 1,
33
+ Star = 2,
34
+ }
35
+
36
+ export enum PositionType {
37
+ Relative = 0,
38
+ Absolute = 1,
39
+ }
40
+
41
+ export enum Visibility {
42
+ Normal = 0,
43
+ Hidden = 1,
44
+ Collapsed = 2,
45
+ }
46
+
47
+ export enum FlexDirection {
48
+ Column = 0,
49
+ Row = 1,
50
+ }
51
+
52
+ export enum FlexWrap {
53
+ NoWrap = 0,
54
+ Wrap = 1,
55
+ WrapReverse = 2,
56
+ }
57
+
58
+ export enum JustifyContent {
59
+ Start = 0,
60
+ Center = 1,
61
+ End = 2,
62
+ }
63
+
64
+ export enum AlignItems {
65
+ Start = 0,
66
+ Center = 1,
67
+ End = 2,
68
+ Stretch = 3,
69
+ None = 4,
70
+ }
71
+
72
+ export enum AlignSelf {
73
+ Auto = 0,
74
+ Start = 1,
75
+ Center = 2,
76
+ End = 3,
77
+ Stretch = 4,
78
+ }
79
+
80
+ export enum BorderStyle {
81
+ Solid = 0,
82
+ Dashed = 1,
83
+ Dotted = 2,
84
+ }
85
+
86
+ export enum ObjectFit {
87
+ Fill = 0,
88
+ Contain = 1,
89
+ Cover = 2,
90
+ None = 3,
91
+ ScaleDown = 4,
92
+ }
93
+
94
+ export enum ImageSamplingKind {
95
+ Linear = 0,
96
+ Nearest = 1,
97
+ LinearMipmapNearest = 2,
98
+ LinearMipmapLinear = 3,
99
+ CubicMitchell = 4,
100
+ CubicCatmullRom = 5,
101
+ Anisotropic = 6,
102
+ }
103
+
104
+ export enum TextAlign {
105
+ Left = 0,
106
+ Center = 1,
107
+ Right = 2,
108
+ }
109
+
110
+ export enum TextVerticalAlign {
111
+ Top = 0,
112
+ Center = 1,
113
+ Bottom = 2,
114
+ }
115
+
116
+ export enum TextOverflow {
117
+ Clip = 0,
118
+ Ellipsis = 1,
119
+ Fade = 2,
120
+ }
121
+
122
+ export enum Orientation {
123
+ None = 0,
124
+ Horizontal = 1,
125
+ Vertical = 2,
126
+ }
127
+
128
+ export enum KeyEventType {
129
+ Down = 1,
130
+ Up = 2,
131
+ }
132
+
133
+ export enum PointerEventType {
134
+ Down = 1,
135
+ Up = 2,
136
+ Move = 3,
137
+ Enter = 4,
138
+ Leave = 5,
139
+ Cancel = 6,
140
+ }
141
+
142
+ export enum KeyModifier {
143
+ Shift = 1 << 0,
144
+ Ctrl = 1 << 1,
145
+ Alt = 1 << 2,
146
+ Meta = 1 << 3,
147
+ }
148
+
149
+ export enum SemanticRole {
150
+ None = 0,
151
+ Button = 1,
152
+ Textbox = 2,
153
+ Link = 3,
154
+ Heading = 4,
155
+ Form = 5,
156
+ List = 6,
157
+ ListItem = 7,
158
+ Image = 8,
159
+ Dialog = 9,
160
+ StaticText = 10,
161
+ Checkbox = 11,
162
+ Radio = 12,
163
+ RadioGroup = 13,
164
+ Switch = 14,
165
+ Slider = 15,
166
+ ComboBox = 16,
167
+ }
168
+
169
+ export enum CursorStyle {
170
+ Default = 0,
171
+ Pointer = 1,
172
+ Text = 2,
173
+ Move = 3,
174
+ Grab = 4,
175
+ Grabbing = 5,
176
+ ResizeNS = 6,
177
+ ResizeEW = 7,
178
+ }
179
+
180
+ export enum SemanticCheckedState {
181
+ None = 0,
182
+ False = 1,
183
+ True = 2,
184
+ Mixed = 3,
185
+ }
186
+
@@ -0,0 +1,34 @@
1
+ import { DrawContext } from "./DrawContext";
2
+ import { CustomDrawable } from "../nodes/CustomDrawable";
3
+ import { EventRouter } from "../core/EventRouter";
4
+
5
+ /**
6
+ * Called from the JS bridge during the render pass when Tier 1
7
+ * encounters an OP_DRAW_CUSTOM instruction.
8
+ *
9
+ * @param handle - The u64 node handle (passed as i64 from JS).
10
+ * @param canvasPtr - The opaque Skia canvas pointer (usize).
11
+ */
12
+ export function fui_dispatch_custom_draw(handle: u64, canvasPtr: usize): void {
13
+ const node = EventRouter.getRegisteredNode(handle);
14
+ if (node !== null && node instanceof CustomDrawable) {
15
+ const custom = node as CustomDrawable;
16
+ const bounds = custom.getBounds();
17
+ const ctx = new DrawContext(canvasPtr);
18
+ const width = unchecked(bounds[2]);
19
+ const height = unchecked(bounds[3]);
20
+ const tl = custom._cornerTopLeft();
21
+ const tr = custom._cornerTopRight();
22
+ const br = custom._cornerBottomRight();
23
+ const bl = custom._cornerBottomLeft();
24
+ ctx.save();
25
+ if (tl > 0.0 || tr > 0.0 || br > 0.0 || bl > 0.0) {
26
+ ctx.clipRoundedRect(0, 0, width, height, tl, tr, br, bl);
27
+ } else {
28
+ ctx.clipRect(0, 0, width, height);
29
+ }
30
+ custom.draw(ctx);
31
+ ctx.restore();
32
+ ctx.flush();
33
+ }
34
+ }
@@ -0,0 +1,189 @@
1
+ import * as ffi from "../core/ffi";
2
+ import { ImageSampling } from "../core/ImageSampling";
3
+ import { error } from "../core/Logger";
4
+ import { Node } from "../core/Node";
5
+ import { TextLayout } from "../text/TextLayout";
6
+ import { Paint } from "./Paint";
7
+ import { Path } from "./Path";
8
+
9
+ const OP_SAVE: u32 = 1;
10
+ const OP_RESTORE: u32 = 2;
11
+ const OP_TRANSLATE: u32 = 3;
12
+ const OP_SCALE: u32 = 4;
13
+ const OP_ROTATE: u32 = 5;
14
+ const OP_CLIP_RECT: u32 = 6;
15
+ const OP_CLIP_ROUND_RECT: u32 = 7;
16
+ const OP_DRAW_RECT: u32 = 10;
17
+ const OP_DRAW_CIRCLE: u32 = 11;
18
+ const OP_DRAW_LINE: u32 = 12;
19
+ const OP_DRAW_ROUND_RECT: u32 = 13;
20
+ const OP_DRAW_PATH: u32 = 20;
21
+ const OP_DRAW_TEXT_NODE: u32 = 30;
22
+ const OP_DRAW_IMAGE: u32 = 31;
23
+ const OP_DRAW_SVG: u32 = 32;
24
+
25
+ /**
26
+ * Immediate-mode drawing context wrapping an opaque Skia canvas pointer.
27
+ *
28
+ * Obtained during the render pass via `CustomDrawable.draw(ctx)` or from
29
+ * `Bitmap.canvas()` for off-screen drawing. The canvas pointer is opaque —
30
+ * all drawing goes through the `fui_canvas_*` FFI functions.
31
+ */
32
+ export class DrawContext {
33
+ private words: Array<u32> = new Array<u32>();
34
+
35
+ constructor(private canvasPtr: usize) {}
36
+
37
+ private pushFloat(value: f32): void {
38
+ this.words.push(reinterpret<u32>(value));
39
+ }
40
+
41
+ flush(): void {
42
+ if (this.words.length == 0) {
43
+ return;
44
+ }
45
+ ffi.fui_canvas_draw_batch(
46
+ this.canvasPtr,
47
+ this.words.dataStart,
48
+ <u32>this.words.length,
49
+ );
50
+ this.words = new Array<u32>();
51
+ }
52
+
53
+ /* ── State ──────────────────────────────────────────────────── */
54
+
55
+ save(): void { this.words.push(OP_SAVE); }
56
+ restore(): void { this.words.push(OP_RESTORE); }
57
+ translate(x: f32, y: f32): void {
58
+ this.words.push(OP_TRANSLATE);
59
+ this.pushFloat(x);
60
+ this.pushFloat(y);
61
+ }
62
+ scale(sx: f32, sy: f32): void {
63
+ this.words.push(OP_SCALE);
64
+ this.pushFloat(sx);
65
+ this.pushFloat(sy);
66
+ }
67
+ rotate(degrees: f32): void {
68
+ this.words.push(OP_ROTATE);
69
+ this.pushFloat(degrees);
70
+ }
71
+ clipRect(x: f32, y: f32, w: f32, h: f32): void {
72
+ this.words.push(OP_CLIP_RECT);
73
+ this.pushFloat(x);
74
+ this.pushFloat(y);
75
+ this.pushFloat(w);
76
+ this.pushFloat(h);
77
+ }
78
+ clipRoundRect(x: f32, y: f32, w: f32, h: f32, radius: f32): void {
79
+ this.clipRoundedRect(x, y, w, h, radius, radius, radius, radius);
80
+ }
81
+ clipRoundedRect(x: f32, y: f32, w: f32, h: f32, topLeft: f32, topRight: f32, bottomRight: f32, bottomLeft: f32): void {
82
+ this.words.push(OP_CLIP_ROUND_RECT);
83
+ this.pushFloat(x);
84
+ this.pushFloat(y);
85
+ this.pushFloat(w);
86
+ this.pushFloat(h);
87
+ this.pushFloat(topLeft);
88
+ this.pushFloat(topRight);
89
+ this.pushFloat(bottomRight);
90
+ this.pushFloat(bottomLeft);
91
+ }
92
+
93
+ /* ── Primitives ─────────────────────────────────────────────── */
94
+
95
+ drawRect(x: f32, y: f32, w: f32, h: f32, paint: Paint): void {
96
+ this.words.push(OP_DRAW_RECT);
97
+ this.pushFloat(x);
98
+ this.pushFloat(y);
99
+ this.pushFloat(w);
100
+ this.pushFloat(h);
101
+ this.words.push(paint.fillColor);
102
+ this.words.push(paint.strokeColor);
103
+ this.pushFloat(paint.strokeWidth);
104
+ }
105
+
106
+ drawCircle(cx: f32, cy: f32, radius: f32, paint: Paint): void {
107
+ this.words.push(OP_DRAW_CIRCLE);
108
+ this.pushFloat(cx);
109
+ this.pushFloat(cy);
110
+ this.pushFloat(radius);
111
+ this.words.push(paint.fillColor);
112
+ this.words.push(paint.strokeColor);
113
+ this.pushFloat(paint.strokeWidth);
114
+ }
115
+
116
+ drawLine(x1: f32, y1: f32, x2: f32, y2: f32, color: u32, strokeWidth: f32): void {
117
+ this.words.push(OP_DRAW_LINE);
118
+ this.pushFloat(x1);
119
+ this.pushFloat(y1);
120
+ this.pushFloat(x2);
121
+ this.pushFloat(y2);
122
+ this.words.push(color);
123
+ this.pushFloat(strokeWidth);
124
+ }
125
+
126
+ drawRoundRect(x: f32, y: f32, w: f32, h: f32, rx: f32, ry: f32, paint: Paint): void {
127
+ this.words.push(OP_DRAW_ROUND_RECT);
128
+ this.pushFloat(x);
129
+ this.pushFloat(y);
130
+ this.pushFloat(w);
131
+ this.pushFloat(h);
132
+ this.pushFloat(rx);
133
+ this.pushFloat(ry);
134
+ this.words.push(paint.fillColor);
135
+ this.words.push(paint.strokeColor);
136
+ this.pushFloat(paint.strokeWidth);
137
+ }
138
+
139
+ /* ── Path ───────────────────────────────────────────────────── */
140
+
141
+ drawPath(path: Path, paint: Paint): void {
142
+ this.words.push(OP_DRAW_PATH);
143
+ this.words.push(path._pathId);
144
+ this.words.push(paint.fillColor);
145
+ this.words.push(paint.strokeColor);
146
+ this.pushFloat(paint.strokeWidth);
147
+ }
148
+
149
+ /* ── Text ───────────────────────────────────────────────────── */
150
+
151
+ drawTextNode(node: Node, x: f32, y: f32): void {
152
+ const handle = node.builtHandle;
153
+ this.words.push(OP_DRAW_TEXT_NODE);
154
+ this.words.push(<u32>handle);
155
+ this.words.push(<u32>(handle >> 32));
156
+ this.pushFloat(x);
157
+ this.pushFloat(y);
158
+ }
159
+
160
+ drawTextLayout(layout: TextLayout, x: f32, y: f32): void {
161
+ if (!layout.isReady) {
162
+ error("TextLayout", "DrawContext.drawTextLayout() called before the TextLayout was ready; register onReady/onReadyWith and draw after the callback.");
163
+ return;
164
+ }
165
+ this.drawTextNode(layout._drawNode(), x, y);
166
+ }
167
+
168
+ /* ── Image / SVG ─────────────────────────────────────────────── */
169
+
170
+ drawImage(textureId: u32, x: f32, y: f32, w: f32, h: f32, sampling: ImageSampling = ImageSampling.linear()): void {
171
+ this.words.push(OP_DRAW_IMAGE);
172
+ this.words.push(textureId);
173
+ this.pushFloat(x);
174
+ this.pushFloat(y);
175
+ this.pushFloat(w);
176
+ this.pushFloat(h);
177
+ this.words.push(<u32>sampling.kind);
178
+ this.words.push(sampling.maxAniso);
179
+ }
180
+
181
+ drawSvg(svgId: u32, x: f32, y: f32, w: f32, h: f32): void {
182
+ this.words.push(OP_DRAW_SVG);
183
+ this.words.push(svgId);
184
+ this.pushFloat(x);
185
+ this.pushFloat(y);
186
+ this.pushFloat(w);
187
+ this.pushFloat(h);
188
+ }
189
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Paint descriptor for DrawContext drawing operations.
3
+ *
4
+ * Models fill and stroke properties as a lightweight value type.
5
+ * Color encoding is 0xRRGGBBAA (matching the repo's Color type).
6
+ */
7
+ export class Paint {
8
+ constructor(
9
+ public fillColor: u32 = 0, // 0xRRGGBBAA; 0 = transparent (no fill)
10
+ public strokeColor: u32 = 0, // 0xRRGGBBAA; 0 = no stroke
11
+ public strokeWidth: f32 = 0, // stroke width in logical pixels
12
+ ) {}
13
+
14
+ static fill(color: u32): Paint {
15
+ return new Paint(color, 0, 0);
16
+ }
17
+
18
+ static stroke(color: u32, width: f32): Paint {
19
+ return new Paint(0, color, width);
20
+ }
21
+
22
+ static filledStroke(fillColor: u32, strokeColor: u32, strokeWidth: f32): Paint {
23
+ return new Paint(fillColor, strokeColor, strokeWidth);
24
+ }
25
+
26
+ /** Returns true if this paint produces any visible output. */
27
+ hasFill(): bool {
28
+ return (this.fillColor & 0xff) !== 0;
29
+ }
30
+
31
+ hasStroke(): bool {
32
+ return this.strokeWidth > 0 && (this.strokeColor & 0xff) !== 0;
33
+ }
34
+ }
@@ -0,0 +1,40 @@
1
+ import * as ffi from "../core/ffi";
2
+ import { Disposable } from "../core/Disposable";
3
+
4
+ /**
5
+ * Retained Skia path, built imperatively then drawn via DrawContext.
6
+ *
7
+ * Callers must `dispose()` the path when done to release the engine-side resource.
8
+ * Paths are pooled in Tier 1 and identified by an opaque u32 ID.
9
+ */
10
+ export class Path implements Disposable {
11
+ // @ts-ignore: assigned in constructor via FFI
12
+ private _id: u32;
13
+ private disposed: bool = false;
14
+
15
+ constructor() {
16
+ this._id = ffi.fui_path_create();
17
+ }
18
+
19
+ /** @internal — exposed for DrawContext.drawPath */
20
+ get _pathId(): u32 {
21
+ return this._id;
22
+ }
23
+
24
+ moveTo(x: f32, y: f32): Path { ffi.fui_path_move_to(this._id, x, y); return this; }
25
+ lineTo(x: f32, y: f32): Path { ffi.fui_path_line_to(this._id, x, y); return this; }
26
+ quadTo(cx: f32, cy: f32, x: f32, y: f32): Path { ffi.fui_path_quad_to(this._id, cx, cy, x, y); return this; }
27
+ cubicTo(cx1: f32, cy1: f32, cx2: f32, cy2: f32, x: f32, y: f32): Path {
28
+ ffi.fui_path_cubic_to(this._id, cx1, cy1, cx2, cy2, x, y); return this;
29
+ }
30
+ close(): Path { ffi.fui_path_close(this._id); return this; }
31
+ addRect(x: f32, y: f32, w: f32, h: f32): Path { ffi.fui_path_add_rect(this._id, x, y, w, h); return this; }
32
+ addCircle(cx: f32, cy: f32, r: f32): Path { ffi.fui_path_add_circle(this._id, cx, cy, r); return this; }
33
+
34
+ dispose(): void {
35
+ if (!this.disposed) {
36
+ ffi.fui_path_destroy(this._id);
37
+ this.disposed = true;
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,61 @@
1
+ import { FlexBox } from "./FlexBox";
2
+ import { DrawContext } from "../drawing/DrawContext";
3
+ import { NodeType } from "../core/ffi";
4
+ import * as ui from "../bindings/ui";
5
+ import { markNeedsCommit } from "../core/FrameScheduler";
6
+
7
+ /**
8
+ * A retained-mode node that draws itself procedurally via a `DrawContext`
9
+ * during the render pass. Extends `FlexBox` for full layout, styling, and
10
+ * child support.
11
+ *
12
+ * Subclasses override `draw(ctx)` with immediate-mode Skia drawing.
13
+ *
14
+ * **Dirty model:** EffinDom only repaints when something is dirty — there
15
+ * is no automatic 60fps repaint loop. A `CustomDrawable` renders once on
16
+ * the initial frame, then only again when `markDirty()` is called. For
17
+ * animations, rebind a `Signal` or timer to call `markDirty()` each frame.
18
+ *
19
+ * ```
20
+ * class Gauge extends CustomDrawable {
21
+ * readonly value = new Signal<f32>(0);
22
+ *
23
+ * constructor() {
24
+ * super();
25
+ * this.width(200).height(200);
26
+ * this.value.bind(this, () => this.markDirty());
27
+ * }
28
+ *
29
+ * draw(ctx: DrawContext): void {
30
+ * // immediate-mode Skia drawing — called only when dirty
31
+ * }
32
+ * }
33
+ * ```
34
+ */
35
+ export abstract class CustomDrawable extends FlexBox {
36
+ abstract draw(ctx: DrawContext): void;
37
+
38
+ build(): u64 {
39
+ this.buildStyledNode(NodeType.FlexBox);
40
+ ui.setCustomDrawable(this.handle, true);
41
+ return this.handle;
42
+ }
43
+
44
+ /**
45
+ * Request a redraw on the next frame.
46
+ *
47
+ * Call this whenever the visual state changes (e.g. a bound `Signal`
48
+ * fires, a timer ticks, or a layout mutation occurs). Without this
49
+ * call the `draw()` method will not be invoked again after the initial
50
+ * render.
51
+ */
52
+ markDirty(): void {
53
+ if (this.handle !== 0) {
54
+ const visibleBounds = ui.tryGetVisibleBounds(this.handle);
55
+ if (visibleBounds === null || unchecked(visibleBounds[2]) <= 0.0 || unchecked(visibleBounds[3]) <= 0.0) {
56
+ return;
57
+ }
58
+ }
59
+ markNeedsCommit();
60
+ }
61
+ }
@@ -5,7 +5,7 @@ import {
5
5
  Animation,
6
6
  AnimationTiming,
7
7
  } from "../core/Animation";
8
- import { Node } from "../core/Node";
8
+ import { PointerClickEventArgs, PointerEventArgs, Node } from "../core/Node";
9
9
  import { warn } from "../core/Logger";
10
10
  import { throwNullArgument } from "../core/Errors";
11
11
  import { NodeTransitions } from "../core/Transitions";
@@ -14,6 +14,7 @@ import {
14
14
  AlignItems,
15
15
  BorderStyle,
16
16
  FlexDirection,
17
+ FlexWrap,
17
18
  HandleValue,
18
19
  JustifyContent,
19
20
  NodeType,
@@ -87,6 +88,28 @@ export class FlexBoxProps {
87
88
  readonly childNodes: Array<Node> = new Array<Node>();
88
89
  }
89
90
 
91
+ export class Border {
92
+ constructor(
93
+ readonly width: f32,
94
+ readonly color: u32,
95
+ readonly style: BorderStyle = BorderStyle.Solid,
96
+ readonly dashOn: f32 = 0.0,
97
+ readonly dashOff: f32 = 0.0,
98
+ ) {}
99
+
100
+ static solid(width: f32, color: u32): Border {
101
+ return new Border(width, color, BorderStyle.Solid);
102
+ }
103
+
104
+ static dashed(width: f32, color: u32, dashOn: f32, dashOff: f32): Border {
105
+ return new Border(width, color, BorderStyle.Dashed, dashOn, dashOff);
106
+ }
107
+
108
+ static dotted(width: f32, color: u32, dashOn: f32, dashOff: f32): Border {
109
+ return new Border(width, color, BorderStyle.Dotted, dashOn, dashOff);
110
+ }
111
+ }
112
+
90
113
  export class FlexBox extends Node {
91
114
  private widthValue: f32 = 0.0;
92
115
  private widthUnit: Unit = Unit.Pixel;
@@ -127,6 +150,8 @@ export class FlexBox extends Node {
127
150
  private borderDashOn: f32 = 0.0;
128
151
  private borderDashOff: f32 = 0.0;
129
152
  private flexDirectionValue: FlexDirection = FlexDirection.Column;
153
+ private hasFlexWrap: bool = false;
154
+ private flexWrapValue: FlexWrap = FlexWrap.NoWrap;
130
155
  private hasFlexDirection: bool = false;
131
156
  private justifyContentValue: JustifyContent = JustifyContent.Start;
132
157
  private hasJustifyContent: bool = false;
@@ -358,10 +383,15 @@ export class FlexBox extends Node {
358
383
  return this;
359
384
  }
360
385
 
361
- border(width: f32, color: u32, style: BorderStyle = BorderStyle.Solid): this {
386
+ _cornerTopLeft(): f32 { return this.cornerTopLeft; }
387
+ _cornerTopRight(): f32 { return this.cornerTopRight; }
388
+ _cornerBottomRight(): f32 { return this.cornerBottomRight; }
389
+ _cornerBottomLeft(): f32 { return this.cornerBottomLeft; }
390
+
391
+ border(width: f32, color: u32): this {
362
392
  this.borderWidth = width;
363
393
  this.borderColor = color;
364
- this.borderStyle = style;
394
+ this.borderStyle = BorderStyle.Solid;
365
395
  this.borderDashOn = 0.0;
366
396
  this.borderDashOff = 0.0;
367
397
  this.hasBoxStyle = true;
@@ -372,10 +402,12 @@ export class FlexBox extends Node {
372
402
  return this;
373
403
  }
374
404
 
375
- borderDashed(on: f32, off: f32): this {
376
- this.borderStyle = BorderStyle.Dashed;
377
- this.borderDashOn = on;
378
- this.borderDashOff = off;
405
+ borderConfig(border: Border): this {
406
+ this.borderWidth = border.width;
407
+ this.borderColor = border.color;
408
+ this.borderStyle = border.style;
409
+ this.borderDashOn = border.dashOn;
410
+ this.borderDashOff = border.dashOff;
379
411
  this.hasBoxStyle = true;
380
412
  if (this.hasBuiltHandle()) {
381
413
  this.applyVisualStyle();
@@ -395,6 +427,17 @@ export class FlexBox extends Node {
395
427
  return this;
396
428
  }
397
429
 
430
+ flexWrap(wrap: FlexWrap): this {
431
+ this.flexWrapValue = wrap;
432
+ this.hasFlexWrap = true;
433
+ if (this.hasBuiltHandle()) {
434
+ ui.setFlexWrap(this.handle, <u32>wrap);
435
+ this.notifyRetainedLayoutMutation();
436
+ this.onRetainedChildLayoutChanged();
437
+ }
438
+ return this;
439
+ }
440
+
398
441
  justifyContent(justify: JustifyContent): this {
399
442
  this.justifyContentValue = justify;
400
443
  this.hasJustifyContent = true;
@@ -571,17 +614,17 @@ export class FlexBox extends Node {
571
614
  return this;
572
615
  }
573
616
 
574
- onClick(cb: () => void): this {
575
- super.onClick(cb);
617
+ onPointerClick(cb: (event: PointerClickEventArgs) => void): this {
618
+ super.onPointerClick(cb);
576
619
  return this;
577
620
  }
578
621
 
579
- onPointerEnter(cb: () => void): this {
622
+ onPointerEnter(cb: (event: PointerEventArgs) => void): this {
580
623
  super.onPointerEnter(cb);
581
624
  return this;
582
625
  }
583
626
 
584
- onPointerLeave(cb: () => void): this {
627
+ onPointerLeave(cb: (event: PointerEventArgs) => void): this {
585
628
  super.onPointerLeave(cb);
586
629
  return this;
587
630
  }
@@ -676,6 +719,9 @@ export class FlexBox extends Node {
676
719
  if (this.hasFlexDirection) {
677
720
  ui.setFlexDirection(this.handle, <u32>this.flexDirectionValue);
678
721
  }
722
+ if (this.hasFlexWrap) {
723
+ ui.setFlexWrap(this.handle, <u32>this.flexWrapValue);
724
+ }
679
725
  if (this.hasFlexBasis) {
680
726
  ui.setFlexBasis(this.handle, this.flexBasisValue);
681
727
  }