@effindomv2/fui-as 0.1.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 (137) hide show
  1. package/LICENSE.md +7 -0
  2. package/browser/src/common-harness/host-imports.ts +430 -0
  3. package/browser/src/common-harness/interop.ts +39 -0
  4. package/browser/src/common-harness/managed-harness-bitmap-host.ts +92 -0
  5. package/browser/src/common-harness/managed-harness-fetch-host.ts +201 -0
  6. package/browser/src/common-harness/managed-harness-file-host.ts +1101 -0
  7. package/browser/src/common-harness/managed-harness-file-payloads.ts +143 -0
  8. package/browser/src/common-harness/managed-harness-file-types.ts +106 -0
  9. package/browser/src/common-harness/managed-harness-session.ts +15 -0
  10. package/browser/src/common-harness/managed-harness.ts +1323 -0
  11. package/browser/src/common-harness/managed-history.ts +168 -0
  12. package/browser/src/common-harness/persisted-restore-policy.ts +50 -0
  13. package/browser/src/common-harness/persisted-ui-state-controller.ts +309 -0
  14. package/browser/src/common-harness/text-session-bridge.ts +452 -0
  15. package/browser/src/common-harness/types.ts +205 -0
  16. package/browser/src/common-harness/ui-chrome.ts +191 -0
  17. package/browser/src/common-harness/ui-imports.ts +529 -0
  18. package/browser/src/common-harness/wasm-module-cache.ts +47 -0
  19. package/browser/src/common-harness.ts +27 -0
  20. package/browser/src/file-processing-worker.ts +89 -0
  21. package/browser/src/host-events.ts +97 -0
  22. package/browser/src/host-services.ts +203 -0
  23. package/browser/src/index.ts +62 -0
  24. package/browser/src/persisted-ui-state.ts +206 -0
  25. package/browser/src/routed-harness.ts +198 -0
  26. package/browser/src/worker-bootstrap.ts +483 -0
  27. package/browser/src/worker-manager.ts +230 -0
  28. package/browser/src/worker-types.ts +50 -0
  29. package/package.json +89 -0
  30. package/scripts/build-demo-as.sh +91 -0
  31. package/scripts/build.sh +325 -0
  32. package/scripts/generate-host-events.ts +175 -0
  33. package/scripts/generate-host-services.ts +157 -0
  34. package/src/Fui.ts +205 -0
  35. package/src/FuiExports.ts +55 -0
  36. package/src/FuiPrimitives.ts +15 -0
  37. package/src/FuiWorker.ts +3 -0
  38. package/src/FuiWorkerExports.ts +6 -0
  39. package/src/bindings/ui.ts +531 -0
  40. package/src/color.ts +86 -0
  41. package/src/controls/AntiSelectionArea.ts +23 -0
  42. package/src/controls/Button.ts +750 -0
  43. package/src/controls/Checkbox.ts +181 -0
  44. package/src/controls/ContextMenu.ts +885 -0
  45. package/src/controls/ControlTemplateSet.ts +37 -0
  46. package/src/controls/Dialog.ts +355 -0
  47. package/src/controls/Dropdown.ts +856 -0
  48. package/src/controls/Form.ts +110 -0
  49. package/src/controls/NavLink.ts +211 -0
  50. package/src/controls/Popup.ts +129 -0
  51. package/src/controls/ProgressBar.ts +180 -0
  52. package/src/controls/RadioButton.ts +135 -0
  53. package/src/controls/RadioGroup.ts +244 -0
  54. package/src/controls/SelectionArea.ts +75 -0
  55. package/src/controls/Slider.ts +471 -0
  56. package/src/controls/Switch.ts +132 -0
  57. package/src/controls/TextArea.ts +20 -0
  58. package/src/controls/TextInput.ts +7 -0
  59. package/src/controls/index.ts +18 -0
  60. package/src/controls/internal/ButtonPresenter.ts +95 -0
  61. package/src/controls/internal/CheckboxIndicatorPresenter.ts +93 -0
  62. package/src/controls/internal/DropdownChevronPresenter.ts +67 -0
  63. package/src/controls/internal/DropdownFieldPresenter.ts +110 -0
  64. package/src/controls/internal/DropdownOptionRowPresenter.ts +82 -0
  65. package/src/controls/internal/PopupPresenter.ts +198 -0
  66. package/src/controls/internal/PressableIndicatorPresenter.ts +32 -0
  67. package/src/controls/internal/PressableLabeledControl.ts +221 -0
  68. package/src/controls/internal/RadioIndicatorPresenter.ts +73 -0
  69. package/src/controls/internal/SliderPresenter.ts +157 -0
  70. package/src/controls/internal/SwitchIndicatorPresenter.ts +72 -0
  71. package/src/controls/internal/TextInputCore.ts +695 -0
  72. package/src/controls/internal/TextInputPresenter.ts +72 -0
  73. package/src/controls/templating.ts +54 -0
  74. package/src/core/Action.ts +94 -0
  75. package/src/core/Actions.ts +37 -0
  76. package/src/core/Animation.ts +412 -0
  77. package/src/core/Application.ts +328 -0
  78. package/src/core/Assets.ts +264 -0
  79. package/src/core/AttachedProperties.ts +32 -0
  80. package/src/core/Bitmap.ts +70 -0
  81. package/src/core/BoundCallback.ts +104 -0
  82. package/src/core/Callbacks.ts +17 -0
  83. package/src/core/ContextMenuManager.ts +466 -0
  84. package/src/core/DebugApi.ts +30 -0
  85. package/src/core/Disposable.ts +10 -0
  86. package/src/core/DragDropManager.ts +179 -0
  87. package/src/core/DragGesture.ts +184 -0
  88. package/src/core/DynamicAssetIds.ts +24 -0
  89. package/src/core/Errors.ts +48 -0
  90. package/src/core/EventRouter.ts +408 -0
  91. package/src/core/ExternalDropManager.ts +122 -0
  92. package/src/core/Fetch.ts +264 -0
  93. package/src/core/FetchFfi.ts +15 -0
  94. package/src/core/File.ts +1002 -0
  95. package/src/core/FocusAdornerManager.ts +263 -0
  96. package/src/core/FocusVisibility.ts +36 -0
  97. package/src/core/FrameScheduler.ts +28 -0
  98. package/src/core/KeyboardScroll.ts +161 -0
  99. package/src/core/KeyboardScrollTracker.ts +386 -0
  100. package/src/core/Logger.ts +80 -0
  101. package/src/core/Navigation.ts +13 -0
  102. package/src/core/Node.ts +1708 -0
  103. package/src/core/PersistedState.ts +102 -0
  104. package/src/core/PersistedUiState.ts +142 -0
  105. package/src/core/Platform.ts +219 -0
  106. package/src/core/Signal.ts +89 -0
  107. package/src/core/Theme.ts +365 -0
  108. package/src/core/Timers.ts +129 -0
  109. package/src/core/ToolTip.ts +122 -0
  110. package/src/core/ToolTipManager.ts +459 -0
  111. package/src/core/Transitions.ts +34 -0
  112. package/src/core/Typography.ts +204 -0
  113. package/src/core/Worker.ts +196 -0
  114. package/src/core/bind.ts +37 -0
  115. package/src/core/event_exports.ts +596 -0
  116. package/src/core/ffi.ts +728 -0
  117. package/src/host-services/runtime.ts +25 -0
  118. package/src/nodes/FlexBox.ts +789 -0
  119. package/src/nodes/GradientStop.ts +9 -0
  120. package/src/nodes/Grid.ts +183 -0
  121. package/src/nodes/Image.ts +189 -0
  122. package/src/nodes/Portal.ts +14 -0
  123. package/src/nodes/RichText.ts +312 -0
  124. package/src/nodes/ScrollBar.ts +570 -0
  125. package/src/nodes/ScrollBox.ts +415 -0
  126. package/src/nodes/ScrollState.ts +10 -0
  127. package/src/nodes/ScrollView.ts +511 -0
  128. package/src/nodes/Svg.ts +142 -0
  129. package/src/nodes/Text.ts +145 -0
  130. package/src/nodes/TextCore.ts +558 -0
  131. package/src/nodes/VirtualList.ts +431 -0
  132. package/src/nodes/helpers.ts +25 -0
  133. package/src/nodes/index.ts +14 -0
  134. package/src/tsconfig.json +7 -0
  135. package/src/worker/Worker.ts +169 -0
  136. package/src/worker/WorkerJob.ts +65 -0
  137. package/src/worker/ffi.ts +23 -0
@@ -0,0 +1,104 @@
1
+ export abstract class Callback0 {
2
+ abstract invoke(): void;
3
+ }
4
+
5
+ export type Handler0<Owner> = (owner: Owner) => void;
6
+
7
+ export class BoundCallback0<Owner> extends Callback0 {
8
+ private readonly owner: Owner;
9
+ private readonly handler: Handler0<Owner>;
10
+
11
+ constructor(owner: Owner, handler: Handler0<Owner>) {
12
+ super();
13
+ this.owner = owner;
14
+ this.handler = handler;
15
+ }
16
+
17
+ invoke(): void {
18
+ this.handler(this.owner);
19
+ }
20
+ }
21
+
22
+ export abstract class Callback1<T> {
23
+ abstract invoke(value: T): void;
24
+ }
25
+
26
+ export type Handler1<Owner, T> = (owner: Owner, value: T) => void;
27
+
28
+ export class BoundCallback1<Owner, T> extends Callback1<T> {
29
+ private readonly owner: Owner;
30
+ private readonly handler: Handler1<Owner, T>;
31
+
32
+ constructor(owner: Owner, handler: Handler1<Owner, T>) {
33
+ super();
34
+ this.owner = owner;
35
+ this.handler = handler;
36
+ }
37
+
38
+ invoke(value: T): void {
39
+ this.handler(this.owner, value);
40
+ }
41
+ }
42
+
43
+ export abstract class Callback2<T1, T2> {
44
+ abstract invoke(value1: T1, value2: T2): void;
45
+ }
46
+
47
+ export type Handler2<Owner, T1, T2> = (owner: Owner, value1: T1, value2: T2) => void;
48
+
49
+ export class BoundCallback2<Owner, T1, T2> extends Callback2<T1, T2> {
50
+ private readonly owner: Owner;
51
+ private readonly handler: Handler2<Owner, T1, T2>;
52
+
53
+ constructor(owner: Owner, handler: Handler2<Owner, T1, T2>) {
54
+ super();
55
+ this.owner = owner;
56
+ this.handler = handler;
57
+ }
58
+
59
+ invoke(value1: T1, value2: T2): void {
60
+ this.handler(this.owner, value1, value2);
61
+ }
62
+ }
63
+
64
+ export abstract class ResultCallback0<R> {
65
+ abstract invoke(): R;
66
+ }
67
+
68
+ export type ResultHandler0<Owner, R> = (owner: Owner) => R;
69
+
70
+ export class BoundResultCallback0<Owner, R> extends ResultCallback0<R> {
71
+ private readonly owner: Owner;
72
+ private readonly handler: ResultHandler0<Owner, R>;
73
+
74
+ constructor(owner: Owner, handler: ResultHandler0<Owner, R>) {
75
+ super();
76
+ this.owner = owner;
77
+ this.handler = handler;
78
+ }
79
+
80
+ invoke(): R {
81
+ return this.handler(this.owner);
82
+ }
83
+ }
84
+
85
+ export abstract class ResultCallback1<T, R> {
86
+ abstract invoke(value: T): R;
87
+ }
88
+
89
+ export type ResultHandler1<Owner, T, R> = (owner: Owner, value: T) => R;
90
+
91
+ export class BoundResultCallback1<Owner, T, R> extends ResultCallback1<T, R> {
92
+ private readonly owner: Owner;
93
+ private readonly handler: ResultHandler1<Owner, T, R>;
94
+
95
+ constructor(owner: Owner, handler: ResultHandler1<Owner, T, R>) {
96
+ super();
97
+ this.owner = owner;
98
+ this.handler = handler;
99
+ }
100
+
101
+ invoke(value: T): R {
102
+ return this.handler(this.owner, value);
103
+ }
104
+ }
@@ -0,0 +1,17 @@
1
+ export {
2
+ BoundCallback0,
3
+ BoundCallback1,
4
+ BoundCallback2,
5
+ BoundResultCallback0,
6
+ BoundResultCallback1,
7
+ Callback0,
8
+ Callback1,
9
+ Callback2,
10
+ Handler0,
11
+ Handler1,
12
+ Handler2,
13
+ ResultCallback0,
14
+ ResultCallback1,
15
+ ResultHandler0,
16
+ ResultHandler1,
17
+ } from "./BoundCallback";
@@ -0,0 +1,466 @@
1
+ import * as ffi from "./ffi";
2
+ import * as ui from "../bindings/ui";
3
+ import { ContextMenu, ContextMenuAction, MenuItem } from "../controls/ContextMenu";
4
+ import { NavLink } from "../controls/NavLink";
5
+ import { TextInputCore } from "../controls/internal/TextInputCore";
6
+ import { EventRouter } from "./EventRouter";
7
+ import { HandleValue, KeyModifier, PointerEventType } from "./ffi";
8
+ import {
9
+ formatPrimaryShortcutLabel,
10
+ formatRedoShortcutLabel,
11
+ formatShortcutLabel,
12
+ formatUndoShortcutLabel,
13
+ getPlatformFamily,
14
+ PlatformFamily,
15
+ } from "./Platform";
16
+ import { Node } from "./Node";
17
+ import { Image } from "../nodes/Image";
18
+ import { Svg } from "../nodes/Svg";
19
+ import { TextCore } from "../nodes/TextCore";
20
+
21
+ function handleMenuVisibilityChanged(visible: bool): void {
22
+ ContextMenuManager.handleMenuVisibilityChanged(visible);
23
+ }
24
+
25
+ function appendMenuSection(items: Array<MenuItem>, section: Array<MenuItem>): void {
26
+ if (section.length == 0) {
27
+ return;
28
+ }
29
+ if (items.length > 0) {
30
+ items.push(MenuItem.separator());
31
+ }
32
+ for (let index = 0; index < section.length; ++index) {
33
+ items.push(unchecked(section[index]));
34
+ }
35
+ }
36
+
37
+ function resolveHistoryShortcutLabel(forward: bool): string {
38
+ const platform = getPlatformFamily();
39
+ if (platform == PlatformFamily.Apple) {
40
+ return formatShortcutLabel(forward ? "]" : "[", KeyModifier.Meta, platform);
41
+ }
42
+ return formatShortcutLabel(forward ? "ArrowRight" : "ArrowLeft", KeyModifier.Alt, platform);
43
+ }
44
+
45
+ export class ContextMenuManager {
46
+ private static menu: ContextMenu | null = null;
47
+ private static defaultMenu: ContextMenu | null = null;
48
+ private static readonly activePointerSelectionHandles: Array<u64> = new Array<u64>();
49
+ private static readonly currentSelectionHandleHints: Array<u64> = new Array<u64>();
50
+ private static currentSelectionText: string = "";
51
+ private static activeMenuLink: NavLink | null = null;
52
+
53
+ static createDefaultMenu(): ContextMenu {
54
+ const menu = new ContextMenu()
55
+ .onVisibilityChanged(handleMenuVisibilityChanged) as ContextMenu;
56
+ this.defaultMenu = menu;
57
+ return menu;
58
+ }
59
+
60
+ static setMenu(menu: ContextMenu | null): void {
61
+ this.releaseActiveMenuLinkPreview();
62
+ const previousMenu = this.menu;
63
+ if (previousMenu !== null) {
64
+ previousMenu.onVisibilityChanged(null);
65
+ }
66
+ this.menu = menu;
67
+ if (menu !== null) {
68
+ menu.onVisibilityChanged(handleMenuVisibilityChanged);
69
+ }
70
+ }
71
+
72
+ static trackPointerEvent(eventType: PointerEventType, handle: u64): void {
73
+ if (eventType == PointerEventType.Down) {
74
+ this.activePointerSelectionHandles.length = 0;
75
+ }
76
+ if (handle != <u64>HandleValue.Invalid) {
77
+ this.pushUniqueHandle(this.activePointerSelectionHandles, handle);
78
+ }
79
+ }
80
+
81
+ static handleSelectionChanged(text: string): void {
82
+ this.currentSelectionText = text;
83
+ if (text.length == 0) {
84
+ this.currentSelectionHandleHints.length = 0;
85
+ return;
86
+ }
87
+
88
+ this.currentSelectionHandleHints.length = 0;
89
+ for (let index = 0; index < this.activePointerSelectionHandles.length; ++index) {
90
+ this.currentSelectionHandleHints.push(unchecked(this.activePointerSelectionHandles[index]));
91
+ }
92
+ }
93
+
94
+ static showForCurrentSelection(handle: u64, x: f32, y: f32): void {
95
+ const menu = this.resolveActiveMenu();
96
+ if (menu === null) {
97
+ return;
98
+ }
99
+
100
+ this.releaseActiveMenuLinkPreview();
101
+ const targetNode = EventRouter.getRegisteredNode(handle);
102
+ let contextNode = targetNode;
103
+ while (contextNode !== null) {
104
+ if (contextNode.isContextMenuDisabled) {
105
+ return;
106
+ }
107
+ const handler = contextNode.contextMenuHandler;
108
+ if (handler !== null) {
109
+ handler(targetNode, x, y);
110
+ return;
111
+ }
112
+ contextNode = contextNode.parentNode;
113
+ }
114
+
115
+ const items = new Array<MenuItem>();
116
+ const link = this.resolveNavLink(targetNode);
117
+ if (link !== null) {
118
+ link.pinPreviewForContextMenu();
119
+ this.activeMenuLink = link;
120
+ const linkItems = new Array<MenuItem>();
121
+ linkItems.push(new MenuItem("Open Link in New Tab", ContextMenuAction.OpenLinkInNewTab, link.href));
122
+ linkItems.push(new MenuItem("Open Link", ContextMenuAction.OpenLink, link.href));
123
+ appendMenuSection(items, linkItems);
124
+ }
125
+
126
+ const textTarget = this.resolveTextTarget(targetNode);
127
+ const selectionHit = ui.isPointInSelection(x, y) || this.selectionHintContainsHandle(handle);
128
+ if (textTarget !== null) {
129
+ appendMenuSection(items, this.buildTextSection(textTarget, selectionHit ? this.currentSelectionText : ""));
130
+ }
131
+
132
+ const imageUrl = this.resolveImageUrl(targetNode, x, y);
133
+ if (imageUrl !== null && imageUrl.length > 0) {
134
+ const imageItems = new Array<MenuItem>();
135
+ imageItems.push(new MenuItem("Open Image in New Tab", ContextMenuAction.OpenImageInNewTab, imageUrl));
136
+ imageItems.push(new MenuItem("Open Image", ContextMenuAction.OpenImage, imageUrl));
137
+ appendMenuSection(items, imageItems);
138
+ }
139
+
140
+ if (selectionHit && textTarget === null && this.currentSelectionText.length > 0) {
141
+ const selectionItems = new Array<MenuItem>();
142
+ selectionItems.push(new MenuItem("Copy", ContextMenuAction.CopyCurrentSelection));
143
+ appendMenuSection(items, selectionItems);
144
+ } else if (!selectionHit && textTarget === null && link === null && imageUrl === null) {
145
+ this.currentSelectionText = "";
146
+ this.currentSelectionHandleHints.length = 0;
147
+ ui.clearCurrentSelection();
148
+ }
149
+
150
+ const navigationItems = new Array<MenuItem>();
151
+ if (ffi.fui_can_navigate_back()) {
152
+ navigationItems.push(new MenuItem("Back", ContextMenuAction.NavigateBack, null, 0, resolveHistoryShortcutLabel(false)));
153
+ }
154
+ if (ffi.fui_can_navigate_forward()) {
155
+ navigationItems.push(new MenuItem("Forward", ContextMenuAction.NavigateForward, null, 0, resolveHistoryShortcutLabel(true)));
156
+ }
157
+ navigationItems.push(
158
+ new MenuItem("Reload Page", ContextMenuAction.ReloadPage, null, 0, formatPrimaryShortcutLabel("r")),
159
+ );
160
+ appendMenuSection(items, navigationItems);
161
+ menu.show(items, x, y);
162
+ }
163
+
164
+ static hideActiveMenu(): void {
165
+ ContextMenu.hideActiveMenu();
166
+ }
167
+
168
+ static handleMenuVisibilityChanged(visible: bool): void {
169
+ if (!visible) {
170
+ this.releaseActiveMenuLinkPreview();
171
+ }
172
+ }
173
+
174
+ private static resolveActiveMenu(): ContextMenu | null {
175
+ if (this.menu !== null) {
176
+ return this.menu;
177
+ }
178
+ return this.defaultMenu;
179
+ }
180
+
181
+ private static resolveNavLink(node: Node | null): NavLink | null {
182
+ let current = node;
183
+ while (current !== null) {
184
+ if (current instanceof NavLink) {
185
+ return changetype<NavLink>(current);
186
+ }
187
+ current = current.parentNode;
188
+ }
189
+ return null;
190
+ }
191
+
192
+ private static resolveTextTarget(node: Node | null): TextCore | null {
193
+ let current = node;
194
+ while (current !== null) {
195
+ if (current instanceof TextInputCore) {
196
+ return changetype<TextInputCore>(current).editorNode;
197
+ }
198
+ if (current instanceof TextCore) {
199
+ return changetype<TextCore>(current);
200
+ }
201
+ current = current.parentNode;
202
+ }
203
+ return null;
204
+ }
205
+
206
+ private static resolveImageUrl(node: Node | null, pointX: f32, pointY: f32): string | null {
207
+ let current = node;
208
+ while (current !== null) {
209
+ const currentUrl = this.resolveDirectImageUrl(current);
210
+ if (currentUrl !== null && currentUrl.length > 0) {
211
+ return currentUrl;
212
+ }
213
+ current = current.parentNode;
214
+ }
215
+ if (node !== null) {
216
+ return this.resolveDescendantImageUrl(node, pointX, pointY);
217
+ }
218
+ return null;
219
+ }
220
+
221
+ private static buildTextSection(target: TextCore, currentSelectionText: string = ""): Array<MenuItem> {
222
+ const items = new Array<MenuItem>();
223
+ const handle = target.builtHandle;
224
+ if (handle == <u64>HandleValue.Invalid || !target.isSelectableText) {
225
+ return items;
226
+ }
227
+
228
+ const selectionStart = this.resolveSelectionStart(target);
229
+ const selectionEnd = this.resolveSelectionEnd(target);
230
+ const content = this.resolveTextContent(target);
231
+ const hasSelection = ui.hasTextSelection(handle) || ffi.fui_has_text_selection_snapshot(handle) || selectionStart != selectionEnd;
232
+ const hasText = content.length > 0;
233
+ const selectedText = hasSelection ? this.resolveSelectedText(target) : "";
234
+ const selectedPayload = selectedText.length > 0 ? selectedText : null;
235
+ if (target.isEditableText) {
236
+ ffi.fui_freeze_text_selection_snapshot(handle);
237
+ items.push(
238
+ new MenuItem(
239
+ "Undo",
240
+ ContextMenuAction.UndoTextEdit,
241
+ null,
242
+ 0,
243
+ formatUndoShortcutLabel(),
244
+ !ui.canUndoTextEdit(handle),
245
+ handle,
246
+ ),
247
+ );
248
+ items.push(
249
+ new MenuItem(
250
+ "Redo",
251
+ ContextMenuAction.RedoTextEdit,
252
+ null,
253
+ 0,
254
+ formatRedoShortcutLabel(),
255
+ !ui.canRedoTextEdit(handle),
256
+ handle,
257
+ ),
258
+ );
259
+ items.push(MenuItem.separator());
260
+ items.push(
261
+ new MenuItem(
262
+ "Cut",
263
+ ContextMenuAction.CutTextSelection,
264
+ selectedPayload,
265
+ 0,
266
+ formatPrimaryShortcutLabel("x"),
267
+ !hasSelection,
268
+ handle,
269
+ ).withSelectionRange(selectionStart, selectionEnd),
270
+ );
271
+ items.push(
272
+ new MenuItem(
273
+ "Copy",
274
+ ContextMenuAction.CopyCurrentSelection,
275
+ selectedPayload,
276
+ 0,
277
+ formatPrimaryShortcutLabel("c"),
278
+ !hasSelection,
279
+ handle,
280
+ ),
281
+ );
282
+ items.push(
283
+ new MenuItem(
284
+ "Paste",
285
+ ContextMenuAction.PasteText,
286
+ null,
287
+ 0,
288
+ formatPrimaryShortcutLabel("v"),
289
+ false,
290
+ handle,
291
+ ),
292
+ );
293
+ items.push(
294
+ new MenuItem(
295
+ "Select All",
296
+ ContextMenuAction.SelectAllText,
297
+ null,
298
+ 0,
299
+ formatPrimaryShortcutLabel("a"),
300
+ !hasText,
301
+ handle,
302
+ ),
303
+ );
304
+ return items;
305
+ }
306
+
307
+ if (currentSelectionText.length > 0) {
308
+ items.push(
309
+ new MenuItem(
310
+ "Copy",
311
+ ContextMenuAction.CopyCurrentSelection,
312
+ null,
313
+ 0,
314
+ formatPrimaryShortcutLabel("c"),
315
+ ),
316
+ );
317
+ } else {
318
+ items.push(
319
+ new MenuItem(
320
+ "Copy",
321
+ ContextMenuAction.CopyCurrentSelection,
322
+ null,
323
+ 0,
324
+ formatPrimaryShortcutLabel("c"),
325
+ !hasSelection,
326
+ handle,
327
+ ),
328
+ );
329
+ }
330
+ items.push(
331
+ new MenuItem(
332
+ "Select All",
333
+ ContextMenuAction.SelectAllText,
334
+ null,
335
+ 0,
336
+ formatPrimaryShortcutLabel("a"),
337
+ !hasText,
338
+ handle,
339
+ ),
340
+ );
341
+ return items;
342
+ }
343
+
344
+ private static resolveSelectedText(target: TextCore): string {
345
+ const selectionStart = this.resolveSelectionStart(target);
346
+ const selectionEnd = this.resolveSelectionEnd(target);
347
+ if (selectionStart == selectionEnd) {
348
+ return "";
349
+ }
350
+ const start = selectionStart < selectionEnd ? selectionStart : selectionEnd;
351
+ const end = selectionStart < selectionEnd ? selectionEnd : selectionStart;
352
+ return this.resolveTextContent(target).substring(start, end);
353
+ }
354
+
355
+ private static resolveSelectionStart(target: TextCore): u32 {
356
+ const parent = target.parentNode;
357
+ if (parent instanceof TextInputCore) {
358
+ return changetype<TextInputCore>(parent).selectionStart;
359
+ }
360
+ return target.selectionStart;
361
+ }
362
+
363
+ private static resolveSelectionEnd(target: TextCore): u32 {
364
+ const parent = target.parentNode;
365
+ if (parent instanceof TextInputCore) {
366
+ return changetype<TextInputCore>(parent).selectionEnd;
367
+ }
368
+ return target.selectionEnd;
369
+ }
370
+
371
+ private static resolveTextContent(target: TextCore): string {
372
+ const parent = target.parentNode;
373
+ if (parent instanceof TextInputCore) {
374
+ return changetype<TextInputCore>(parent).value;
375
+ }
376
+ return target.content;
377
+ }
378
+
379
+ private static resolveDirectImageUrl(node: Node): string | null {
380
+ if (node instanceof Image) {
381
+ const url = changetype<Image>(node).assetUrl();
382
+ return this.isBrowsableImageUrl(url) ? url : null;
383
+ }
384
+ if (node instanceof Svg) {
385
+ const url = changetype<Svg>(node).assetUrl();
386
+ return this.isBrowsableImageUrl(url) ? url : null;
387
+ }
388
+ return null;
389
+ }
390
+
391
+ private static resolveDescendantImageUrl(node: Node, pointX: f32, pointY: f32): string | null {
392
+ for (let index = 0; index < node.childCount; ++index) {
393
+ const child = node.getChildAt(index);
394
+ if (child === null) {
395
+ continue;
396
+ }
397
+ const childNode = changetype<Node>(child);
398
+ const childBounds = childNode.builtHandle != <u64>HandleValue.Invalid
399
+ ? ui.tryGetBounds(childNode.builtHandle)
400
+ : null;
401
+ if (childBounds === null || !this.boundsContainPoint(changetype<Float32Array>(childBounds), pointX, pointY)) {
402
+ continue;
403
+ }
404
+ const directUrl = this.resolveDirectImageUrl(childNode);
405
+ if (directUrl !== null && directUrl.length > 0) {
406
+ return directUrl;
407
+ }
408
+ const descendantUrl = this.resolveDescendantImageUrl(childNode, pointX, pointY);
409
+ if (descendantUrl !== null && descendantUrl.length > 0) {
410
+ return descendantUrl;
411
+ }
412
+ }
413
+ return null;
414
+ }
415
+
416
+ private static boundsContainPoint(bounds: Float32Array, pointX: f32, pointY: f32): bool {
417
+ const x = unchecked(bounds[0]);
418
+ const y = unchecked(bounds[1]);
419
+ const width = unchecked(bounds[2]);
420
+ const height = unchecked(bounds[3]);
421
+ return pointX >= x && pointX <= (x + width) && pointY >= y && pointY <= (y + height);
422
+ }
423
+
424
+ private static selectionHintContainsHandle(handle: u64): bool {
425
+ if (handle == <u64>HandleValue.Invalid) {
426
+ return false;
427
+ }
428
+ for (let index = 0; index < this.currentSelectionHandleHints.length; ++index) {
429
+ if (unchecked(this.currentSelectionHandleHints[index]) == handle) {
430
+ return true;
431
+ }
432
+ }
433
+ return false;
434
+ }
435
+
436
+ private static pushUniqueHandle(handles: Array<u64>, handle: u64): void {
437
+ for (let index = 0; index < handles.length; ++index) {
438
+ if (unchecked(handles[index]) == handle) {
439
+ return;
440
+ }
441
+ }
442
+ handles.push(handle);
443
+ }
444
+
445
+ private static releaseActiveMenuLinkPreview(): void {
446
+ const link = this.activeMenuLink;
447
+ if (link === null) {
448
+ return;
449
+ }
450
+ link.releasePreviewForContextMenu();
451
+ this.activeMenuLink = null;
452
+ }
453
+
454
+ private static isBrowsableImageUrl(url: string): bool {
455
+ if (url.length == 0) {
456
+ return false;
457
+ }
458
+ if (url.length >= 5 && url.substring(0, 5) == "data:") {
459
+ return false;
460
+ }
461
+ if (url.length >= 5 && url.substring(0, 5) == "blob:") {
462
+ return false;
463
+ }
464
+ return true;
465
+ }
466
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ __fui_on_focus_changed,
3
+ __fui_on_key_event,
4
+ __fui_on_pointer_event,
5
+ __fui_on_scroll,
6
+ } from "./event_exports";
7
+
8
+ export function __fui_debug_pointer_event(eventType: u32, handle: u64, x: f32, y: f32, modifiers: u32 = 0): void {
9
+ __fui_on_pointer_event(eventType, handle, x, y, modifiers);
10
+ }
11
+
12
+ export function __fui_debug_focus_changed(handle: u64, focused: bool): void {
13
+ __fui_on_focus_changed(handle, focused);
14
+ }
15
+
16
+ export function __fui_debug_key_event(eventType: u32, keyPtr: usize, keyLen: u32, modifiers: u32): void {
17
+ __fui_on_key_event(eventType, keyPtr, keyLen, modifiers);
18
+ }
19
+
20
+ export function __fui_debug_scroll(
21
+ handle: u64,
22
+ offsetX: f32,
23
+ offsetY: f32,
24
+ contentWidth: f32,
25
+ contentHeight: f32,
26
+ viewportWidth: f32,
27
+ viewportHeight: f32,
28
+ ): void {
29
+ __fui_on_scroll(handle, offsetX, offsetY, contentWidth, contentHeight, viewportWidth, viewportHeight);
30
+ }
@@ -0,0 +1,10 @@
1
+ export interface Disposable {
2
+ dispose(): void;
3
+ }
4
+
5
+ export function disposeAll(disposables: Array<Disposable>): void {
6
+ for (let index = disposables.length - 1; index >= 0; --index) {
7
+ unchecked(disposables[index]).dispose();
8
+ }
9
+ disposables.length = 0;
10
+ }