@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.
- package/browser/routed-app-conventions.ts +1 -1
- package/browser/src/common-harness.ts +1 -27
- package/browser/src/index.ts +19 -26
- package/browser/src/routed-harness.ts +1 -194
- package/browser/src/shared-browser.ts +6 -0
- package/package.json +26 -16
- package/scripts/build.sh +51 -47
- package/scripts/framework-host-services.ts +1 -1
- package/scripts/generate-host-events.ts +15 -241
- package/scripts/generate-host-services.ts +13 -261
- package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
- package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
- package/scripts/hostgen/common.ts +73 -0
- package/scripts/hostgen/registry.ts +159 -0
- package/src/Fui.ts +70 -4
- package/src/FuiExports.ts +13 -1
- package/src/bindings/ui.ts +165 -8
- package/src/controls/Button.ts +98 -71
- package/src/controls/Checkbox.ts +23 -21
- package/src/controls/ComboBox.ts +1201 -0
- package/src/controls/ContextMenu.ts +279 -165
- package/src/controls/Dialog.ts +91 -17
- package/src/controls/Dropdown.ts +101 -333
- package/src/controls/NavLink.ts +29 -6
- package/src/controls/Popup.ts +10 -5
- package/src/controls/ProgressBar.ts +1 -2
- package/src/controls/RadioButton.ts +34 -13
- package/src/controls/RadioGroup.ts +32 -19
- package/src/controls/Slider.ts +21 -23
- package/src/controls/Switch.ts +34 -17
- package/src/controls/index.ts +2 -1
- package/src/controls/internal/ButtonPresenter.ts +5 -2
- package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
- package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
- package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
- package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
- package/src/controls/internal/PressableLabeledControl.ts +4 -7
- package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
- package/src/controls/internal/SelectablePopupList.ts +463 -0
- package/src/controls/internal/SliderPresenter.ts +5 -5
- package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
- package/src/controls/internal/TextInputCore.ts +307 -41
- package/src/controls/internal/TextInputPresenter.ts +2 -2
- package/src/controls/templating.ts +10 -0
- package/src/core/Application.ts +19 -9
- package/src/core/Bitmap.ts +219 -1
- package/src/core/ContextMenuManager.ts +128 -44
- package/src/core/DebugApi.ts +17 -2
- package/src/core/DragGesture.ts +8 -7
- package/src/core/EventRouter.ts +332 -12
- package/src/core/Fetch.ts +15 -7
- package/src/core/FetchFfi.ts +1 -15
- package/src/core/File.ts +75 -34
- package/src/core/FocusAdornerManager.ts +3 -3
- package/src/core/FocusVisibility.ts +7 -0
- package/src/core/FrameScheduler.ts +126 -0
- package/src/core/ImageSampling.ts +43 -0
- package/src/core/Logger.ts +2 -0
- package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
- package/src/core/Node.ts +1039 -97
- package/src/core/Platform.ts +16 -16
- package/src/core/SelectionHandleAdornerManager.ts +697 -0
- package/src/core/Theme.ts +34 -20
- package/src/core/ToolTipManager.ts +3 -3
- package/src/core/Typography.ts +411 -19
- package/src/core/Worker.ts +69 -33
- package/src/core/event_exports.ts +123 -5
- package/src/core/ffi.ts +5 -746
- package/src/core/generated/FetchHostAbi.ts +10 -0
- package/src/core/generated/FrameworkHostServices.ts +1 -1
- package/src/core/generated/HostAbi.ts +220 -0
- package/src/core/generated/UiAbi.ts +356 -0
- package/src/core/generated/UiEnums.ts +186 -0
- package/src/drawing/DrawCallback.ts +34 -0
- package/src/drawing/DrawContext.ts +189 -0
- package/src/drawing/Paint.ts +34 -0
- package/src/drawing/Path.ts +40 -0
- package/src/nodes/CustomDrawable.ts +61 -0
- package/src/nodes/FlexBox.ts +57 -11
- package/src/nodes/Grid.ts +5 -5
- package/src/nodes/Image.ts +20 -1
- package/src/nodes/RichText.ts +36 -25
- package/src/nodes/ScrollBar.ts +2 -1
- package/src/nodes/ScrollBox.ts +7 -2
- package/src/nodes/ScrollView.ts +17 -6
- package/src/nodes/Svg.ts +18 -1
- package/src/nodes/Text.ts +1 -1
- package/src/nodes/TextCore.ts +115 -20
- package/src/nodes/index.ts +3 -1
- package/src/text/DynamicTextLayout.ts +275 -0
- package/src/text/TextLayout.ts +308 -0
- package/src/worker/Worker.ts +3 -36
- package/src/worker/WorkerJob.ts +5 -9
- package/src/worker/ffi.ts +0 -6
- package/browser/src/common-harness/host-imports.ts +0 -447
- package/browser/src/common-harness/interop.ts +0 -39
- package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
- package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
- package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
- package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
- package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
- package/browser/src/common-harness/managed-harness-session.ts +0 -15
- package/browser/src/common-harness/managed-harness.ts +0 -1451
- package/browser/src/common-harness/managed-history.ts +0 -168
- package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
- package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
- package/browser/src/common-harness/text-session-bridge.ts +0 -452
- package/browser/src/common-harness/types.ts +0 -205
- package/browser/src/common-harness/ui-chrome.ts +0 -206
- package/browser/src/common-harness/ui-imports.ts +0 -553
- package/browser/src/common-harness/wasm-module-cache.ts +0 -47
- package/browser/src/file-processing-worker.ts +0 -89
- package/browser/src/host-events.ts +0 -118
- package/browser/src/host-services.ts +0 -483
- package/browser/src/persisted-ui-state.ts +0 -206
- package/browser/src/routed-app-conventions.ts +0 -292
- package/browser/src/worker-bootstrap.ts +0 -916
- package/browser/src/worker-manager.ts +0 -234
- 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
|
+
}
|
package/src/nodes/FlexBox.ts
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
376
|
-
this.
|
|
377
|
-
this.
|
|
378
|
-
this.
|
|
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
|
-
|
|
575
|
-
super.
|
|
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
|
}
|