@effindomv2/fui-as 0.1.36 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/browser/routed-app-conventions.ts +1 -1
  2. package/browser/src/common-harness.ts +1 -27
  3. package/browser/src/index.ts +19 -26
  4. package/browser/src/routed-harness.ts +1 -194
  5. package/browser/src/shared-browser.ts +6 -0
  6. package/package.json +26 -16
  7. package/scripts/build.sh +51 -47
  8. package/scripts/framework-host-services.ts +1 -1
  9. package/scripts/generate-host-events.ts +15 -241
  10. package/scripts/generate-host-services.ts +13 -261
  11. package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
  12. package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
  13. package/scripts/hostgen/common.ts +73 -0
  14. package/scripts/hostgen/registry.ts +159 -0
  15. package/src/Fui.ts +70 -4
  16. package/src/FuiExports.ts +13 -1
  17. package/src/bindings/ui.ts +165 -8
  18. package/src/controls/Button.ts +98 -71
  19. package/src/controls/Checkbox.ts +23 -21
  20. package/src/controls/ComboBox.ts +1201 -0
  21. package/src/controls/ContextMenu.ts +279 -165
  22. package/src/controls/Dialog.ts +91 -17
  23. package/src/controls/Dropdown.ts +101 -333
  24. package/src/controls/NavLink.ts +29 -6
  25. package/src/controls/Popup.ts +10 -5
  26. package/src/controls/ProgressBar.ts +1 -2
  27. package/src/controls/RadioButton.ts +34 -13
  28. package/src/controls/RadioGroup.ts +32 -19
  29. package/src/controls/Slider.ts +21 -23
  30. package/src/controls/Switch.ts +34 -17
  31. package/src/controls/index.ts +2 -1
  32. package/src/controls/internal/ButtonPresenter.ts +5 -2
  33. package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
  34. package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
  35. package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
  36. package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
  37. package/src/controls/internal/PressableLabeledControl.ts +4 -7
  38. package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
  39. package/src/controls/internal/SelectablePopupList.ts +463 -0
  40. package/src/controls/internal/SliderPresenter.ts +5 -5
  41. package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
  42. package/src/controls/internal/TextInputCore.ts +307 -41
  43. package/src/controls/internal/TextInputPresenter.ts +2 -2
  44. package/src/controls/templating.ts +10 -0
  45. package/src/core/Application.ts +19 -9
  46. package/src/core/Bitmap.ts +219 -1
  47. package/src/core/ContextMenuManager.ts +128 -44
  48. package/src/core/DebugApi.ts +17 -2
  49. package/src/core/DragGesture.ts +8 -7
  50. package/src/core/EventRouter.ts +332 -12
  51. package/src/core/Fetch.ts +15 -7
  52. package/src/core/FetchFfi.ts +1 -15
  53. package/src/core/File.ts +75 -34
  54. package/src/core/FocusAdornerManager.ts +3 -3
  55. package/src/core/FocusVisibility.ts +7 -0
  56. package/src/core/FrameScheduler.ts +126 -0
  57. package/src/core/ImageSampling.ts +43 -0
  58. package/src/core/Logger.ts +2 -0
  59. package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
  60. package/src/core/Node.ts +1039 -97
  61. package/src/core/Platform.ts +16 -16
  62. package/src/core/SelectionHandleAdornerManager.ts +697 -0
  63. package/src/core/Theme.ts +34 -20
  64. package/src/core/ToolTipManager.ts +3 -3
  65. package/src/core/Typography.ts +411 -19
  66. package/src/core/Worker.ts +69 -33
  67. package/src/core/event_exports.ts +123 -5
  68. package/src/core/ffi.ts +5 -746
  69. package/src/core/generated/FetchHostAbi.ts +10 -0
  70. package/src/core/generated/FrameworkHostServices.ts +1 -1
  71. package/src/core/generated/HostAbi.ts +220 -0
  72. package/src/core/generated/UiAbi.ts +356 -0
  73. package/src/core/generated/UiEnums.ts +186 -0
  74. package/src/drawing/DrawCallback.ts +34 -0
  75. package/src/drawing/DrawContext.ts +189 -0
  76. package/src/drawing/Paint.ts +34 -0
  77. package/src/drawing/Path.ts +40 -0
  78. package/src/nodes/CustomDrawable.ts +61 -0
  79. package/src/nodes/FlexBox.ts +57 -11
  80. package/src/nodes/Grid.ts +5 -5
  81. package/src/nodes/Image.ts +20 -1
  82. package/src/nodes/RichText.ts +36 -25
  83. package/src/nodes/ScrollBar.ts +2 -1
  84. package/src/nodes/ScrollBox.ts +7 -2
  85. package/src/nodes/ScrollView.ts +17 -6
  86. package/src/nodes/Svg.ts +18 -1
  87. package/src/nodes/Text.ts +1 -1
  88. package/src/nodes/TextCore.ts +115 -20
  89. package/src/nodes/index.ts +3 -1
  90. package/src/text/DynamicTextLayout.ts +275 -0
  91. package/src/text/TextLayout.ts +308 -0
  92. package/src/worker/Worker.ts +3 -36
  93. package/src/worker/WorkerJob.ts +5 -9
  94. package/src/worker/ffi.ts +0 -6
  95. package/browser/src/common-harness/host-imports.ts +0 -447
  96. package/browser/src/common-harness/interop.ts +0 -39
  97. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  98. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  99. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  100. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  101. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  102. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  103. package/browser/src/common-harness/managed-harness.ts +0 -1451
  104. package/browser/src/common-harness/managed-history.ts +0 -168
  105. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  106. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  107. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  108. package/browser/src/common-harness/types.ts +0 -205
  109. package/browser/src/common-harness/ui-chrome.ts +0 -206
  110. package/browser/src/common-harness/ui-imports.ts +0 -553
  111. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  112. package/browser/src/file-processing-worker.ts +0 -89
  113. package/browser/src/host-events.ts +0 -118
  114. package/browser/src/host-services.ts +0 -483
  115. package/browser/src/persisted-ui-state.ts +0 -206
  116. package/browser/src/routed-app-conventions.ts +0 -292
  117. package/browser/src/worker-bootstrap.ts +0 -916
  118. package/browser/src/worker-manager.ts +0 -234
  119. package/browser/src/worker-types.ts +0 -68
@@ -0,0 +1,697 @@
1
+ import { FlexBox, Portal } from "../nodes";
2
+ import { PointerEventType, Unit, Visibility, fui_is_coarse_pointer } from "./ffi";
3
+ import { MobileTextSelectionToolbarManager } from "./MobileTextSelectionToolbarManager";
4
+ import { Node, PointerEventArgs, PointerType } from "./Node";
5
+ import * as ui from "../bindings/ui";
6
+
7
+ const HANDLE_COLOR: u32 = 0x0a84ffff;
8
+ const HIT_TARGET_SIZE: f32 = 90.0;
9
+ const HIT_TARGET_PADDING: f32 = 25.0;
10
+ const START_ANCHOR_WIDTH: f32 = 0.0;
11
+ const KNOB_SIZE: f32 = 18.0;
12
+ const SHOULDER_SIZE: f32 = 8.0;
13
+ const KNOB_Y: f32 = HIT_TARGET_PADDING;
14
+ const SHOULDER_Y: f32 = HIT_TARGET_PADDING;
15
+ const START_STEM_X: f32 = 72.0;
16
+ const END_STEM_X: f32 = 18.0;
17
+
18
+ export enum SelectionHandleMode {
19
+ Auto = 0,
20
+ Always = 1,
21
+ Disabled = 2,
22
+ }
23
+
24
+ enum SelectionHandleSide {
25
+ Start = 0,
26
+ End = 1,
27
+ }
28
+
29
+ enum SelectionHandleDragSide {
30
+ None = -1,
31
+ Start = 0,
32
+ End = 1,
33
+ }
34
+
35
+ function handleStemX(side: SelectionHandleSide): f32 {
36
+ return side == SelectionHandleSide.Start ? START_STEM_X : END_STEM_X;
37
+ }
38
+
39
+ function handleKnobX(side: SelectionHandleSide): f32 {
40
+ return side == SelectionHandleSide.Start
41
+ ? handleStemX(side) - KNOB_SIZE + START_ANCHOR_WIDTH
42
+ : handleStemX(side);
43
+ }
44
+
45
+ function handleShoulderX(side: SelectionHandleSide): f32 {
46
+ const knobX = handleKnobX(side);
47
+ return side == SelectionHandleSide.Start
48
+ ? knobX + KNOB_SIZE - SHOULDER_SIZE
49
+ : knobX;
50
+ }
51
+
52
+ function makeKnob(side: SelectionHandleSide): FlexBox {
53
+ return new FlexBox()
54
+ .positionAbsolute()
55
+ .position(handleKnobX(side), KNOB_Y)
56
+ .width(KNOB_SIZE, Unit.Pixel)
57
+ .height(KNOB_SIZE, Unit.Pixel)
58
+ .cornerRadius(KNOB_SIZE * 0.5)
59
+ .bgColor(HANDLE_COLOR)
60
+ .border(1.0, 0x00000022)
61
+ .preserveSelectionOnPointerDown(true) as FlexBox;
62
+ }
63
+
64
+ function makeShoulder(side: SelectionHandleSide): FlexBox {
65
+ return new FlexBox()
66
+ .positionAbsolute()
67
+ .position(handleShoulderX(side), SHOULDER_Y)
68
+ .width(SHOULDER_SIZE, Unit.Pixel)
69
+ .height(SHOULDER_SIZE, Unit.Pixel)
70
+ .bgColor(HANDLE_COLOR)
71
+ .preserveSelectionOnPointerDown(true) as FlexBox;
72
+ }
73
+
74
+ function makeHandle(side: SelectionHandleSide): FlexBox {
75
+ const knob = makeKnob(side);
76
+ const shoulder = makeShoulder(side);
77
+ if (side == SelectionHandleSide.Start) {
78
+ knob.onPointerDown(handleStartPointerDown);
79
+ shoulder.onPointerDown(handleStartPointerDown);
80
+ } else {
81
+ knob.onPointerDown(handleEndPointerDown);
82
+ shoulder.onPointerDown(handleEndPointerDown);
83
+ }
84
+ knob
85
+ .onPointerMove(handlePointerMove)
86
+ .onPointerUp(handlePointerUp)
87
+ .onPointerCancel(handlePointerCancel);
88
+ shoulder
89
+ .onPointerMove(handlePointerMove)
90
+ .onPointerUp(handlePointerUp)
91
+ .onPointerCancel(handlePointerCancel);
92
+ const handle = new FlexBox()
93
+ .positionAbsolute()
94
+ .width(HIT_TARGET_SIZE, Unit.Pixel)
95
+ .height(HIT_TARGET_SIZE, Unit.Pixel)
96
+ .bgColor(0x00000000)
97
+ .child(knob)
98
+ .child(shoulder)
99
+ .visibility(Visibility.Hidden)
100
+ .preserveSelectionOnPointerDown(true) as FlexBox;
101
+ return handle;
102
+ }
103
+
104
+ function handleStartPointerDown(event: PointerEventArgs): void {
105
+ SelectionHandleAdornerManager.handleStartPointerDown(event);
106
+ }
107
+
108
+ function handleEndPointerDown(event: PointerEventArgs): void {
109
+ SelectionHandleAdornerManager.handleEndPointerDown(event);
110
+ }
111
+
112
+ function handlePointerMove(event: PointerEventArgs): void {
113
+ SelectionHandleAdornerManager.handlePointerMove(event);
114
+ }
115
+
116
+ function handlePointerUp(event: PointerEventArgs): void {
117
+ SelectionHandleAdornerManager.handlePointerUp(event);
118
+ }
119
+
120
+ function handlePointerCancel(event: PointerEventArgs): void {
121
+ SelectionHandleAdornerManager.handlePointerCancel(event);
122
+ }
123
+
124
+ export class SelectionHandleAdornerManager {
125
+ private static hostRoot: Portal | null = null;
126
+ private static startHandle: FlexBox | null = null;
127
+ private static endHandle: FlexBox | null = null;
128
+ private static activeHandle: u64 = 0;
129
+ private static activeStart: u32 = 0;
130
+ private static activeEnd: u32 = 0;
131
+ private static activeUsesRangeGeometry: bool = false;
132
+ private static activeUsesCrossGeometry: bool = false;
133
+ private static modeValue: SelectionHandleMode = SelectionHandleMode.Auto;
134
+ private static lastPointerType: PointerType = PointerType.Unknown;
135
+ private static draggingSide: SelectionHandleDragSide = SelectionHandleDragSide.None;
136
+ private static draggingCapturedVisualSide: SelectionHandleDragSide = SelectionHandleDragSide.None;
137
+ private static startAnchorX: f32 = 0.0;
138
+ private static startAnchorY: f32 = 0.0;
139
+ private static endAnchorX: f32 = 0.0;
140
+ private static endAnchorY: f32 = 0.0;
141
+ private static hasStartAnchor: bool = false;
142
+ private static hasEndAnchor: bool = false;
143
+ private static stationaryAnchorX: f32 = 0.0;
144
+ private static stationaryAnchorY: f32 = 0.0;
145
+ private static hasStationaryAnchor: bool = false;
146
+
147
+ static createDefaultHost(): Portal {
148
+ const existingHost = this.hostRoot;
149
+ if (existingHost !== null) {
150
+ return existingHost;
151
+ }
152
+ const startHandle = makeHandle(SelectionHandleSide.Start);
153
+ const endHandle = makeHandle(SelectionHandleSide.End);
154
+ startHandle
155
+ .onPointerDown(handleStartPointerDown)
156
+ .onPointerMove(handlePointerMove)
157
+ .onPointerUp(handlePointerUp)
158
+ .onPointerCancel(handlePointerCancel);
159
+ endHandle
160
+ .onPointerDown(handleEndPointerDown)
161
+ .onPointerMove(handlePointerMove)
162
+ .onPointerUp(handlePointerUp)
163
+ .onPointerCancel(handlePointerCancel);
164
+ const hostRoot = new Portal()
165
+ .positionAbsolute()
166
+ .position(0.0, 0.0)
167
+ .width(100.0, Unit.Percent)
168
+ .height(100.0, Unit.Percent)
169
+ .child(startHandle)
170
+ .child(endHandle) as Portal;
171
+ this.hostRoot = hostRoot;
172
+ this.startHandle = startHandle;
173
+ this.endHandle = endHandle;
174
+ return hostRoot;
175
+ }
176
+
177
+ static clear(): void {
178
+ if (this.draggingSide != SelectionHandleDragSide.None) {
179
+ this.endHandleDrag();
180
+ }
181
+ this.activeHandle = 0;
182
+ this.activeStart = 0;
183
+ this.activeEnd = 0;
184
+ this.activeUsesRangeGeometry = false;
185
+ this.activeUsesCrossGeometry = false;
186
+ this.draggingSide = SelectionHandleDragSide.None;
187
+ this.draggingCapturedVisualSide = SelectionHandleDragSide.None;
188
+ this.hasStationaryAnchor = false;
189
+ this.hideHandles();
190
+ }
191
+
192
+ static reset(): void {
193
+ const hostRoot = this.hostRoot;
194
+ if (hostRoot !== null) {
195
+ hostRoot.dispose();
196
+ }
197
+ this.hostRoot = null;
198
+ this.startHandle = null;
199
+ this.endHandle = null;
200
+ this.activeHandle = 0;
201
+ this.activeStart = 0;
202
+ this.activeEnd = 0;
203
+ this.activeUsesRangeGeometry = false;
204
+ this.activeUsesCrossGeometry = false;
205
+ this.modeValue = SelectionHandleMode.Auto;
206
+ this.lastPointerType = PointerType.Unknown;
207
+ this.draggingSide = SelectionHandleDragSide.None;
208
+ this.draggingCapturedVisualSide = SelectionHandleDragSide.None;
209
+ this.startAnchorX = 0.0;
210
+ this.startAnchorY = 0.0;
211
+ this.endAnchorX = 0.0;
212
+ this.endAnchorY = 0.0;
213
+ this.hasStartAnchor = false;
214
+ this.hasEndAnchor = false;
215
+ this.stationaryAnchorX = 0.0;
216
+ this.stationaryAnchorY = 0.0;
217
+ this.hasStationaryAnchor = false;
218
+ }
219
+
220
+ static setMode(mode: SelectionHandleMode): void {
221
+ this.modeValue = mode;
222
+ if (mode == SelectionHandleMode.Disabled) {
223
+ this.clear();
224
+ }
225
+ }
226
+
227
+ static mode(): SelectionHandleMode {
228
+ return this.modeValue;
229
+ }
230
+
231
+ static recordPointerEvent(eventType: PointerEventType, pointerType: PointerType): void {
232
+ if (eventType == PointerEventType.Down) {
233
+ this.lastPointerType = pointerType;
234
+ }
235
+ }
236
+
237
+ static handleSelectionChanged(handle: u64, start: u32, end: u32): void {
238
+ if (start == end || !this.shouldShow()) {
239
+ if (start == end && this.draggingSide != SelectionHandleDragSide.None && this.activeHandle == handle) {
240
+ this.activeStart = start;
241
+ this.activeEnd = end;
242
+ this.ensureHost();
243
+ this.positionRangeHandles();
244
+ this.showNonDraggedHandles();
245
+ return;
246
+ }
247
+ this.clear();
248
+ return;
249
+ }
250
+ this.activeHandle = handle;
251
+ this.activeStart = start;
252
+ this.activeEnd = end;
253
+ this.activeUsesRangeGeometry = true;
254
+ this.activeUsesCrossGeometry = false;
255
+ this.ensureHost();
256
+ if (!this.positionRangeHandles()) {
257
+ this.clear();
258
+ return;
259
+ }
260
+ if (this.draggingSide != SelectionHandleDragSide.None) {
261
+ this.showNonDraggedHandles();
262
+ return;
263
+ }
264
+ this.showHandles();
265
+ }
266
+
267
+ static handleCrossSelectionChanged(handle: u64, text: string): void {
268
+ if (text.length == 0 || !this.shouldShow()) {
269
+ if (text.length == 0 && this.draggingSide != SelectionHandleDragSide.None && this.activeHandle == handle) {
270
+ this.activeStart = 0;
271
+ this.activeEnd = 0;
272
+ this.ensureHost();
273
+ this.showNonDraggedHandles();
274
+ return;
275
+ }
276
+ this.clear();
277
+ return;
278
+ }
279
+ this.activeHandle = handle;
280
+ this.activeStart = 0;
281
+ this.activeEnd = <u32>text.length;
282
+ this.activeUsesRangeGeometry = true;
283
+ this.activeUsesCrossGeometry = true;
284
+ this.ensureHost();
285
+ if (!this.positionCrossSelectionHandles()) {
286
+ this.activeUsesRangeGeometry = false;
287
+ this.activeUsesCrossGeometry = false;
288
+ this.positionPlaceholderHandles();
289
+ }
290
+ if (this.draggingSide != SelectionHandleDragSide.None) {
291
+ this.showNonDraggedHandles();
292
+ return;
293
+ }
294
+ this.showHandles();
295
+ }
296
+
297
+ static refreshActiveGeometry(): void {
298
+ if (this.activeHandle == 0 || !this.activeUsesRangeGeometry) {
299
+ return;
300
+ }
301
+ if (this.activeUsesCrossGeometry) {
302
+ if (!this.positionCrossSelectionHandles()) {
303
+ this.clear();
304
+ }
305
+ return;
306
+ }
307
+ if (!this.positionRangeHandles()) {
308
+ this.clear();
309
+ }
310
+ }
311
+
312
+ static isVisible(): bool {
313
+ return this.activeHandle != 0;
314
+ }
315
+
316
+ static activeTextHandle(): u64 {
317
+ return this.activeHandle;
318
+ }
319
+
320
+ static activeSelectionStart(): u32 {
321
+ return this.activeStart;
322
+ }
323
+
324
+ static activeSelectionEnd(): u32 {
325
+ return this.activeEnd;
326
+ }
327
+
328
+ static startHandleNode(): Node | null {
329
+ return this.startHandle;
330
+ }
331
+
332
+ static endHandleNode(): Node | null {
333
+ return this.endHandle;
334
+ }
335
+
336
+ private static ensureHost(): void {
337
+ if (this.hostRoot === null) {
338
+ this.createDefaultHost();
339
+ }
340
+ }
341
+
342
+ private static shouldShow(): bool {
343
+ if (this.modeValue == SelectionHandleMode.Disabled) {
344
+ return false;
345
+ }
346
+ if (this.modeValue == SelectionHandleMode.Always) {
347
+ return true;
348
+ }
349
+ if (this.lastPointerType == PointerType.Touch) {
350
+ return true;
351
+ }
352
+ if (this.lastPointerType != PointerType.Unknown) {
353
+ return false;
354
+ }
355
+ return fui_is_coarse_pointer();
356
+ }
357
+
358
+ private static showHandles(): void {
359
+ const startHandle = this.startHandle;
360
+ const endHandle = this.endHandle;
361
+ if (startHandle !== null) {
362
+ startHandle.visibility(Visibility.Normal);
363
+ this.setHandleChromeVisibility(startHandle, Visibility.Normal);
364
+ }
365
+ if (endHandle !== null) {
366
+ endHandle.visibility(Visibility.Normal);
367
+ this.setHandleChromeVisibility(endHandle, Visibility.Normal);
368
+ }
369
+ }
370
+
371
+ private static hideHandle(side: SelectionHandleDragSide): void {
372
+ const handle = side == SelectionHandleDragSide.Start ? this.startHandle : this.endHandle;
373
+ if (handle !== null) {
374
+ handle.visibility(Visibility.Normal);
375
+ this.setHandleChromeVisibility(handle, Visibility.Hidden);
376
+ }
377
+ }
378
+
379
+ private static showHandle(side: SelectionHandleDragSide): void {
380
+ const handle = side == SelectionHandleDragSide.Start ? this.startHandle : this.endHandle;
381
+ if (handle !== null) {
382
+ handle.visibility(Visibility.Normal);
383
+ this.setHandleChromeVisibility(handle, Visibility.Normal);
384
+ }
385
+ }
386
+
387
+ private static showNonDraggedHandles(): void {
388
+ const hiddenVisualSide = this.draggingCapturedVisualSide;
389
+ const startHandle = this.startHandle;
390
+ const endHandle = this.endHandle;
391
+ if (startHandle !== null) {
392
+ startHandle.visibility(Visibility.Normal);
393
+ this.setHandleChromeVisibility(
394
+ startHandle,
395
+ hiddenVisualSide == SelectionHandleDragSide.Start ? Visibility.Hidden : Visibility.Normal,
396
+ );
397
+ }
398
+ if (endHandle !== null) {
399
+ endHandle.visibility(Visibility.Normal);
400
+ this.setHandleChromeVisibility(
401
+ endHandle,
402
+ hiddenVisualSide == SelectionHandleDragSide.End ? Visibility.Hidden : Visibility.Normal,
403
+ );
404
+ }
405
+ }
406
+
407
+ private static hideHandles(): void {
408
+ const startHandle = this.startHandle;
409
+ const endHandle = this.endHandle;
410
+ if (startHandle !== null) {
411
+ this.setHandleChromeVisibility(startHandle, Visibility.Normal);
412
+ startHandle.visibility(Visibility.Hidden);
413
+ }
414
+ if (endHandle !== null) {
415
+ this.setHandleChromeVisibility(endHandle, Visibility.Normal);
416
+ endHandle.visibility(Visibility.Hidden);
417
+ }
418
+ }
419
+
420
+ private static setHandleChromeVisibility(handle: Node, visibility: Visibility): void {
421
+ for (let i = 0; i < handle.childCount; i += 1) {
422
+ const child = handle.getChildAt(i);
423
+ if (child !== null) {
424
+ child.visibility(visibility);
425
+ }
426
+ }
427
+ }
428
+
429
+ static handleStartPointerDown(event: PointerEventArgs): void {
430
+ event.handled = SelectionHandleAdornerManager.beginHandleDrag(
431
+ SelectionHandleAdornerManager.semanticSideForVisualSide(SelectionHandleDragSide.Start),
432
+ SelectionHandleDragSide.Start,
433
+ event.pointerType,
434
+ );
435
+ }
436
+
437
+ static handleEndPointerDown(event: PointerEventArgs): void {
438
+ event.handled = SelectionHandleAdornerManager.beginHandleDrag(
439
+ SelectionHandleAdornerManager.semanticSideForVisualSide(SelectionHandleDragSide.End),
440
+ SelectionHandleDragSide.End,
441
+ event.pointerType,
442
+ );
443
+ }
444
+
445
+ static handlePointerMove(event: PointerEventArgs): void {
446
+ if (SelectionHandleAdornerManager.draggingSide == SelectionHandleDragSide.None) {
447
+ return;
448
+ }
449
+ event.handled = true;
450
+ SelectionHandleAdornerManager.refreshActiveGeometry();
451
+ SelectionHandleAdornerManager.showNonDraggedHandles();
452
+ }
453
+
454
+ static handlePointerUp(event: PointerEventArgs): void {
455
+ if (SelectionHandleAdornerManager.draggingSide == SelectionHandleDragSide.None) {
456
+ return;
457
+ }
458
+ event.handled = true;
459
+ SelectionHandleAdornerManager.endHandleDrag();
460
+ }
461
+
462
+ static handlePointerCancel(event: PointerEventArgs): void {
463
+ if (SelectionHandleAdornerManager.draggingSide == SelectionHandleDragSide.None) {
464
+ return;
465
+ }
466
+ event.handled = true;
467
+ SelectionHandleAdornerManager.endHandleDrag();
468
+ }
469
+
470
+ static routeActiveHandleDragEvent(event: PointerEventArgs): bool {
471
+ if (SelectionHandleAdornerManager.draggingSide == SelectionHandleDragSide.None) {
472
+ return false;
473
+ }
474
+ if (event.eventType == PointerEventType.Move) {
475
+ SelectionHandleAdornerManager.handlePointerMove(event);
476
+ return event.handled;
477
+ }
478
+ if (event.eventType == PointerEventType.Up) {
479
+ SelectionHandleAdornerManager.handlePointerUp(event);
480
+ return event.handled;
481
+ }
482
+ if (event.eventType == PointerEventType.Cancel) {
483
+ SelectionHandleAdornerManager.handlePointerCancel(event);
484
+ return event.handled;
485
+ }
486
+ return false;
487
+ }
488
+
489
+ private static beginHandleDrag(
490
+ side: SelectionHandleDragSide,
491
+ visualSide: SelectionHandleDragSide,
492
+ pointerType: PointerType,
493
+ ): bool {
494
+ if (pointerType != PointerType.Touch && pointerType != PointerType.Pen) {
495
+ return false;
496
+ }
497
+ if (this.activeHandle == 0) {
498
+ return false;
499
+ }
500
+ if (!this.captureStationaryAnchor(visualSide)) {
501
+ return false;
502
+ }
503
+ const handle = visualSide == SelectionHandleDragSide.Start ? this.startHandle : this.endHandle;
504
+ if (handle === null) {
505
+ this.hasStationaryAnchor = false;
506
+ return false;
507
+ }
508
+ this.draggingSide = side;
509
+ this.draggingCapturedVisualSide = visualSide;
510
+ if (!ui.beginSelectionEndpointDrag(this.activeHandle, <u32>side)) {
511
+ this.draggingSide = SelectionHandleDragSide.None;
512
+ this.draggingCapturedVisualSide = SelectionHandleDragSide.None;
513
+ this.hasStationaryAnchor = false;
514
+ return false;
515
+ }
516
+ MobileTextSelectionToolbarManager.hideForHandleDrag();
517
+ handle._captureDragPointer();
518
+ this.setHandleHitTestVisible(visualSide, false);
519
+ this.hideHandle(visualSide);
520
+ this.showHandle(visualSide == SelectionHandleDragSide.Start ? SelectionHandleDragSide.End : SelectionHandleDragSide.Start);
521
+ return true;
522
+ }
523
+
524
+ private static semanticSideForVisualSide(visualSide: SelectionHandleDragSide): SelectionHandleDragSide {
525
+ if (visualSide == SelectionHandleDragSide.None) {
526
+ return SelectionHandleDragSide.None;
527
+ }
528
+ const forward = this.activeStart <= this.activeEnd;
529
+ if (visualSide == SelectionHandleDragSide.Start) {
530
+ return forward ? SelectionHandleDragSide.Start : SelectionHandleDragSide.End;
531
+ }
532
+ return forward ? SelectionHandleDragSide.End : SelectionHandleDragSide.Start;
533
+ }
534
+
535
+ private static endHandleDrag(): void {
536
+ const visualSide = this.draggingCapturedVisualSide;
537
+ const handle = visualSide == SelectionHandleDragSide.Start ? this.startHandle : this.endHandle;
538
+ if (handle !== null) {
539
+ handle._releaseDragPointer();
540
+ }
541
+ this.setHandleHitTestVisible(visualSide, true);
542
+ this.draggingSide = SelectionHandleDragSide.None;
543
+ this.draggingCapturedVisualSide = SelectionHandleDragSide.None;
544
+ this.hasStationaryAnchor = false;
545
+ if (this.activeStart == this.activeEnd) {
546
+ this.clear();
547
+ return;
548
+ }
549
+ this.refreshActiveGeometry();
550
+ this.showHandles();
551
+ MobileTextSelectionToolbarManager.showAfterHandleDrag(this.isVisible());
552
+ }
553
+
554
+ private static setHandleHitTestVisible(side: SelectionHandleDragSide, visible: bool): void {
555
+ const handle = side == SelectionHandleDragSide.Start ? this.startHandle : this.endHandle;
556
+ if (handle === null || handle.builtHandle == 0) {
557
+ return;
558
+ }
559
+ ui.setInteractive(handle.builtHandle, visible);
560
+ }
561
+
562
+ private static positionPlaceholderHandles(): void {
563
+ const startHandle = this.startHandle;
564
+ const endHandle = this.endHandle;
565
+ if (startHandle !== null) {
566
+ startHandle.position(0.0, 0.0);
567
+ }
568
+ if (endHandle !== null) {
569
+ endHandle.position(HIT_TARGET_SIZE + 8.0, 0.0);
570
+ }
571
+ }
572
+
573
+ private static positionRangeHandles(): bool {
574
+ const startHandle = this.startHandle;
575
+ const endHandle = this.endHandle;
576
+ if (startHandle === null || endHandle === null || this.activeHandle == 0) {
577
+ return false;
578
+ }
579
+ let rangeStart = this.activeStart;
580
+ let rangeEnd = this.activeEnd;
581
+ if (rangeEnd < rangeStart) {
582
+ const swap = rangeStart;
583
+ rangeStart = rangeEnd;
584
+ rangeEnd = swap;
585
+ }
586
+ const rects = ui.getTextRangeRects(this.activeHandle, rangeStart, rangeEnd);
587
+ if (rects.length == 0) {
588
+ return false;
589
+ }
590
+ const firstRect = unchecked(rects[0]);
591
+ const lastRect = unchecked(rects[rects.length - 1]);
592
+ if (this.draggingSide != SelectionHandleDragSide.None) {
593
+ this.positionDraggingRangeHandles(firstRect.x, firstRect.y + firstRect.height, lastRect.x + lastRect.width, lastRect.y + lastRect.height);
594
+ return true;
595
+ }
596
+ this.positionStartHandle(firstRect.x, firstRect.y + firstRect.height);
597
+ this.positionEndHandle(lastRect.x + lastRect.width, lastRect.y + lastRect.height);
598
+ return true;
599
+ }
600
+
601
+ private static positionDraggingRangeHandles(lowerX: f32, lowerY: f32, upperX: f32, upperY: f32): void {
602
+ const forward = this.activeStart <= this.activeEnd;
603
+ const draggingStart = this.draggingSide == SelectionHandleDragSide.Start;
604
+ const movingIsLower = draggingStart == forward;
605
+ let movingX = movingIsLower ? lowerX : upperX;
606
+ let movingY = movingIsLower ? lowerY : upperY;
607
+ let stationaryX = movingIsLower ? upperX : lowerX;
608
+ let stationaryY = movingIsLower ? upperY : lowerY;
609
+ if (this.hasStationaryAnchor) {
610
+ stationaryX = this.stationaryAnchorX;
611
+ stationaryY = this.stationaryAnchorY;
612
+ const lowerDistance = this.distanceSquared(lowerX, lowerY, stationaryX, stationaryY);
613
+ const upperDistance = this.distanceSquared(upperX, upperY, stationaryX, stationaryY);
614
+ if (upperDistance >= lowerDistance) {
615
+ movingX = upperX;
616
+ movingY = upperY;
617
+ } else {
618
+ movingX = lowerX;
619
+ movingY = lowerY;
620
+ }
621
+ }
622
+ if (this.draggingCapturedVisualSide == SelectionHandleDragSide.Start) {
623
+ this.positionStartHandle(movingX, movingY);
624
+ this.positionEndHandle(stationaryX, stationaryY);
625
+ } else {
626
+ this.positionStartHandle(stationaryX, stationaryY);
627
+ this.positionEndHandle(movingX, movingY);
628
+ }
629
+ }
630
+
631
+ private static distanceSquared(leftX: f32, leftY: f32, rightX: f32, rightY: f32): f32 {
632
+ const dx = leftX - rightX;
633
+ const dy = leftY - rightY;
634
+ return dx * dx + dy * dy;
635
+ }
636
+
637
+ private static positionCrossSelectionHandles(): bool {
638
+ const startHandle = this.startHandle;
639
+ const endHandle = this.endHandle;
640
+ if (startHandle === null || endHandle === null || this.activeHandle == 0) {
641
+ return false;
642
+ }
643
+ const endpointRects = ui.getCrossSelectionEndpointRects(this.activeHandle);
644
+ if (endpointRects === null) {
645
+ return false;
646
+ }
647
+ const startRect = endpointRects.start;
648
+ const endRect = endpointRects.end;
649
+ this.positionStartHandle(startRect.x, startRect.y + startRect.height);
650
+ this.positionEndHandle(endRect.x + endRect.width, endRect.y + endRect.height);
651
+ return true;
652
+ }
653
+
654
+ private static positionStartHandle(anchorX: f32, anchorY: f32): void {
655
+ const startHandle = this.startHandle;
656
+ if (startHandle === null) {
657
+ return;
658
+ }
659
+ this.startAnchorX = anchorX;
660
+ this.startAnchorY = anchorY;
661
+ this.hasStartAnchor = true;
662
+ startHandle.position(anchorX - START_STEM_X, anchorY - HIT_TARGET_PADDING);
663
+ }
664
+
665
+ private static positionEndHandle(anchorX: f32, anchorY: f32): void {
666
+ const endHandle = this.endHandle;
667
+ if (endHandle === null) {
668
+ return;
669
+ }
670
+ this.endAnchorX = anchorX;
671
+ this.endAnchorY = anchorY;
672
+ this.hasEndAnchor = true;
673
+ endHandle.position(anchorX - END_STEM_X, anchorY - HIT_TARGET_PADDING);
674
+ }
675
+
676
+ private static captureStationaryAnchor(visualSide: SelectionHandleDragSide): bool {
677
+ if (visualSide == SelectionHandleDragSide.Start) {
678
+ if (!this.hasEndAnchor) {
679
+ return false;
680
+ }
681
+ this.stationaryAnchorX = this.endAnchorX;
682
+ this.stationaryAnchorY = this.endAnchorY;
683
+ this.hasStationaryAnchor = true;
684
+ return true;
685
+ }
686
+ if (visualSide == SelectionHandleDragSide.End) {
687
+ if (!this.hasStartAnchor) {
688
+ return false;
689
+ }
690
+ this.stationaryAnchorX = this.startAnchorX;
691
+ this.stationaryAnchorY = this.startAnchorY;
692
+ this.hasStationaryAnchor = true;
693
+ return true;
694
+ }
695
+ return false;
696
+ }
697
+ }