@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,275 @@
|
|
|
1
|
+
import * as ui from "../bindings/ui";
|
|
2
|
+
import { TextOverflow } from "../core/ffi";
|
|
3
|
+
import { FontFamily, FontStack, FontStyle, FontWeight } from "../core/Typography";
|
|
4
|
+
import { TextAlign, TextVerticalAlign, Unit } from "../core/ffi";
|
|
5
|
+
import { TextLayout, TextLayoutReadyEventArgs, TextLayoutReadyHandler } from "./TextLayout";
|
|
6
|
+
|
|
7
|
+
export enum DynamicTextOverflow {
|
|
8
|
+
Reject = 0,
|
|
9
|
+
FallbackShape = 1,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type DynamicTextLayoutReadyHandler<Owner> = TextLayoutReadyHandler<Owner>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Short, single-style text resource for labels that change frequently.
|
|
16
|
+
*
|
|
17
|
+
* Stage 4 keeps the public API and validation contract separate from the
|
|
18
|
+
* retained text node backing used by `TextLayout`. A later core resource pass
|
|
19
|
+
* can replace the backing without changing call sites.
|
|
20
|
+
*/
|
|
21
|
+
export class DynamicTextLayout extends TextLayout {
|
|
22
|
+
private charsetValue: string = "";
|
|
23
|
+
private overflowValue: DynamicTextOverflow = DynamicTextOverflow.FallbackShape;
|
|
24
|
+
private currentTextValue: string = "";
|
|
25
|
+
private numericMode: bool = false;
|
|
26
|
+
private numericPrecisionValue: i32 = -1;
|
|
27
|
+
private numericPrefixValue: string = "";
|
|
28
|
+
private numericSuffixValue: string = "";
|
|
29
|
+
private hasNumericValue: bool = false;
|
|
30
|
+
private numericValue: f64 = 0.0;
|
|
31
|
+
|
|
32
|
+
constructor(charset: string = "") {
|
|
33
|
+
super("");
|
|
34
|
+
this.charsetValue = charset;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static fixedCharset(charset: string): DynamicTextLayout {
|
|
38
|
+
return new DynamicTextLayout(charset);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static numeric(): DynamicTextLayout {
|
|
42
|
+
const layout = new DynamicTextLayout("0123456789.-");
|
|
43
|
+
layout.numericMode = true;
|
|
44
|
+
return layout;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get currentText(): string {
|
|
48
|
+
return this.currentTextValue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @internal */
|
|
52
|
+
_prepareNow(): void {
|
|
53
|
+
ui.setDynamicTextCharset(this._drawNode().builtHandle, this.charsetValue);
|
|
54
|
+
super._prepareNow();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setText(value: string): bool {
|
|
58
|
+
if (!this.supportsText(value)) {
|
|
59
|
+
if (this.overflowValue == DynamicTextOverflow.Reject) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const wasReady = this.isReady;
|
|
64
|
+
this.currentTextValue = value;
|
|
65
|
+
super.text(value);
|
|
66
|
+
if (wasReady) {
|
|
67
|
+
this._prepareNow();
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
overflow(mode: DynamicTextOverflow): this {
|
|
73
|
+
this.overflowValue = mode;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
precision(digits: i32): this {
|
|
78
|
+
this.numericMode = true;
|
|
79
|
+
this.numericPrecisionValue = digits < 0 ? 0 : digits;
|
|
80
|
+
this.refreshNumericText();
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
prefix(value: string): this {
|
|
85
|
+
this.numericMode = true;
|
|
86
|
+
this.numericPrefixValue = value;
|
|
87
|
+
this.includeInCharset(value);
|
|
88
|
+
this.refreshNumericText();
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
suffix(value: string): this {
|
|
93
|
+
this.numericMode = true;
|
|
94
|
+
this.numericSuffixValue = value;
|
|
95
|
+
this.includeInCharset(value);
|
|
96
|
+
this.refreshNumericText();
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setValue(value: f64): bool {
|
|
101
|
+
this.numericMode = true;
|
|
102
|
+
this.hasNumericValue = true;
|
|
103
|
+
this.numericValue = value;
|
|
104
|
+
return this.setText(this.composeNumericText(value));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
textOverflow(mode: TextOverflow): this {
|
|
108
|
+
super.overflow(mode);
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
onReady(callback: (event: TextLayoutReadyEventArgs) => void): this {
|
|
113
|
+
super.onReady(callback);
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
onReadyWith<Owner>(owner: Owner, handler: TextLayoutReadyHandler<Owner>): this {
|
|
118
|
+
super.onReadyWith<Owner>(owner, handler);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
width(value: f32, unit: Unit = Unit.Pixel): this {
|
|
123
|
+
super.width(value, unit);
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
height(value: f32, unit: Unit = Unit.Pixel): this {
|
|
128
|
+
super.height(value, unit);
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fontStack(stack: FontStack, size: f32): this {
|
|
133
|
+
super.fontStack(stack, size);
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fontFamily(family: FontFamily): this {
|
|
138
|
+
super.fontFamily(family);
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fontWeight(weight: FontWeight): this {
|
|
143
|
+
super.fontWeight(weight);
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fontStyle(style: FontStyle): this {
|
|
148
|
+
super.fontStyle(style);
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fontSize(size: f32): this {
|
|
153
|
+
super.fontSize(size);
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
lineHeight(px: f32): this {
|
|
158
|
+
super.lineHeight(px);
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
color(color: u32): this {
|
|
163
|
+
super.color(color);
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
textColor(color: u32): this {
|
|
168
|
+
super.textColor(color);
|
|
169
|
+
return this;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
textAlign(align: TextAlign): this {
|
|
173
|
+
super.textAlign(align);
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
verticalAlign(align: TextVerticalAlign): this {
|
|
178
|
+
super.verticalAlign(align);
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
maxLines(lines: i32): this {
|
|
183
|
+
super.maxLines(lines);
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
textLimits(maxChars: i32, maxLines: i32): this {
|
|
188
|
+
super.textLimits(maxChars, maxLines);
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
wrap(flag: bool = true): this {
|
|
193
|
+
super.wrap(flag);
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
wrapping(flag: bool = true): this {
|
|
198
|
+
super.wrapping(flag);
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private supportsText(value: string): bool {
|
|
203
|
+
if (this.charsetValue.length == 0) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
for (let i = 0; i < value.length; i++) {
|
|
207
|
+
if (!this.charsetContains(value.charCodeAt(i))) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private charsetContains(code: i32): bool {
|
|
215
|
+
for (let i = 0; i < this.charsetValue.length; i++) {
|
|
216
|
+
if (this.charsetValue.charCodeAt(i) == code) {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private includeInCharset(value: string): void {
|
|
224
|
+
for (let i = 0; i < value.length; i++) {
|
|
225
|
+
const code = value.charCodeAt(i);
|
|
226
|
+
if (!this.charsetContains(code)) {
|
|
227
|
+
this.charsetValue += value.charAt(i);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private refreshNumericText(): void {
|
|
233
|
+
if (!this.numericMode || !this.hasNumericValue) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
this.setText(this.composeNumericText(this.numericValue));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private composeNumericText(value: f64): string {
|
|
240
|
+
this.includeInCharset(this.numericPrefixValue);
|
|
241
|
+
this.includeInCharset(this.numericSuffixValue);
|
|
242
|
+
return this.numericPrefixValue + this.formatNumericValue(value) + this.numericSuffixValue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private formatNumericValue(value: f64): string {
|
|
246
|
+
if (value != value || !isFinite(value) || this.numericPrecisionValue < 0) {
|
|
247
|
+
return value.toString();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let scale: i64 = 1;
|
|
251
|
+
for (let i = 0; i < this.numericPrecisionValue; i++) {
|
|
252
|
+
scale *= 10;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const negative = value < 0.0;
|
|
256
|
+
const absValue = negative ? -value : value;
|
|
257
|
+
const scaled = <i64>Math.round(absValue * <f64>scale);
|
|
258
|
+
const whole = scaled / scale;
|
|
259
|
+
const fraction = scaled % scale;
|
|
260
|
+
|
|
261
|
+
let text = whole.toString();
|
|
262
|
+
if (this.numericPrecisionValue > 0) {
|
|
263
|
+
let fractionText = fraction.toString();
|
|
264
|
+
while (fractionText.length < this.numericPrecisionValue) {
|
|
265
|
+
fractionText = "0" + fractionText;
|
|
266
|
+
}
|
|
267
|
+
text += "." + fractionText;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (negative) {
|
|
271
|
+
text = "-" + text;
|
|
272
|
+
}
|
|
273
|
+
return text;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import * as ui from "../bindings/ui";
|
|
2
|
+
import { LoadedEventArgs, onLoadedWith } from "../core/FrameScheduler";
|
|
3
|
+
import { error } from "../core/Logger";
|
|
4
|
+
import { Node } from "../core/Node";
|
|
5
|
+
import { FontFace, FontFamily, FontStack, FontStyle, FontWeight, FontsLoadedEventArgs } from "../core/Typography";
|
|
6
|
+
import { TextAlign, TextOverflow, TextVerticalAlign, Unit } from "../core/ffi";
|
|
7
|
+
import { RichText, RichTextSpan } from "../nodes/RichText";
|
|
8
|
+
import { Text } from "../nodes/Text";
|
|
9
|
+
|
|
10
|
+
export class TextLayoutReadyEventArgs {
|
|
11
|
+
readonly layout: TextLayout;
|
|
12
|
+
|
|
13
|
+
constructor(layout: TextLayout) {
|
|
14
|
+
this.layout = layout;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type TextLayoutReadyHandler<Owner> = (owner: Owner, event: TextLayoutReadyEventArgs) => void;
|
|
19
|
+
|
|
20
|
+
export class TextMetrics {
|
|
21
|
+
constructor(
|
|
22
|
+
readonly width: f32 = 0.0,
|
|
23
|
+
readonly height: f32 = 0.0,
|
|
24
|
+
readonly baseline: f32 = 0.0,
|
|
25
|
+
readonly lineCount: i32 = 0,
|
|
26
|
+
readonly maxLineWidth: f32 = 0.0,
|
|
27
|
+
) {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class TextLayoutReadyCallback {
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly layout: TextLayout,
|
|
33
|
+
private readonly callback: (event: TextLayoutReadyEventArgs) => void,
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
handleReady(): void {
|
|
37
|
+
onLoadedWith<TextLayoutReadyCallback>(this, handleTextLayoutLoaded);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
handleLoaded(): void {
|
|
41
|
+
this.layout._ensureBuiltForReady();
|
|
42
|
+
FontFace.whenFontsLoadedWith<TextLayoutReadyCallback>(
|
|
43
|
+
this.layout._requiredFontIdsForReady(),
|
|
44
|
+
this,
|
|
45
|
+
handleTextLayoutFontsReady,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
handleFontsReady(): void {
|
|
50
|
+
this.layout._prepareNow();
|
|
51
|
+
this.callback(new TextLayoutReadyEventArgs(this.layout));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class TextLayoutReadyHandlerAction<Owner> {
|
|
56
|
+
constructor(
|
|
57
|
+
private readonly layout: TextLayout,
|
|
58
|
+
private readonly owner: Owner,
|
|
59
|
+
private readonly handler: TextLayoutReadyHandler<Owner>,
|
|
60
|
+
) {}
|
|
61
|
+
|
|
62
|
+
handleReady(): void {
|
|
63
|
+
onLoadedWith<TextLayoutReadyHandlerAction<Owner>>(
|
|
64
|
+
this,
|
|
65
|
+
(action: TextLayoutReadyHandlerAction<Owner>, _event: LoadedEventArgs): void => {
|
|
66
|
+
action.handleLoaded();
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
handleLoaded(): void {
|
|
72
|
+
this.layout._ensureBuiltForReady();
|
|
73
|
+
FontFace.whenFontsLoadedWith<TextLayoutReadyHandlerAction<Owner>>(
|
|
74
|
+
this.layout._requiredFontIdsForReady(),
|
|
75
|
+
this,
|
|
76
|
+
(action: TextLayoutReadyHandlerAction<Owner>, _event: FontsLoadedEventArgs): void => {
|
|
77
|
+
action.handleFontsReady();
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
handleFontsReady(): void {
|
|
83
|
+
this.layout._prepareNow();
|
|
84
|
+
this.handler(this.owner, new TextLayoutReadyEventArgs(this.layout));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleTextLayoutReady(callback: TextLayoutReadyCallback): void {
|
|
89
|
+
callback.handleReady();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function handleTextLayoutLoaded(callback: TextLayoutReadyCallback, _event: LoadedEventArgs): void {
|
|
93
|
+
callback.handleLoaded();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function handleTextLayoutFontsReady(callback: TextLayoutReadyCallback, _event: FontsLoadedEventArgs): void {
|
|
97
|
+
callback.handleFontsReady();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Immediate-mode formatted text resource.
|
|
102
|
+
*
|
|
103
|
+
* Stage 3 backs this with the existing retained text preparation pipeline so it
|
|
104
|
+
* shares HarfBuzz shaping, fallback fonts, RichText style runs, and core glyph
|
|
105
|
+
* rendering with `Text`/`RichText`.
|
|
106
|
+
*/
|
|
107
|
+
export class TextLayout {
|
|
108
|
+
private readonly nodeValue: Text;
|
|
109
|
+
private readyValue: bool = false;
|
|
110
|
+
private metricsValue: TextMetrics = new TextMetrics();
|
|
111
|
+
|
|
112
|
+
constructor(text: string = "", node: Text | null = null) {
|
|
113
|
+
this.nodeValue = node === null ? new Text(text) : changetype<Text>(node);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static text(text: string): TextLayout {
|
|
117
|
+
return new TextLayout(text);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static rich(fragments: Array<RichTextSpan>): TextLayout {
|
|
121
|
+
return new TextLayout("", new RichText(fragments));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get isReady(): bool {
|
|
125
|
+
return this.readyValue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** @internal */
|
|
129
|
+
_drawNode(): Node {
|
|
130
|
+
this.ensureBuilt();
|
|
131
|
+
return this.nodeValue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @internal */
|
|
135
|
+
_prepareNow(): void {
|
|
136
|
+
this.ensureBuilt();
|
|
137
|
+
const prepared = ui.prepareNode(this.nodeValue.builtHandle);
|
|
138
|
+
if (prepared == 0) {
|
|
139
|
+
this.readyValue = false;
|
|
140
|
+
this.metricsValue = new TextMetrics();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const metrics = ui.tryGetTextMetrics(this.nodeValue.builtHandle);
|
|
144
|
+
if (metrics === null) {
|
|
145
|
+
this.metricsValue = new TextMetrics();
|
|
146
|
+
} else {
|
|
147
|
+
this.metricsValue = new TextMetrics(
|
|
148
|
+
unchecked(metrics[0]),
|
|
149
|
+
unchecked(metrics[1]),
|
|
150
|
+
unchecked(metrics[2]),
|
|
151
|
+
<i32>unchecked(metrics[3]),
|
|
152
|
+
unchecked(metrics[4]),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
this.readyValue = true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
measure(): TextMetrics {
|
|
159
|
+
if (!this.readyValue) {
|
|
160
|
+
error("TextLayout", "TextLayout.measure() called before the TextLayout was ready; register onReady/onReadyWith and measure after the callback.");
|
|
161
|
+
return new TextMetrics();
|
|
162
|
+
}
|
|
163
|
+
return this.metricsValue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get measuredWidth(): f32 {
|
|
167
|
+
return this.measure().width;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get measuredHeight(): f32 {
|
|
171
|
+
return this.measure().height;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
onReady(callback: (event: TextLayoutReadyEventArgs) => void): this {
|
|
175
|
+
const ready = new TextLayoutReadyCallback(this, callback);
|
|
176
|
+
handleTextLayoutReady(ready);
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
onReadyWith<Owner>(owner: Owner, handler: TextLayoutReadyHandler<Owner>): this {
|
|
181
|
+
const ready = new TextLayoutReadyHandlerAction<Owner>(this, owner, handler);
|
|
182
|
+
ready.handleReady();
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
text(value: string): this {
|
|
187
|
+
this.nodeValue.text(value);
|
|
188
|
+
this.markDirty();
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
width(value: f32, unit: Unit = Unit.Pixel): this {
|
|
193
|
+
this.nodeValue.width(value, unit);
|
|
194
|
+
this.markDirty();
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
height(value: f32, unit: Unit = Unit.Pixel): this {
|
|
199
|
+
this.nodeValue.height(value, unit);
|
|
200
|
+
this.markDirty();
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fontStack(stack: FontStack, size: f32): this {
|
|
205
|
+
this.nodeValue.fontStack(stack, size);
|
|
206
|
+
this.markDirty();
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fontFamily(family: FontFamily): this {
|
|
211
|
+
this.nodeValue.fontFamily(family);
|
|
212
|
+
this.markDirty();
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
fontWeight(weight: FontWeight): this {
|
|
217
|
+
this.nodeValue.fontWeight(weight);
|
|
218
|
+
this.markDirty();
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fontStyle(style: FontStyle): this {
|
|
223
|
+
this.nodeValue.fontStyle(style);
|
|
224
|
+
this.markDirty();
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
fontSize(size: f32): this {
|
|
229
|
+
this.nodeValue.fontSize(size);
|
|
230
|
+
this.markDirty();
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
lineHeight(px: f32): this {
|
|
235
|
+
this.nodeValue.lineHeight(px);
|
|
236
|
+
this.markDirty();
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
color(color: u32): this {
|
|
241
|
+
this.nodeValue.textColor(color);
|
|
242
|
+
this.markDirty();
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
textColor(color: u32): this {
|
|
247
|
+
return this.color(color);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
textAlign(align: TextAlign): this {
|
|
251
|
+
this.nodeValue.textAlign(align);
|
|
252
|
+
this.markDirty();
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
verticalAlign(align: TextVerticalAlign): this {
|
|
257
|
+
this.nodeValue.verticalAlign(align);
|
|
258
|
+
this.markDirty();
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
maxLines(lines: i32): this {
|
|
263
|
+
this.nodeValue.maxLines(lines);
|
|
264
|
+
this.markDirty();
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
textLimits(maxChars: i32, maxLines: i32): this {
|
|
269
|
+
this.nodeValue.textLimits(maxChars, maxLines);
|
|
270
|
+
this.markDirty();
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
wrap(flag: bool = true): this {
|
|
275
|
+
this.nodeValue.wrapping(flag);
|
|
276
|
+
this.markDirty();
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
wrapping(flag: bool = true): this {
|
|
281
|
+
return this.wrap(flag);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
overflow(mode: TextOverflow): this {
|
|
285
|
+
this.nodeValue.overflow(mode);
|
|
286
|
+
this.markDirty();
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private ensureBuilt(): void {
|
|
291
|
+
this.nodeValue.build();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** @internal */
|
|
295
|
+
_ensureBuiltForReady(): void {
|
|
296
|
+
this.ensureBuilt();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** @internal */
|
|
300
|
+
_requiredFontIdsForReady(): Array<u32> {
|
|
301
|
+
return this.nodeValue._requiredFontIds();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private markDirty(): void {
|
|
305
|
+
this.readyValue = false;
|
|
306
|
+
this.metricsValue = new TextMetrics();
|
|
307
|
+
}
|
|
308
|
+
}
|
package/src/worker/Worker.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
fui_worker_complete_string,
|
|
3
|
-
fui_worker_copy_input,
|
|
4
3
|
fui_worker_fail,
|
|
5
|
-
fui_worker_input_length,
|
|
6
4
|
fui_worker_is_cancelled,
|
|
7
5
|
fui_worker_request_yield,
|
|
8
6
|
fui_worker_request_yield_delay,
|
|
@@ -10,45 +8,21 @@ import {
|
|
|
10
8
|
} from "./ffi";
|
|
11
9
|
import { handleFetchComplete, handleFetchError } from "../core/Fetch";
|
|
12
10
|
|
|
13
|
-
let inputRead = false;
|
|
14
|
-
let inputCache = "";
|
|
15
11
|
let terminalSent = false;
|
|
16
|
-
let testInputOverride: string | null = null;
|
|
17
12
|
const WORKER_CALLBACK_BUFFER = new Uint8Array(1024 * 1024);
|
|
18
13
|
|
|
19
14
|
function encodeUtf8(text: string): Uint8Array {
|
|
20
15
|
return Uint8Array.wrap(String.UTF8.encode(text, false));
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
function readInputMessage(): string {
|
|
24
|
-
if (testInputOverride !== null) {
|
|
25
|
-
return changetype<string>(testInputOverride);
|
|
26
|
-
}
|
|
27
|
-
const byteLength = fui_worker_input_length();
|
|
28
|
-
if (byteLength == 0) {
|
|
29
|
-
return "";
|
|
30
|
-
}
|
|
31
|
-
const bytes = new Uint8Array(byteLength);
|
|
32
|
-
const copied = fui_worker_copy_input(bytes.dataStart, <u32>bytes.length);
|
|
33
|
-
if (copied == 0) {
|
|
34
|
-
return "";
|
|
35
|
-
}
|
|
36
|
-
return String.UTF8.decodeUnsafe(bytes.dataStart, <usize>copied, false);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
18
|
function sendText(text: string, callback: (ptr: usize, len: u32) => void): void {
|
|
40
19
|
const bytes = encodeUtf8(text);
|
|
41
20
|
callback(bytes.length > 0 ? bytes.dataStart : 0, <u32>bytes.length);
|
|
42
21
|
}
|
|
43
22
|
|
|
44
23
|
export class Worker {
|
|
45
|
-
static
|
|
46
|
-
|
|
47
|
-
return inputCache;
|
|
48
|
-
}
|
|
49
|
-
inputCache = readInputMessage();
|
|
50
|
-
inputRead = true;
|
|
51
|
-
return inputCache;
|
|
24
|
+
static entry(inputPtr: usize, inputLen: u32, handler: (input: string) => void): void {
|
|
25
|
+
handler(inputPtr == 0 || inputLen == 0 ? "" : String.UTF8.decodeUnsafe(inputPtr, <usize>inputLen, false));
|
|
52
26
|
}
|
|
53
27
|
|
|
54
28
|
static reportProgress(progress: string): void {
|
|
@@ -157,13 +131,6 @@ export function __fui_on_fetch_error(requestId: u32, payloadPtr: usize, payloadL
|
|
|
157
131
|
);
|
|
158
132
|
}
|
|
159
133
|
|
|
160
|
-
export function
|
|
161
|
-
inputRead = false;
|
|
162
|
-
inputCache = "";
|
|
134
|
+
export function resetWorkerRuntime(): void {
|
|
163
135
|
terminalSent = false;
|
|
164
|
-
testInputOverride = null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function __setWorkerInputForTests(value: string): void {
|
|
168
|
-
testInputOverride = value;
|
|
169
136
|
}
|
package/src/worker/WorkerJob.ts
CHANGED
|
@@ -4,8 +4,8 @@ export abstract class WorkerJob {
|
|
|
4
4
|
private started: bool = false;
|
|
5
5
|
private finished: bool = false;
|
|
6
6
|
|
|
7
|
-
static resume<T extends WorkerJob>(job: T): T | null {
|
|
8
|
-
job.ensureStarted();
|
|
7
|
+
static resume<T extends WorkerJob>(job: T, input: string = ""): T | null {
|
|
8
|
+
job.ensureStarted(input);
|
|
9
9
|
if (job.finished) {
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
@@ -13,14 +13,10 @@ export abstract class WorkerJob {
|
|
|
13
13
|
return job.finished ? null : job;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
protected onStart(): void {}
|
|
16
|
+
protected onStart(input: string): void {}
|
|
17
17
|
|
|
18
18
|
abstract run(): void;
|
|
19
19
|
|
|
20
|
-
protected receiveMessage(): string {
|
|
21
|
-
return Worker.receiveMessage();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
20
|
protected reportProgress(progress: string): void {
|
|
25
21
|
if (this.finished) {
|
|
26
22
|
return;
|
|
@@ -55,11 +51,11 @@ export abstract class WorkerJob {
|
|
|
55
51
|
return Worker.yield(delayMs);
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
private ensureStarted(): void {
|
|
54
|
+
private ensureStarted(input: string): void {
|
|
59
55
|
if (this.started) {
|
|
60
56
|
return;
|
|
61
57
|
}
|
|
62
58
|
this.started = true;
|
|
63
|
-
this.onStart();
|
|
59
|
+
this.onStart(input);
|
|
64
60
|
}
|
|
65
61
|
}
|
package/src/worker/ffi.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
@external("fui_worker_host", "fui_worker_input_length")
|
|
2
|
-
export declare function fui_worker_input_length(): u32;
|
|
3
|
-
|
|
4
|
-
@external("fui_worker_host", "fui_worker_copy_input")
|
|
5
|
-
export declare function fui_worker_copy_input(ptr: usize, capacity: u32): u32;
|
|
6
|
-
|
|
7
1
|
@external("fui_worker_host", "fui_worker_report_progress")
|
|
8
2
|
export declare function fui_worker_report_progress(ptr: usize, len: u32): void;
|
|
9
3
|
|