@effindomv2/fui-as 0.1.37 → 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 +23 -13
- package/scripts/build.sh +47 -46
- 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 +65 -5
- package/src/FuiExports.ts +12 -1
- package/src/bindings/ui.ts +157 -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 +17 -8
- package/src/core/Bitmap.ts +192 -3
- 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 +104 -5
- 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 -865
- 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 +17 -1
- package/src/drawing/DrawContext.ts +140 -32
- package/src/nodes/CustomDrawable.ts +8 -1
- package/src/nodes/FlexBox.ts +40 -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 +1 -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-canvas-host.ts +0 -72
- 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 -1472
- 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 -206
- package/browser/src/common-harness/ui-chrome.ts +0 -206
- package/browser/src/common-harness/ui-imports.ts +0 -559
- 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
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as ui from "../../bindings/ui";
|
|
2
2
|
import { HandlerAction } from "../../core/Action";
|
|
3
|
-
import { Callback1,
|
|
3
|
+
import { Callback1, Handler1 } from "../../core/BoundCallback";
|
|
4
4
|
import { Disposable, disposeAll } from "../../core/Disposable";
|
|
5
5
|
import { FocusAdornerManager } from "../../core/FocusAdornerManager";
|
|
6
6
|
import { keyboardFocusVisible } from "../../core/FocusVisibility";
|
|
7
|
-
import { Node } from "../../core/Node";
|
|
7
|
+
import { FocusChangedEventArgs, Node, SelectionChangedEventArgs, TextChangedEventArgs } from "../../core/Node";
|
|
8
8
|
import {
|
|
9
|
-
BorderStyle,
|
|
10
9
|
CursorStyle,
|
|
11
10
|
HandleValue,
|
|
11
|
+
KeyEventType,
|
|
12
|
+
KeyModifier,
|
|
12
13
|
PointerEventType,
|
|
13
14
|
SemanticRole,
|
|
14
15
|
TextVerticalAlign,
|
|
@@ -18,7 +19,7 @@ import { Theme, activeTheme } from "../../core/Theme";
|
|
|
18
19
|
import { FontFamily } from "../../core/Typography";
|
|
19
20
|
import { PersistedStringCodec, PersistedValueState } from "../../core/PersistedState";
|
|
20
21
|
import { FlexBox, ScrollBarVisibility, ScrollBox, TextCore } from "../../nodes";
|
|
21
|
-
import { bind1
|
|
22
|
+
import { bind1 } from "../../core/bind";
|
|
22
23
|
import { ScrollState } from "../../nodes/ScrollState";
|
|
23
24
|
import { ScrollView } from "../../nodes/ScrollView";
|
|
24
25
|
import { getControlTemplates } from "../ControlTemplateSet";
|
|
@@ -42,6 +43,93 @@ function utf8ByteLength(text: string): u32 {
|
|
|
42
43
|
return <u32>bytes.length;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
function utf8CodePointByteLength(codePoint: u32): u32 {
|
|
47
|
+
if (codePoint <= 0x7F) {
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
if (codePoint <= 0x7FF) {
|
|
51
|
+
return 2;
|
|
52
|
+
}
|
|
53
|
+
if (codePoint <= 0xFFFF) {
|
|
54
|
+
return 3;
|
|
55
|
+
}
|
|
56
|
+
return 4;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readCodePoint(text: string, index: i32): u32 {
|
|
60
|
+
const first = <u32>text.charCodeAt(index);
|
|
61
|
+
if (first < 0xD800 || first > 0xDBFF || index + 1 >= text.length) {
|
|
62
|
+
return first;
|
|
63
|
+
}
|
|
64
|
+
const second = <u32>text.charCodeAt(index + 1);
|
|
65
|
+
if (second < 0xDC00 || second > 0xDFFF) {
|
|
66
|
+
return first;
|
|
67
|
+
}
|
|
68
|
+
return 0x10000 + ((first - 0xD800) << 10) + (second - 0xDC00);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function codePointStringLength(codePoint: u32): i32 {
|
|
72
|
+
return codePoint > 0xFFFF ? 2 : 1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function codePointCount(text: string): u32 {
|
|
76
|
+
let count: u32 = 0;
|
|
77
|
+
let cursor = 0;
|
|
78
|
+
while (cursor < text.length) {
|
|
79
|
+
const codePoint = readCodePoint(text, cursor);
|
|
80
|
+
cursor += codePointStringLength(codePoint);
|
|
81
|
+
count += 1;
|
|
82
|
+
}
|
|
83
|
+
return count;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function utf8ByteOffsetFromCodePointIndex(text: string, index: u32): u32 {
|
|
87
|
+
const countLimit = codePointCount(text);
|
|
88
|
+
const clampedIndex = index > countLimit ? countLimit : index;
|
|
89
|
+
let byteOffset: u32 = 0;
|
|
90
|
+
let cursor = 0;
|
|
91
|
+
let count: u32 = 0;
|
|
92
|
+
while (cursor < text.length && count < clampedIndex) {
|
|
93
|
+
const codePoint = readCodePoint(text, cursor);
|
|
94
|
+
byteOffset += utf8CodePointByteLength(codePoint);
|
|
95
|
+
cursor += codePointStringLength(codePoint);
|
|
96
|
+
count += 1;
|
|
97
|
+
}
|
|
98
|
+
return byteOffset;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function stringOffsetFromCodePointIndex(text: string, index: u32): i32 {
|
|
102
|
+
const countLimit = codePointCount(text);
|
|
103
|
+
const clampedIndex = index > countLimit ? countLimit : index;
|
|
104
|
+
let cursor = 0;
|
|
105
|
+
let count: u32 = 0;
|
|
106
|
+
while (cursor < text.length && count < clampedIndex) {
|
|
107
|
+
const codePoint = readCodePoint(text, cursor);
|
|
108
|
+
cursor += codePointStringLength(codePoint);
|
|
109
|
+
count += 1;
|
|
110
|
+
}
|
|
111
|
+
return cursor;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function codePointIndexFromUtf8ByteOffset(text: string, byteOffset: u32): u32 {
|
|
115
|
+
const length = utf8ByteLength(text);
|
|
116
|
+
const clampedOffset = byteOffset > length ? length : byteOffset;
|
|
117
|
+
let bytes: u32 = 0;
|
|
118
|
+
let cursor = 0;
|
|
119
|
+
let count: u32 = 0;
|
|
120
|
+
while (cursor < text.length) {
|
|
121
|
+
const codePoint = readCodePoint(text, cursor);
|
|
122
|
+
const byteLength = utf8CodePointByteLength(codePoint);
|
|
123
|
+
if (bytes + byteLength > clampedOffset) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
bytes += byteLength;
|
|
127
|
+
cursor += codePointStringLength(codePoint);
|
|
128
|
+
count += 1;
|
|
129
|
+
}
|
|
130
|
+
return count;
|
|
131
|
+
}
|
|
132
|
+
|
|
45
133
|
export class TextInputProfile {
|
|
46
134
|
constructor(
|
|
47
135
|
readonly maxLines: i32,
|
|
@@ -109,6 +197,16 @@ class TextInputEditorText extends TextCore {
|
|
|
109
197
|
super._handleSelectionChanged(start, end);
|
|
110
198
|
this.owner.handleEditorSelectionChanged(start, end);
|
|
111
199
|
}
|
|
200
|
+
|
|
201
|
+
_handleKeyEvent(eventType: KeyEventType, key: string, modifiers: u32): bool {
|
|
202
|
+
if (super._handleKeyEvent(eventType, key, modifiers)) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
if (eventType == KeyEventType.Down && this.owner.handleEditorKeyDown(key, modifiers)) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
112
210
|
}
|
|
113
211
|
|
|
114
212
|
class TextInputPlaceholderHost extends FlexBox {
|
|
@@ -163,9 +261,13 @@ export class TextInputCore extends FlexBox {
|
|
|
163
261
|
private placeholderValue: string = "";
|
|
164
262
|
private selectionStartValue: u32 = 0;
|
|
165
263
|
private selectionEndValue: u32 = 0;
|
|
264
|
+
private selectionStartByteValue: u32 = 0;
|
|
265
|
+
private selectionEndByteValue: u32 = 0;
|
|
166
266
|
private focusedState: bool = false;
|
|
167
267
|
private readOnlyValue: bool = false;
|
|
168
268
|
private passwordValue: bool = false;
|
|
269
|
+
private acceptsTabValue: bool = false;
|
|
270
|
+
private hostAutofillHintValue: string | null = null;
|
|
169
271
|
private maxCharsValue: i32 = UNLIMITED_TEXT_LENGTH;
|
|
170
272
|
private wrappingValue: bool;
|
|
171
273
|
private verticalScrollbarVisibilityValue: ScrollBarVisibility = ScrollBarVisibility.Auto;
|
|
@@ -173,12 +275,12 @@ export class TextInputCore extends FlexBox {
|
|
|
173
275
|
private fontFamilyOverride: FontFamily | null = null;
|
|
174
276
|
private fontSizeOverride: f32 = 0.0;
|
|
175
277
|
private hasFontSizeOverride: bool = false;
|
|
176
|
-
private changedCb: ((
|
|
177
|
-
private changedBinding: Callback1<
|
|
178
|
-
private selectionChangedCb: ((
|
|
179
|
-
private selectionChangedBinding:
|
|
180
|
-
private focusChangedListener: ((
|
|
181
|
-
private textInputFocusChangedBinding: Callback1<
|
|
278
|
+
private changedCb: ((event: TextChangedEventArgs) => void) | null = null;
|
|
279
|
+
private changedBinding: Callback1<TextChangedEventArgs> | null = null;
|
|
280
|
+
private selectionChangedCb: ((event: SelectionChangedEventArgs) => void) | null = null;
|
|
281
|
+
private selectionChangedBinding: Callback1<SelectionChangedEventArgs> | null = null;
|
|
282
|
+
private focusChangedListener: ((event: FocusChangedEventArgs) => void) | null = null;
|
|
283
|
+
private textInputFocusChangedBinding: Callback1<FocusChangedEventArgs> | null = null;
|
|
182
284
|
private templateValue: TextInputTemplate | null = null;
|
|
183
285
|
private colorsValue: TextInputColors | null = null;
|
|
184
286
|
|
|
@@ -186,6 +288,12 @@ export class TextInputCore extends FlexBox {
|
|
|
186
288
|
super();
|
|
187
289
|
this.profile = profile;
|
|
188
290
|
this.textValue = text;
|
|
291
|
+
const initialCaret = codePointCount(text);
|
|
292
|
+
const initialCaretByte = utf8ByteLength(text);
|
|
293
|
+
this.selectionStartValue = initialCaret;
|
|
294
|
+
this.selectionEndValue = initialCaret;
|
|
295
|
+
this.selectionStartByteValue = initialCaretByte;
|
|
296
|
+
this.selectionEndByteValue = initialCaretByte;
|
|
189
297
|
this.wrappingValue = profile.wrapsByDefault;
|
|
190
298
|
this.editorText = new TextInputEditorText(this, text);
|
|
191
299
|
this.placeholderText = new TextCore("");
|
|
@@ -213,6 +321,7 @@ export class TextInputCore extends FlexBox {
|
|
|
213
321
|
this.syncSemanticLabel();
|
|
214
322
|
this.syncEditorLimits();
|
|
215
323
|
this.syncEditorEditability();
|
|
324
|
+
this.syncBrowserInputMetadata();
|
|
216
325
|
this.syncThemeState(activeTheme.value);
|
|
217
326
|
this.syncPlaceholderVisibility();
|
|
218
327
|
this.persistState(TEXT_INPUT_PERSISTED_STATE);
|
|
@@ -234,6 +343,14 @@ export class TextInputCore extends FlexBox {
|
|
|
234
343
|
return this.selectionEndValue;
|
|
235
344
|
}
|
|
236
345
|
|
|
346
|
+
get selectionStartByteOffset(): u32 {
|
|
347
|
+
return this.selectionStartByteValue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
get selectionEndByteOffset(): u32 {
|
|
351
|
+
return this.selectionEndByteValue;
|
|
352
|
+
}
|
|
353
|
+
|
|
237
354
|
get isFocused(): bool {
|
|
238
355
|
return this.focusedState;
|
|
239
356
|
}
|
|
@@ -266,13 +383,43 @@ export class TextInputCore extends FlexBox {
|
|
|
266
383
|
|
|
267
384
|
text(value: string): this {
|
|
268
385
|
this.textValue = value;
|
|
269
|
-
this.clampSelectionToText();
|
|
270
386
|
this.editorText.text(value);
|
|
387
|
+
this.caretStringIndex(codePointCount(value));
|
|
271
388
|
this.syncSemanticLabel();
|
|
272
389
|
this.syncPlaceholderVisibility();
|
|
273
390
|
return this;
|
|
274
391
|
}
|
|
275
392
|
|
|
393
|
+
selectionRange(start: u32, end: u32): this {
|
|
394
|
+
const length = codePointCount(this.textValue);
|
|
395
|
+
const clampedStart = clampSelection(start, <i32>length);
|
|
396
|
+
const clampedEnd = clampSelection(end, <i32>length);
|
|
397
|
+
const clampedStartByte = utf8ByteOffsetFromCodePointIndex(this.textValue, clampedStart);
|
|
398
|
+
const clampedEndByte = utf8ByteOffsetFromCodePointIndex(this.textValue, clampedEnd);
|
|
399
|
+
this.selectionStartValue = clampedStart;
|
|
400
|
+
this.selectionEndValue = clampedEnd;
|
|
401
|
+
this.selectionStartByteValue = clampedStartByte;
|
|
402
|
+
this.selectionEndByteValue = clampedEndByte;
|
|
403
|
+
const handle = this.editorText.builtHandle;
|
|
404
|
+
if (handle != <u64>HandleValue.Invalid) {
|
|
405
|
+
ui.setTextSelectionRange(handle, clampedStartByte, clampedEndByte);
|
|
406
|
+
}
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
caret(position: u32): this {
|
|
411
|
+
return this.caretStringIndex(position);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
caretToEnd(): this {
|
|
415
|
+
const end = codePointCount(this.textValue);
|
|
416
|
+
return this.caretStringIndex(end);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private caretStringIndex(position: u32): this {
|
|
420
|
+
return this.selectionRange(position, position);
|
|
421
|
+
}
|
|
422
|
+
|
|
276
423
|
placeholder(value: string): this {
|
|
277
424
|
this.placeholderValue = value;
|
|
278
425
|
this.placeholderText.text(value);
|
|
@@ -293,65 +440,78 @@ export class TextInputCore extends FlexBox {
|
|
|
293
440
|
return this;
|
|
294
441
|
}
|
|
295
442
|
|
|
443
|
+
acceptsTab(flag: bool = true): this {
|
|
444
|
+
this.acceptsTabValue = flag;
|
|
445
|
+
this.editorText._editorAcceptsTab(flag);
|
|
446
|
+
return this;
|
|
447
|
+
}
|
|
448
|
+
|
|
296
449
|
password(flag: bool = true): this {
|
|
297
450
|
this.passwordValue = flag;
|
|
298
451
|
this.editorText.obscured(flag);
|
|
452
|
+
this.syncBrowserInputMetadata();
|
|
299
453
|
this.syncSemanticLabel();
|
|
300
454
|
return this;
|
|
301
455
|
}
|
|
302
456
|
|
|
303
|
-
|
|
457
|
+
hostAutofill(hint: string | null = null): this {
|
|
458
|
+
this.hostAutofillHintValue = hint !== null && hint.length > 0 ? hint : null;
|
|
459
|
+
this.syncBrowserInputMetadata();
|
|
460
|
+
return this;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
onChanged(cb: (event: TextChangedEventArgs) => void): this {
|
|
304
464
|
this.changedCb = cb;
|
|
305
465
|
this.changedBinding = null;
|
|
306
466
|
return this;
|
|
307
467
|
}
|
|
308
468
|
|
|
309
|
-
bindChanged<Owner>(owner: Owner, handler: Handler1<Owner,
|
|
469
|
+
bindChanged<Owner>(owner: Owner, handler: Handler1<Owner, TextChangedEventArgs>): this {
|
|
310
470
|
this.changedCb = null;
|
|
311
|
-
this.changedBinding = bind1<Owner,
|
|
471
|
+
this.changedBinding = bind1<Owner, TextChangedEventArgs>(owner, handler);
|
|
312
472
|
return this;
|
|
313
473
|
}
|
|
314
474
|
|
|
315
|
-
onChangedWith<Owner>(owner: Owner, handler: Handler1<Owner,
|
|
475
|
+
onChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, TextChangedEventArgs>): this {
|
|
316
476
|
this.bindChanged(owner, handler);
|
|
317
477
|
return this;
|
|
318
478
|
}
|
|
319
479
|
|
|
320
|
-
onTextChanged(cb: (
|
|
480
|
+
onTextChanged(cb: (event: TextChangedEventArgs) => void): this {
|
|
321
481
|
this.onChanged(cb);
|
|
322
482
|
return this;
|
|
323
483
|
}
|
|
324
484
|
|
|
325
|
-
onSelectionChanged(cb: (
|
|
485
|
+
onSelectionChanged(cb: (event: SelectionChangedEventArgs) => void): this {
|
|
326
486
|
this.selectionChangedCb = cb;
|
|
327
487
|
this.selectionChangedBinding = null;
|
|
328
488
|
return this;
|
|
329
489
|
}
|
|
330
490
|
|
|
331
|
-
bindSelectionChanged<Owner>(owner: Owner, handler:
|
|
491
|
+
bindSelectionChanged<Owner>(owner: Owner, handler: Handler1<Owner, SelectionChangedEventArgs>): this {
|
|
332
492
|
this.selectionChangedCb = null;
|
|
333
|
-
this.selectionChangedBinding =
|
|
493
|
+
this.selectionChangedBinding = bind1<Owner, SelectionChangedEventArgs>(owner, handler);
|
|
334
494
|
return this;
|
|
335
495
|
}
|
|
336
496
|
|
|
337
|
-
onSelectionChangedWith<Owner>(owner: Owner, handler:
|
|
497
|
+
onSelectionChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, SelectionChangedEventArgs>): this {
|
|
338
498
|
this.bindSelectionChanged(owner, handler);
|
|
339
499
|
return this;
|
|
340
500
|
}
|
|
341
501
|
|
|
342
|
-
onFocusChanged(cb: (
|
|
502
|
+
onFocusChanged(cb: (event: FocusChangedEventArgs) => void): this {
|
|
343
503
|
this.focusChangedListener = cb;
|
|
344
504
|
this.textInputFocusChangedBinding = null;
|
|
345
505
|
return this;
|
|
346
506
|
}
|
|
347
507
|
|
|
348
|
-
bindFocusChanged<Owner>(owner: Owner, handler: Handler1<Owner,
|
|
508
|
+
bindFocusChanged<Owner>(owner: Owner, handler: Handler1<Owner, FocusChangedEventArgs>): this {
|
|
349
509
|
this.focusChangedListener = null;
|
|
350
|
-
this.textInputFocusChangedBinding = bind1<Owner,
|
|
510
|
+
this.textInputFocusChangedBinding = bind1<Owner, FocusChangedEventArgs>(owner, handler);
|
|
351
511
|
return this;
|
|
352
512
|
}
|
|
353
513
|
|
|
354
|
-
onFocusChangedWith<Owner>(owner: Owner, handler: Handler1<Owner,
|
|
514
|
+
onFocusChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, FocusChangedEventArgs>): this {
|
|
355
515
|
this.bindFocusChanged(owner, handler);
|
|
356
516
|
return this;
|
|
357
517
|
}
|
|
@@ -382,6 +542,7 @@ export class TextInputCore extends FlexBox {
|
|
|
382
542
|
|
|
383
543
|
focusNow(): this {
|
|
384
544
|
this.focusEditor();
|
|
545
|
+
this.selectionByteRange(this.selectionStartByteValue, this.selectionEndByteValue);
|
|
385
546
|
return this;
|
|
386
547
|
}
|
|
387
548
|
|
|
@@ -420,8 +581,12 @@ export class TextInputCore extends FlexBox {
|
|
|
420
581
|
if (this.builtHandle != <u64>HandleValue.Invalid) {
|
|
421
582
|
return this.builtHandle;
|
|
422
583
|
}
|
|
584
|
+
const intendedSelectionStartByte = this.selectionStartByteValue;
|
|
585
|
+
const intendedSelectionEndByte = this.selectionEndByteValue;
|
|
423
586
|
const handle = super.build();
|
|
424
587
|
ui.setSelectionAreaBarrier(handle, true);
|
|
588
|
+
this.syncBrowserInputMetadata();
|
|
589
|
+
this.selectionByteRange(intendedSelectionStartByte, intendedSelectionEndByte);
|
|
425
590
|
return handle;
|
|
426
591
|
}
|
|
427
592
|
|
|
@@ -524,7 +689,7 @@ export class TextInputCore extends FlexBox {
|
|
|
524
689
|
this.bgColor(colors.backgroundColor);
|
|
525
690
|
}
|
|
526
691
|
if (colors !== null && colors.hasBorder) {
|
|
527
|
-
this.border(1.0, colors.borderColor
|
|
692
|
+
this.border(1.0, colors.borderColor);
|
|
528
693
|
}
|
|
529
694
|
|
|
530
695
|
this.syncFocusChrome(theme);
|
|
@@ -537,17 +702,21 @@ export class TextInputCore extends FlexBox {
|
|
|
537
702
|
this.focusedState = focused;
|
|
538
703
|
this.syncFocusChrome(activeTheme.value);
|
|
539
704
|
this.syncPlaceholderVisibility();
|
|
705
|
+
const event = new FocusChangedEventArgs(focused);
|
|
540
706
|
const callback = this.focusChangedListener;
|
|
541
707
|
if (callback !== null) {
|
|
542
|
-
callback(
|
|
708
|
+
callback(event);
|
|
543
709
|
}
|
|
544
710
|
const binding = this.textInputFocusChangedBinding;
|
|
545
711
|
if (binding !== null) {
|
|
546
|
-
binding.invoke(
|
|
712
|
+
binding.invoke(event);
|
|
547
713
|
}
|
|
548
714
|
}
|
|
549
715
|
|
|
550
716
|
handleEditorTextChanged(value: string): void {
|
|
717
|
+
if (this.textValue == value) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
551
720
|
this.textValue = value;
|
|
552
721
|
this.clampSelectionToText();
|
|
553
722
|
this.syncSemanticLabel();
|
|
@@ -555,27 +724,97 @@ export class TextInputCore extends FlexBox {
|
|
|
555
724
|
this.emitTextChanged(value);
|
|
556
725
|
}
|
|
557
726
|
|
|
727
|
+
handleEditorKeyDown(key: string, modifiers: u32): bool {
|
|
728
|
+
if (!this.isEnabled) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
const nonShiftModifiers = modifiers & (<u32>KeyModifier.Ctrl | <u32>KeyModifier.Alt | <u32>KeyModifier.Meta);
|
|
732
|
+
if (
|
|
733
|
+
nonShiftModifiers == 0 &&
|
|
734
|
+
(
|
|
735
|
+
key == "ArrowLeft" ||
|
|
736
|
+
key == "ArrowRight" ||
|
|
737
|
+
key == "ArrowUp" ||
|
|
738
|
+
key == "ArrowDown" ||
|
|
739
|
+
key == "Home" ||
|
|
740
|
+
key == "End" ||
|
|
741
|
+
key == "PageUp" ||
|
|
742
|
+
key == "PageDown"
|
|
743
|
+
)
|
|
744
|
+
) {
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
if (this.acceptsTabValue && key == "Tab" && modifiers == 0 && !this.readOnlyValue) {
|
|
748
|
+
this.replaceSelectionWithText("\t");
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private replaceSelectionWithText(inserted: string): void {
|
|
755
|
+
const text = this.textValue;
|
|
756
|
+
const start = this.selectionStartValue < this.selectionEndValue ? this.selectionStartValue : this.selectionEndValue;
|
|
757
|
+
const end = this.selectionStartValue > this.selectionEndValue ? this.selectionStartValue : this.selectionEndValue;
|
|
758
|
+
const currentLength = codePointCount(text);
|
|
759
|
+
const insertedLength = codePointCount(inserted);
|
|
760
|
+
const replacementLength = end - start;
|
|
761
|
+
if (currentLength - replacementLength + insertedLength > <u32>this.maxCharsValue) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const startOffset = stringOffsetFromCodePointIndex(text, start);
|
|
765
|
+
const endOffset = stringOffsetFromCodePointIndex(text, end);
|
|
766
|
+
const value = text.substring(0, startOffset) + inserted + text.substring(endOffset);
|
|
767
|
+
const caret = start + insertedLength;
|
|
768
|
+
this.textValue = value;
|
|
769
|
+
const handle = this.editorText.builtHandle;
|
|
770
|
+
if (handle != 0) {
|
|
771
|
+
const startByte = utf8ByteOffsetFromCodePointIndex(text, start);
|
|
772
|
+
const endByte = utf8ByteOffsetFromCodePointIndex(text, end);
|
|
773
|
+
const caretByte = utf8ByteOffsetFromCodePointIndex(value, caret);
|
|
774
|
+
ui.replaceTextRange(handle, startByte, endByte, inserted, caretByte);
|
|
775
|
+
this.selectionStartValue = caret;
|
|
776
|
+
this.selectionEndValue = caret;
|
|
777
|
+
this.selectionStartByteValue = caretByte;
|
|
778
|
+
this.selectionEndByteValue = caretByte;
|
|
779
|
+
} else {
|
|
780
|
+
this.editorText.text(value);
|
|
781
|
+
this.selectionRange(caret, caret);
|
|
782
|
+
}
|
|
783
|
+
this.syncSemanticLabel();
|
|
784
|
+
this.syncPlaceholderVisibility();
|
|
785
|
+
this.emitTextChanged(value);
|
|
786
|
+
this.emitSelectionChanged();
|
|
787
|
+
}
|
|
788
|
+
|
|
558
789
|
private emitTextChanged(value: string): void {
|
|
790
|
+
const event = new TextChangedEventArgs(value);
|
|
559
791
|
const callback = this.changedCb;
|
|
560
792
|
if (callback !== null) {
|
|
561
|
-
callback(
|
|
793
|
+
callback(event);
|
|
562
794
|
}
|
|
563
795
|
const binding = this.changedBinding;
|
|
564
796
|
if (binding !== null) {
|
|
565
|
-
binding.invoke(
|
|
797
|
+
binding.invoke(event);
|
|
566
798
|
}
|
|
567
799
|
}
|
|
568
800
|
|
|
569
801
|
handleEditorSelectionChanged(start: u32, end: u32): void {
|
|
570
|
-
this.
|
|
571
|
-
this.
|
|
802
|
+
this.selectionStartByteValue = start;
|
|
803
|
+
this.selectionEndByteValue = end;
|
|
804
|
+
this.selectionStartValue = codePointIndexFromUtf8ByteOffset(this.textValue, start);
|
|
805
|
+
this.selectionEndValue = codePointIndexFromUtf8ByteOffset(this.textValue, end);
|
|
806
|
+
this.emitSelectionChanged();
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
private emitSelectionChanged(): void {
|
|
810
|
+
const event = new SelectionChangedEventArgs(this.selectionStartValue, this.selectionEndValue);
|
|
572
811
|
const callback = this.selectionChangedCb;
|
|
573
812
|
if (callback !== null) {
|
|
574
|
-
callback(
|
|
813
|
+
callback(event);
|
|
575
814
|
}
|
|
576
815
|
const binding = this.selectionChangedBinding;
|
|
577
816
|
if (binding !== null) {
|
|
578
|
-
binding.invoke(
|
|
817
|
+
binding.invoke(event);
|
|
579
818
|
}
|
|
580
819
|
}
|
|
581
820
|
|
|
@@ -588,6 +827,14 @@ export class TextInputCore extends FlexBox {
|
|
|
588
827
|
this.editorText.editable(!this.readOnlyValue && this.isEnabled);
|
|
589
828
|
}
|
|
590
829
|
|
|
830
|
+
private syncBrowserInputMetadata(): void {
|
|
831
|
+
const handle = this.editorText.builtHandle;
|
|
832
|
+
if (handle == <u64>HandleValue.Invalid) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
ui.registerTextInputMetadata(handle, this.passwordValue, this.hostAutofillHintValue);
|
|
836
|
+
}
|
|
837
|
+
|
|
591
838
|
private syncSemanticLabel(): void {
|
|
592
839
|
if (this.placeholderValue.length > 0) {
|
|
593
840
|
this.editorText._defaultSemanticLabel(this.placeholderValue);
|
|
@@ -618,9 +865,26 @@ export class TextInputCore extends FlexBox {
|
|
|
618
865
|
}
|
|
619
866
|
|
|
620
867
|
private clampSelectionToText(): void {
|
|
621
|
-
const length = this.textValue
|
|
622
|
-
this.selectionStartValue = clampSelection(this.selectionStartValue, length);
|
|
623
|
-
this.selectionEndValue = clampSelection(this.selectionEndValue, length);
|
|
868
|
+
const length = codePointCount(this.textValue);
|
|
869
|
+
this.selectionStartValue = clampSelection(this.selectionStartValue, <i32>length);
|
|
870
|
+
this.selectionEndValue = clampSelection(this.selectionEndValue, <i32>length);
|
|
871
|
+
this.selectionStartByteValue = utf8ByteOffsetFromCodePointIndex(this.textValue, this.selectionStartValue);
|
|
872
|
+
this.selectionEndByteValue = utf8ByteOffsetFromCodePointIndex(this.textValue, this.selectionEndValue);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
private selectionByteRange(start: u32, end: u32): this {
|
|
876
|
+
const length = utf8ByteLength(this.textValue);
|
|
877
|
+
const clampedStart = clampSelection(start, <i32>length);
|
|
878
|
+
const clampedEnd = clampSelection(end, <i32>length);
|
|
879
|
+
this.selectionStartByteValue = clampedStart;
|
|
880
|
+
this.selectionEndByteValue = clampedEnd;
|
|
881
|
+
this.selectionStartValue = codePointIndexFromUtf8ByteOffset(this.textValue, clampedStart);
|
|
882
|
+
this.selectionEndValue = codePointIndexFromUtf8ByteOffset(this.textValue, clampedEnd);
|
|
883
|
+
const handle = this.editorText.builtHandle;
|
|
884
|
+
if (handle != <u64>HandleValue.Invalid) {
|
|
885
|
+
ui.setTextSelectionRange(handle, clampedStart, clampedEnd);
|
|
886
|
+
}
|
|
887
|
+
return this;
|
|
624
888
|
}
|
|
625
889
|
|
|
626
890
|
focusEditor(): void {
|
|
@@ -635,7 +899,11 @@ export class TextInputCore extends FlexBox {
|
|
|
635
899
|
return;
|
|
636
900
|
}
|
|
637
901
|
if (!this.profile.multiline) {
|
|
638
|
-
this.
|
|
902
|
+
if (this.focusedState) {
|
|
903
|
+
this.focusEditor();
|
|
904
|
+
} else {
|
|
905
|
+
this.focusNow();
|
|
906
|
+
}
|
|
639
907
|
return;
|
|
640
908
|
}
|
|
641
909
|
this.handleViewportPointerDown();
|
|
@@ -645,13 +913,11 @@ export class TextInputCore extends FlexBox {
|
|
|
645
913
|
if (!this.isEnabled) {
|
|
646
914
|
return;
|
|
647
915
|
}
|
|
648
|
-
|
|
649
|
-
if (handle == <u64>HandleValue.Invalid) {
|
|
916
|
+
if (this.focusedState) {
|
|
650
917
|
this.focusEditor();
|
|
651
918
|
return;
|
|
652
919
|
}
|
|
653
|
-
|
|
654
|
-
ui.setTextSelectionRange(handle, documentEnd, documentEnd);
|
|
920
|
+
this.focusNow();
|
|
655
921
|
}
|
|
656
922
|
|
|
657
923
|
private syncEditorWrapping(): void {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CursorStyle, Unit } from "../../core/ffi";
|
|
2
2
|
import { Node } from "../../core/Node";
|
|
3
3
|
import { Theme } from "../../core/Theme";
|
|
4
4
|
import { FlexBox } from "../../nodes";
|
|
@@ -54,7 +54,7 @@ class DefaultTextInputPresenter extends TextInputPresenter {
|
|
|
54
54
|
this.host
|
|
55
55
|
.bgColor(bg)
|
|
56
56
|
.cornerRadius(theme.spacing.sm)
|
|
57
|
-
.border(1.0, borderColor
|
|
57
|
+
.border(1.0, borderColor)
|
|
58
58
|
.padding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
|
|
59
59
|
.cursor(shellCursor);
|
|
60
60
|
this.host.opacity(state.enabled ? 1.0 : 0.6);
|
|
@@ -2,6 +2,8 @@ export {
|
|
|
2
2
|
ButtonPresenter,
|
|
3
3
|
ButtonTemplate,
|
|
4
4
|
ButtonVisualState,
|
|
5
|
+
createDefaultButtonPresenter,
|
|
6
|
+
defaultButtonTemplate,
|
|
5
7
|
} from "./internal/ButtonPresenter";
|
|
6
8
|
export {
|
|
7
9
|
ControlTemplateSet,
|
|
@@ -14,22 +16,30 @@ export {
|
|
|
14
16
|
CheckboxIndicatorPresenter,
|
|
15
17
|
CheckboxIndicatorTemplate,
|
|
16
18
|
CheckboxIndicatorVisualState,
|
|
19
|
+
createDefaultCheckboxIndicatorPresenter,
|
|
20
|
+
defaultCheckboxIndicatorTemplate,
|
|
17
21
|
} from "./internal/CheckboxIndicatorPresenter";
|
|
18
22
|
export {
|
|
19
23
|
RadioIndicatorPresenter,
|
|
20
24
|
RadioIndicatorTemplate,
|
|
21
25
|
RadioIndicatorVisualState,
|
|
26
|
+
createDefaultRadioIndicatorPresenter,
|
|
27
|
+
defaultRadioIndicatorTemplate,
|
|
22
28
|
} from "./internal/RadioIndicatorPresenter";
|
|
23
29
|
export {
|
|
24
30
|
SwitchIndicatorPresenter,
|
|
25
31
|
SwitchIndicatorTemplate,
|
|
26
32
|
SwitchIndicatorVisualState,
|
|
33
|
+
createDefaultSwitchIndicatorPresenter,
|
|
34
|
+
defaultSwitchIndicatorTemplate,
|
|
27
35
|
} from "./internal/SwitchIndicatorPresenter";
|
|
28
36
|
export {
|
|
29
37
|
SliderPresenter,
|
|
30
38
|
SliderPresenterMetrics,
|
|
31
39
|
SliderTemplate,
|
|
32
40
|
SliderVisualState,
|
|
41
|
+
createDefaultSliderPresenter,
|
|
42
|
+
defaultSliderTemplate,
|
|
33
43
|
} from "./internal/SliderPresenter";
|
|
34
44
|
export {
|
|
35
45
|
DropdownFieldMetrics,
|
package/src/core/Application.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { ContextMenuManager } from "./ContextMenuManager";
|
|
|
3
3
|
import { disposeAllFetchRequests } from "./Fetch";
|
|
4
4
|
import { disposeAllFileRequests } from "./File";
|
|
5
5
|
import { FocusAdornerManager } from "./FocusAdornerManager";
|
|
6
|
+
import { MobileTextSelectionToolbarManager } from "./MobileTextSelectionToolbarManager";
|
|
7
|
+
import { SelectionHandleAdornerManager } from "./SelectionHandleAdornerManager";
|
|
6
8
|
import { ToolTipManager } from "./ToolTipManager";
|
|
7
9
|
import * as ui from "../bindings/ui";
|
|
8
10
|
import { flushCommit, fireLoadedCallbacks, markNeedsCommit, resetCommitState } from "./FrameScheduler";
|
|
@@ -29,12 +31,20 @@ function createEmptyPage(): Node {
|
|
|
29
31
|
return new FlexBox().width(100.0, Unit.Percent).height(100.0, Unit.Percent);
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
function disposeAppScopedAsyncWork(): void {
|
|
35
|
+
disposeAllFetchRequests();
|
|
36
|
+
disposeAllFileRequests();
|
|
37
|
+
disposeAllWorkers();
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
class ApplicationShell extends FlexBox {
|
|
33
41
|
constructor(root: Node) {
|
|
34
42
|
super();
|
|
35
43
|
this.width(100.0, Unit.Percent)
|
|
36
44
|
.height(100.0, Unit.Percent)
|
|
37
45
|
.child(root)
|
|
46
|
+
.child(SelectionHandleAdornerManager.createDefaultHost())
|
|
47
|
+
.child(MobileTextSelectionToolbarManager.createDefaultHost())
|
|
38
48
|
.child(FocusAdornerManager.createDefaultHost())
|
|
39
49
|
.child(ContextMenuManager.createDefaultMenu())
|
|
40
50
|
.child(ToolTipManager.createDefaultHost());
|
|
@@ -198,10 +208,10 @@ export class Application<TPage> {
|
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
cancelAllTimers();
|
|
201
|
-
|
|
202
|
-
disposeAllFileRequests();
|
|
203
|
-
disposeAllWorkers();
|
|
211
|
+
disposeAppScopedAsyncWork();
|
|
204
212
|
FocusAdornerManager.clear();
|
|
213
|
+
SelectionHandleAdornerManager.clear();
|
|
214
|
+
MobileTextSelectionToolbarManager.clear();
|
|
205
215
|
ToolTipManager.clear();
|
|
206
216
|
resetCommitState();
|
|
207
217
|
ui.reset();
|
|
@@ -225,10 +235,10 @@ export class Application<TPage> {
|
|
|
225
235
|
mountedShell = null;
|
|
226
236
|
mountedRoot = null;
|
|
227
237
|
cancelAllTimers();
|
|
228
|
-
|
|
229
|
-
disposeAllFileRequests();
|
|
230
|
-
disposeAllWorkers();
|
|
238
|
+
disposeAppScopedAsyncWork();
|
|
231
239
|
FocusAdornerManager.clear();
|
|
240
|
+
SelectionHandleAdornerManager.clear();
|
|
241
|
+
MobileTextSelectionToolbarManager.clear();
|
|
232
242
|
ToolTipManager.clear();
|
|
233
243
|
resetCommitState();
|
|
234
244
|
}
|
|
@@ -331,8 +341,7 @@ export function __disposeApp(): void {
|
|
|
331
341
|
return;
|
|
332
342
|
}
|
|
333
343
|
app.dispose();
|
|
334
|
-
|
|
335
|
-
disposeAllWorkers();
|
|
344
|
+
disposeAppScopedAsyncWork();
|
|
336
345
|
}
|
|
337
346
|
|
|
338
347
|
export function __flushRenders(): void {
|