@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.
- package/LICENSE.md +7 -0
- package/browser/src/common-harness/host-imports.ts +430 -0
- package/browser/src/common-harness/interop.ts +39 -0
- package/browser/src/common-harness/managed-harness-bitmap-host.ts +92 -0
- package/browser/src/common-harness/managed-harness-fetch-host.ts +201 -0
- package/browser/src/common-harness/managed-harness-file-host.ts +1101 -0
- package/browser/src/common-harness/managed-harness-file-payloads.ts +143 -0
- package/browser/src/common-harness/managed-harness-file-types.ts +106 -0
- package/browser/src/common-harness/managed-harness-session.ts +15 -0
- package/browser/src/common-harness/managed-harness.ts +1323 -0
- package/browser/src/common-harness/managed-history.ts +168 -0
- package/browser/src/common-harness/persisted-restore-policy.ts +50 -0
- package/browser/src/common-harness/persisted-ui-state-controller.ts +309 -0
- package/browser/src/common-harness/text-session-bridge.ts +452 -0
- package/browser/src/common-harness/types.ts +205 -0
- package/browser/src/common-harness/ui-chrome.ts +191 -0
- package/browser/src/common-harness/ui-imports.ts +529 -0
- package/browser/src/common-harness/wasm-module-cache.ts +47 -0
- package/browser/src/common-harness.ts +27 -0
- package/browser/src/file-processing-worker.ts +89 -0
- package/browser/src/host-events.ts +97 -0
- package/browser/src/host-services.ts +203 -0
- package/browser/src/index.ts +62 -0
- package/browser/src/persisted-ui-state.ts +206 -0
- package/browser/src/routed-harness.ts +198 -0
- package/browser/src/worker-bootstrap.ts +483 -0
- package/browser/src/worker-manager.ts +230 -0
- package/browser/src/worker-types.ts +50 -0
- package/package.json +89 -0
- package/scripts/build-demo-as.sh +91 -0
- package/scripts/build.sh +325 -0
- package/scripts/generate-host-events.ts +175 -0
- package/scripts/generate-host-services.ts +157 -0
- package/src/Fui.ts +205 -0
- package/src/FuiExports.ts +55 -0
- package/src/FuiPrimitives.ts +15 -0
- package/src/FuiWorker.ts +3 -0
- package/src/FuiWorkerExports.ts +6 -0
- package/src/bindings/ui.ts +531 -0
- package/src/color.ts +86 -0
- package/src/controls/AntiSelectionArea.ts +23 -0
- package/src/controls/Button.ts +750 -0
- package/src/controls/Checkbox.ts +181 -0
- package/src/controls/ContextMenu.ts +885 -0
- package/src/controls/ControlTemplateSet.ts +37 -0
- package/src/controls/Dialog.ts +355 -0
- package/src/controls/Dropdown.ts +856 -0
- package/src/controls/Form.ts +110 -0
- package/src/controls/NavLink.ts +211 -0
- package/src/controls/Popup.ts +129 -0
- package/src/controls/ProgressBar.ts +180 -0
- package/src/controls/RadioButton.ts +135 -0
- package/src/controls/RadioGroup.ts +244 -0
- package/src/controls/SelectionArea.ts +75 -0
- package/src/controls/Slider.ts +471 -0
- package/src/controls/Switch.ts +132 -0
- package/src/controls/TextArea.ts +20 -0
- package/src/controls/TextInput.ts +7 -0
- package/src/controls/index.ts +18 -0
- package/src/controls/internal/ButtonPresenter.ts +95 -0
- package/src/controls/internal/CheckboxIndicatorPresenter.ts +93 -0
- package/src/controls/internal/DropdownChevronPresenter.ts +67 -0
- package/src/controls/internal/DropdownFieldPresenter.ts +110 -0
- package/src/controls/internal/DropdownOptionRowPresenter.ts +82 -0
- package/src/controls/internal/PopupPresenter.ts +198 -0
- package/src/controls/internal/PressableIndicatorPresenter.ts +32 -0
- package/src/controls/internal/PressableLabeledControl.ts +221 -0
- package/src/controls/internal/RadioIndicatorPresenter.ts +73 -0
- package/src/controls/internal/SliderPresenter.ts +157 -0
- package/src/controls/internal/SwitchIndicatorPresenter.ts +72 -0
- package/src/controls/internal/TextInputCore.ts +695 -0
- package/src/controls/internal/TextInputPresenter.ts +72 -0
- package/src/controls/templating.ts +54 -0
- package/src/core/Action.ts +94 -0
- package/src/core/Actions.ts +37 -0
- package/src/core/Animation.ts +412 -0
- package/src/core/Application.ts +328 -0
- package/src/core/Assets.ts +264 -0
- package/src/core/AttachedProperties.ts +32 -0
- package/src/core/Bitmap.ts +70 -0
- package/src/core/BoundCallback.ts +104 -0
- package/src/core/Callbacks.ts +17 -0
- package/src/core/ContextMenuManager.ts +466 -0
- package/src/core/DebugApi.ts +30 -0
- package/src/core/Disposable.ts +10 -0
- package/src/core/DragDropManager.ts +179 -0
- package/src/core/DragGesture.ts +184 -0
- package/src/core/DynamicAssetIds.ts +24 -0
- package/src/core/Errors.ts +48 -0
- package/src/core/EventRouter.ts +408 -0
- package/src/core/ExternalDropManager.ts +122 -0
- package/src/core/Fetch.ts +264 -0
- package/src/core/FetchFfi.ts +15 -0
- package/src/core/File.ts +1002 -0
- package/src/core/FocusAdornerManager.ts +263 -0
- package/src/core/FocusVisibility.ts +36 -0
- package/src/core/FrameScheduler.ts +28 -0
- package/src/core/KeyboardScroll.ts +161 -0
- package/src/core/KeyboardScrollTracker.ts +386 -0
- package/src/core/Logger.ts +80 -0
- package/src/core/Navigation.ts +13 -0
- package/src/core/Node.ts +1708 -0
- package/src/core/PersistedState.ts +102 -0
- package/src/core/PersistedUiState.ts +142 -0
- package/src/core/Platform.ts +219 -0
- package/src/core/Signal.ts +89 -0
- package/src/core/Theme.ts +365 -0
- package/src/core/Timers.ts +129 -0
- package/src/core/ToolTip.ts +122 -0
- package/src/core/ToolTipManager.ts +459 -0
- package/src/core/Transitions.ts +34 -0
- package/src/core/Typography.ts +204 -0
- package/src/core/Worker.ts +196 -0
- package/src/core/bind.ts +37 -0
- package/src/core/event_exports.ts +596 -0
- package/src/core/ffi.ts +728 -0
- package/src/host-services/runtime.ts +25 -0
- package/src/nodes/FlexBox.ts +789 -0
- package/src/nodes/GradientStop.ts +9 -0
- package/src/nodes/Grid.ts +183 -0
- package/src/nodes/Image.ts +189 -0
- package/src/nodes/Portal.ts +14 -0
- package/src/nodes/RichText.ts +312 -0
- package/src/nodes/ScrollBar.ts +570 -0
- package/src/nodes/ScrollBox.ts +415 -0
- package/src/nodes/ScrollState.ts +10 -0
- package/src/nodes/ScrollView.ts +511 -0
- package/src/nodes/Svg.ts +142 -0
- package/src/nodes/Text.ts +145 -0
- package/src/nodes/TextCore.ts +558 -0
- package/src/nodes/VirtualList.ts +431 -0
- package/src/nodes/helpers.ts +25 -0
- package/src/nodes/index.ts +14 -0
- package/src/tsconfig.json +7 -0
- package/src/worker/Worker.ts +169 -0
- package/src/worker/WorkerJob.ts +65 -0
- package/src/worker/ffi.ts +23 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import * as ui from "../bindings/ui";
|
|
2
|
+
import { ScrollView } from "../nodes";
|
|
3
|
+
import { Node } from "./Node";
|
|
4
|
+
|
|
5
|
+
const BOUNDS_TOLERANCE: f32 = 0.5;
|
|
6
|
+
|
|
7
|
+
const scrollViews: Array<ScrollView> = new Array<ScrollView>();
|
|
8
|
+
let selectedScrollView: ScrollView | null = null;
|
|
9
|
+
let selectedBranchRoot: Node | null = null;
|
|
10
|
+
|
|
11
|
+
function indexOfScrollView(target: ScrollView): i32 {
|
|
12
|
+
for (let index = 0; index < scrollViews.length; ++index) {
|
|
13
|
+
if (unchecked(scrollViews[index]) === target) {
|
|
14
|
+
return index;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return -1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isUsableBounds(bounds: Float32Array | null): bool {
|
|
21
|
+
return bounds !== null && unchecked(bounds[2]) > BOUNDS_TOLERANCE && unchecked(bounds[3]) > BOUNDS_TOLERANCE;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isVisible(bounds: Float32Array): bool {
|
|
25
|
+
const x = unchecked(bounds[0]);
|
|
26
|
+
const y = unchecked(bounds[1]);
|
|
27
|
+
const width = unchecked(bounds[2]);
|
|
28
|
+
const height = unchecked(bounds[3]);
|
|
29
|
+
const viewportWidth = ui.getViewportWidth();
|
|
30
|
+
const viewportHeight = ui.getViewportHeight();
|
|
31
|
+
return x + width > 0.0 &&
|
|
32
|
+
y + height > 0.0 &&
|
|
33
|
+
x < viewportWidth &&
|
|
34
|
+
y < viewportHeight;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function containsPoint(bounds: Float32Array, x: f32, y: f32): bool {
|
|
38
|
+
return x >= unchecked(bounds[0]) &&
|
|
39
|
+
x <= unchecked(bounds[0]) + unchecked(bounds[2]) &&
|
|
40
|
+
y >= unchecked(bounds[1]) &&
|
|
41
|
+
y <= unchecked(bounds[1]) + unchecked(bounds[3]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function area(bounds: Float32Array): f32 {
|
|
45
|
+
return unchecked(bounds[2]) * unchecked(bounds[3]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function distanceSquared(bounds: Float32Array, x: f32, y: f32): f32 {
|
|
49
|
+
let deltaX: f32 = 0.0;
|
|
50
|
+
if (x < unchecked(bounds[0])) {
|
|
51
|
+
deltaX = unchecked(bounds[0]) - x;
|
|
52
|
+
} else {
|
|
53
|
+
const right = unchecked(bounds[0]) + unchecked(bounds[2]);
|
|
54
|
+
if (x > right) {
|
|
55
|
+
deltaX = x - right;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let deltaY: f32 = 0.0;
|
|
60
|
+
if (y < unchecked(bounds[1])) {
|
|
61
|
+
deltaY = unchecked(bounds[1]) - y;
|
|
62
|
+
} else {
|
|
63
|
+
const bottom = unchecked(bounds[1]) + unchecked(bounds[3]);
|
|
64
|
+
if (y > bottom) {
|
|
65
|
+
deltaY = y - bottom;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (deltaX * deltaX) + (deltaY * deltaY);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isBetterPointCandidate(
|
|
73
|
+
candidateBounds: Float32Array,
|
|
74
|
+
candidateContainsPoint: bool,
|
|
75
|
+
bestBounds: Float32Array,
|
|
76
|
+
bestContainsPoint: bool,
|
|
77
|
+
pointX: f32,
|
|
78
|
+
pointY: f32,
|
|
79
|
+
): bool {
|
|
80
|
+
if (candidateContainsPoint != bestContainsPoint) {
|
|
81
|
+
return candidateContainsPoint;
|
|
82
|
+
}
|
|
83
|
+
if (candidateContainsPoint) {
|
|
84
|
+
const candidateArea = area(candidateBounds);
|
|
85
|
+
const bestArea = area(bestBounds);
|
|
86
|
+
if (candidateArea + BOUNDS_TOLERANCE < bestArea) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (bestArea + BOUNDS_TOLERANCE < candidateArea) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
const candidateDistance = distanceSquared(candidateBounds, pointX, pointY);
|
|
94
|
+
const bestDistance = distanceSquared(bestBounds, pointX, pointY);
|
|
95
|
+
if (candidateDistance + BOUNDS_TOLERANCE < bestDistance) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (bestDistance + BOUNDS_TOLERANCE < candidateDistance) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (unchecked(candidateBounds[1]) + BOUNDS_TOLERANCE < unchecked(bestBounds[1])) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (unchecked(bestBounds[1]) + BOUNDS_TOLERANCE < unchecked(candidateBounds[1])) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return unchecked(candidateBounds[0]) < unchecked(bestBounds[0]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isBetterDefaultCandidate(candidateBounds: Float32Array, bestBounds: Float32Array): bool {
|
|
112
|
+
if (unchecked(candidateBounds[1]) + BOUNDS_TOLERANCE < unchecked(bestBounds[1])) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (unchecked(bestBounds[1]) + BOUNDS_TOLERANCE < unchecked(candidateBounds[1])) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return unchecked(candidateBounds[0]) < unchecked(bestBounds[0]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getUsableVisibleBounds(node: Node): Float32Array | null {
|
|
122
|
+
const bounds = ui.tryGetBounds(node.builtHandle);
|
|
123
|
+
if (!isUsableBounds(bounds)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const rect = changetype<Float32Array>(bounds);
|
|
127
|
+
if (!isVisible(rect)) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return rect;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getDefaultOrderingAnchor(scrollView: ScrollView): Node {
|
|
134
|
+
let anchor: Node = scrollView;
|
|
135
|
+
let cursor = scrollView.parentNode;
|
|
136
|
+
while (cursor !== null) {
|
|
137
|
+
if (cursor instanceof ScrollView) {
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
anchor = changetype<Node>(cursor);
|
|
141
|
+
cursor = cursor.parentNode;
|
|
142
|
+
}
|
|
143
|
+
return anchor;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function appendUniqueScrollView(target: Array<ScrollView>, view: ScrollView): void {
|
|
147
|
+
if (indexOfScrollView(view) < 0 || getUsableVisibleBounds(view) === null) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
for (let index = 0; index < target.length; ++index) {
|
|
151
|
+
if (unchecked(target[index]) === view) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
target.push(view);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function appendDefaultOrderedCandidate(target: Array<ScrollView>, candidate: ScrollView): void {
|
|
159
|
+
if (indexOfScrollView(candidate) < 0) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const candidateBounds = getUsableVisibleBounds(candidate);
|
|
163
|
+
if (candidateBounds === null) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const anchorBounds = getUsableVisibleBounds(getDefaultOrderingAnchor(candidate));
|
|
167
|
+
if (anchorBounds === null) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
let insertIndex = target.length;
|
|
171
|
+
while (insertIndex > 0) {
|
|
172
|
+
const current = unchecked(target[insertIndex - 1]);
|
|
173
|
+
const currentAnchorBounds = getUsableVisibleBounds(getDefaultOrderingAnchor(current));
|
|
174
|
+
const currentCandidateBounds = getUsableVisibleBounds(current);
|
|
175
|
+
if (currentAnchorBounds === null || currentCandidateBounds === null) {
|
|
176
|
+
insertIndex -= 1;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (isBetterDefaultCandidate(anchorBounds, currentAnchorBounds)) {
|
|
180
|
+
insertIndex -= 1;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (
|
|
184
|
+
unchecked(anchorBounds[0]) == unchecked(currentAnchorBounds[0]) &&
|
|
185
|
+
unchecked(anchorBounds[1]) == unchecked(currentAnchorBounds[1]) &&
|
|
186
|
+
unchecked(anchorBounds[2]) == unchecked(currentAnchorBounds[2]) &&
|
|
187
|
+
unchecked(anchorBounds[3]) == unchecked(currentAnchorBounds[3]) &&
|
|
188
|
+
isBetterDefaultCandidate(candidateBounds, currentCandidateBounds)
|
|
189
|
+
) {
|
|
190
|
+
insertIndex -= 1;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
target.push(candidate);
|
|
196
|
+
for (let cursor = target.length - 1; cursor > insertIndex; --cursor) {
|
|
197
|
+
unchecked(target[cursor] = unchecked(target[cursor - 1]));
|
|
198
|
+
}
|
|
199
|
+
unchecked(target[insertIndex] = candidate);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function isDescendantOf(node: Node, ancestor: Node): bool {
|
|
203
|
+
let current: Node | null = node;
|
|
204
|
+
while (current !== null) {
|
|
205
|
+
if (current === ancestor) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
current = current.parentNode;
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function selectDefaultCandidatesWithin(root: Node | null): Array<ScrollView> {
|
|
214
|
+
const ordered = new Array<ScrollView>();
|
|
215
|
+
if (root === null) {
|
|
216
|
+
return ordered;
|
|
217
|
+
}
|
|
218
|
+
for (let index = 0; index < scrollViews.length; ++index) {
|
|
219
|
+
const candidate = unchecked(scrollViews[index]);
|
|
220
|
+
if (!isDescendantOf(candidate, root)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
appendDefaultOrderedCandidate(ordered, candidate);
|
|
224
|
+
}
|
|
225
|
+
return ordered;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function findNearestDescendantScrollBranch(node: Node | null): Node | null {
|
|
229
|
+
let current = node;
|
|
230
|
+
while (current !== null) {
|
|
231
|
+
if (current instanceof ScrollView) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
if (selectDefaultCandidatesWithin(current).length > 0) {
|
|
235
|
+
return current;
|
|
236
|
+
}
|
|
237
|
+
current = current.parentNode;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function selectScrollViewByPoint(pointX: f32, pointY: f32): ScrollView | null {
|
|
243
|
+
let bestView: ScrollView | null = null;
|
|
244
|
+
let bestBounds: Float32Array | null = null;
|
|
245
|
+
let bestContainsPoint = false;
|
|
246
|
+
for (let index = 0; index < scrollViews.length; ++index) {
|
|
247
|
+
const candidateView = unchecked(scrollViews[index]);
|
|
248
|
+
const bounds = ui.tryGetBounds(candidateView.builtHandle);
|
|
249
|
+
if (!isUsableBounds(bounds)) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const candidateBounds = changetype<Float32Array>(bounds);
|
|
253
|
+
const candidateContainsPoint = containsPoint(candidateBounds, pointX, pointY);
|
|
254
|
+
if (bestBounds === null || isBetterPointCandidate(
|
|
255
|
+
candidateBounds,
|
|
256
|
+
candidateContainsPoint,
|
|
257
|
+
changetype<Float32Array>(bestBounds),
|
|
258
|
+
bestContainsPoint,
|
|
259
|
+
pointX,
|
|
260
|
+
pointY,
|
|
261
|
+
)) {
|
|
262
|
+
bestView = candidateView;
|
|
263
|
+
bestBounds = candidateBounds;
|
|
264
|
+
bestContainsPoint = candidateContainsPoint;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return bestView;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function resolveAncestorScrollView(node: Node | null): ScrollView | null {
|
|
271
|
+
let current = node;
|
|
272
|
+
while (current !== null) {
|
|
273
|
+
if (current instanceof ScrollView) {
|
|
274
|
+
return changetype<ScrollView>(current);
|
|
275
|
+
}
|
|
276
|
+
current = current.parentNode;
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function appendAncestorScrollViewFallbacks(view: ScrollView | null, target: Array<ScrollView>): void {
|
|
282
|
+
if (view === null) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
let current: Node | null = view.parentNode;
|
|
286
|
+
while (current !== null) {
|
|
287
|
+
if (current instanceof ScrollView) {
|
|
288
|
+
appendUniqueScrollView(target, changetype<ScrollView>(current));
|
|
289
|
+
}
|
|
290
|
+
current = current.parentNode;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function selectDefaultCandidates(): Array<ScrollView> {
|
|
295
|
+
const ordered = new Array<ScrollView>();
|
|
296
|
+
for (let index = 0; index < scrollViews.length; ++index) {
|
|
297
|
+
appendDefaultOrderedCandidate(ordered, unchecked(scrollViews[index]));
|
|
298
|
+
}
|
|
299
|
+
return ordered;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function registerKeyboardScrollNode(node: Node): void {
|
|
303
|
+
if (!(node instanceof ScrollView)) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const view = changetype<ScrollView>(node);
|
|
307
|
+
if (indexOfScrollView(view) >= 0) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
scrollViews.push(view);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function unregisterKeyboardScrollNode(node: Node): void {
|
|
314
|
+
if (!(node instanceof ScrollView)) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const view = changetype<ScrollView>(node);
|
|
318
|
+
const index = indexOfScrollView(view);
|
|
319
|
+
if (index < 0) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
for (let cursor = index; cursor < scrollViews.length - 1; ++cursor) {
|
|
323
|
+
unchecked(scrollViews[cursor] = unchecked(scrollViews[cursor + 1]));
|
|
324
|
+
}
|
|
325
|
+
scrollViews.length = scrollViews.length - 1;
|
|
326
|
+
if (selectedScrollView === view) {
|
|
327
|
+
selectedScrollView = null;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function trackKeyboardScrollPointerUp(targetNode: Node | null, x: f32, y: f32): void {
|
|
332
|
+
selectedBranchRoot = targetNode instanceof ScrollView
|
|
333
|
+
? changetype<Node>(targetNode)
|
|
334
|
+
: findNearestDescendantScrollBranch(targetNode);
|
|
335
|
+
const ancestorScrollView = resolveAncestorScrollView(targetNode);
|
|
336
|
+
if (ancestorScrollView !== null) {
|
|
337
|
+
if (selectedBranchRoot === null) {
|
|
338
|
+
selectedBranchRoot = changetype<Node>(ancestorScrollView);
|
|
339
|
+
}
|
|
340
|
+
selectedScrollView = ancestorScrollView;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (selectedBranchRoot !== null) {
|
|
344
|
+
const branchCandidates = selectDefaultCandidatesWithin(selectedBranchRoot);
|
|
345
|
+
selectedScrollView = branchCandidates.length > 0 ? unchecked(branchCandidates[0]) : null;
|
|
346
|
+
if (selectedScrollView !== null) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
selectedScrollView = selectScrollViewByPoint(x, y);
|
|
351
|
+
if (selectedScrollView === null) {
|
|
352
|
+
selectedBranchRoot = null;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function getKeyboardScrollSelectedCandidate(): ScrollView | null {
|
|
357
|
+
return selectedScrollView;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function getKeyboardScrollFallbackCandidates(): Array<ScrollView> {
|
|
361
|
+
const ordered = new Array<ScrollView>();
|
|
362
|
+
appendAncestorScrollViewFallbacks(selectedScrollView, ordered);
|
|
363
|
+
if (selectedBranchRoot !== null) {
|
|
364
|
+
const branchCandidates = selectDefaultCandidatesWithin(selectedBranchRoot);
|
|
365
|
+
for (let index = 0; index < branchCandidates.length; ++index) {
|
|
366
|
+
const candidate = unchecked(branchCandidates[index]);
|
|
367
|
+
if (candidate !== selectedScrollView) {
|
|
368
|
+
appendUniqueScrollView(ordered, candidate);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const globalCandidates = selectDefaultCandidates();
|
|
373
|
+
for (let index = 0; index < globalCandidates.length; ++index) {
|
|
374
|
+
const candidate = unchecked(globalCandidates[index]);
|
|
375
|
+
if (candidate !== selectedScrollView) {
|
|
376
|
+
appendUniqueScrollView(ordered, candidate);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return ordered;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function resetKeyboardScrollTracking(): void {
|
|
383
|
+
scrollViews.length = 0;
|
|
384
|
+
selectedScrollView = null;
|
|
385
|
+
selectedBranchRoot = null;
|
|
386
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { KeyEventType, PointerEventType, fui_log, fui_logs_enabled } from "./ffi";
|
|
2
|
+
|
|
3
|
+
function writeUtf8(text: string): Uint8Array {
|
|
4
|
+
return Uint8Array.wrap(String.UTF8.encode(text, false));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function describeValue<T>(value: T): string {
|
|
8
|
+
if (isString<T>()) {
|
|
9
|
+
return "\"" + changetype<string>(value) + "\"";
|
|
10
|
+
}
|
|
11
|
+
if (isBoolean<T>()) {
|
|
12
|
+
return changetype<bool>(value) ? "true" : "false";
|
|
13
|
+
}
|
|
14
|
+
if (isInteger<T>() || isFloat<T>()) {
|
|
15
|
+
return value.toString();
|
|
16
|
+
}
|
|
17
|
+
return "<type#" + idof<T>().toString() + ">";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function log(category: string, message: string): void {
|
|
21
|
+
if (!logsEnabled()) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
writeLog(category, message);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function warn(category: string, message: string): void {
|
|
28
|
+
writeLog("Warning/" + category, message);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function error(category: string, message: string): void {
|
|
32
|
+
writeLog("Error/" + category, message);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function writeLog(category: string, message: string): void {
|
|
36
|
+
const cat = writeUtf8(category);
|
|
37
|
+
const msg = writeUtf8(message);
|
|
38
|
+
fui_log(
|
|
39
|
+
cat.length > 0 ? cat.dataStart : 0,
|
|
40
|
+
<u32>cat.length,
|
|
41
|
+
msg.length > 0 ? msg.dataStart : 0,
|
|
42
|
+
<u32>msg.length,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function logsEnabled(): bool {
|
|
47
|
+
return fui_logs_enabled();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function describeHandle(handle: u64): string {
|
|
51
|
+
return handle.toString();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function describePointerEventType(eventType: PointerEventType): string {
|
|
55
|
+
switch (eventType) {
|
|
56
|
+
case PointerEventType.Down:
|
|
57
|
+
return "down";
|
|
58
|
+
case PointerEventType.Up:
|
|
59
|
+
return "up";
|
|
60
|
+
case PointerEventType.Move:
|
|
61
|
+
return "move";
|
|
62
|
+
case PointerEventType.Enter:
|
|
63
|
+
return "enter";
|
|
64
|
+
case PointerEventType.Leave:
|
|
65
|
+
return "leave";
|
|
66
|
+
default:
|
|
67
|
+
return "pointer(" + (<u32>eventType).toString() + ")";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function describeKeyEventType(eventType: KeyEventType): string {
|
|
72
|
+
switch (eventType) {
|
|
73
|
+
case KeyEventType.Down:
|
|
74
|
+
return "down";
|
|
75
|
+
case KeyEventType.Up:
|
|
76
|
+
return "up";
|
|
77
|
+
default:
|
|
78
|
+
return "key(" + (<u32>eventType).toString() + ")";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { fui_navigate_to } from "./ffi";
|
|
2
|
+
import { Signal } from "./Signal";
|
|
3
|
+
|
|
4
|
+
export const currentRoute = new Signal<string>("");
|
|
5
|
+
|
|
6
|
+
export function navigateTo(target: string, openInNewTab: bool = false): void {
|
|
7
|
+
const bytes = Uint8Array.wrap(String.UTF8.encode(target, false));
|
|
8
|
+
fui_navigate_to(bytes.length > 0 ? bytes.dataStart : 0, <u32>bytes.length, openInNewTab);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function handleRouteChanged(route: string): void {
|
|
12
|
+
currentRoute.value = route;
|
|
13
|
+
}
|