@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,471 @@
|
|
|
1
|
+
import * as ui from "../bindings/ui";
|
|
2
|
+
import { HandlerAction } from "../core/Action";
|
|
3
|
+
import { Callback1, Handler1 } from "../core/BoundCallback";
|
|
4
|
+
import { Disposable, disposeAll } from "../core/Disposable";
|
|
5
|
+
import { DragCompletedEvent, DragDeltaEvent, DragGesture, DragGestureHost, DragStartedEvent } from "../core/DragGesture";
|
|
6
|
+
import { FocusAdornerManager } from "../core/FocusAdornerManager";
|
|
7
|
+
import { keyboardFocusVisible } from "../core/FocusVisibility";
|
|
8
|
+
import {
|
|
9
|
+
BorderStyle,
|
|
10
|
+
CursorStyle,
|
|
11
|
+
KeyEventType,
|
|
12
|
+
Orientation,
|
|
13
|
+
PointerEventType,
|
|
14
|
+
SemanticRole,
|
|
15
|
+
Unit,
|
|
16
|
+
} from "../core/ffi";
|
|
17
|
+
import { Theme, activeTheme } from "../core/Theme";
|
|
18
|
+
import { warn } from "../core/Logger";
|
|
19
|
+
import { PersistedFloat32Codec, PersistedValueState } from "../core/PersistedState";
|
|
20
|
+
import { Node } from "../core/Node";
|
|
21
|
+
import { FlexBox } from "../nodes";
|
|
22
|
+
import { bind1 } from "../core/bind";
|
|
23
|
+
import { getControlTemplates } from "./ControlTemplateSet";
|
|
24
|
+
import {
|
|
25
|
+
defaultSliderTemplate,
|
|
26
|
+
SliderPresenter,
|
|
27
|
+
SliderTemplate,
|
|
28
|
+
SliderVisualState,
|
|
29
|
+
} from "./internal/SliderPresenter";
|
|
30
|
+
|
|
31
|
+
const SLIDER_PERSISTED_CODEC = new PersistedFloat32Codec();
|
|
32
|
+
|
|
33
|
+
class PersistedSliderState extends PersistedValueState<Slider, f32> {
|
|
34
|
+
constructor() {
|
|
35
|
+
super("slider-value", SLIDER_PERSISTED_CODEC, 1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected captureValue(node: Slider): f32 {
|
|
39
|
+
return node.value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected restoreValue(node: Slider, value: f32): void {
|
|
43
|
+
node._applyPersistedValue(value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const SLIDER_PERSISTED_STATE = new PersistedSliderState();
|
|
48
|
+
|
|
49
|
+
function clamp(value: f32, min: f32, max: f32): f32 {
|
|
50
|
+
if (value < min) {
|
|
51
|
+
return min;
|
|
52
|
+
}
|
|
53
|
+
if (value > max) {
|
|
54
|
+
return max;
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const SLIDER_FOCUS_BORDER_WIDTH: f32 = 2.0;
|
|
60
|
+
const SLIDER_PADDING: f32 = 2.0;
|
|
61
|
+
const SLIDER_CHROME_INSET: f32 = SLIDER_FOCUS_BORDER_WIDTH + SLIDER_PADDING;
|
|
62
|
+
const SLIDER_CONTENT_INSET: f32 = 1.0;
|
|
63
|
+
const SLIDER_OUTER_INSET: f32 = SLIDER_CHROME_INSET + SLIDER_CONTENT_INSET;
|
|
64
|
+
const SLIDER_CHILD_INSET: f32 = SLIDER_PADDING + SLIDER_CONTENT_INSET;
|
|
65
|
+
|
|
66
|
+
function createSliderPresenter(template: SliderTemplate | null): SliderPresenter {
|
|
67
|
+
if (template !== null) {
|
|
68
|
+
return template.create();
|
|
69
|
+
}
|
|
70
|
+
const templateSet = getControlTemplates();
|
|
71
|
+
const appTemplate = templateSet !== null ? templateSet.slider : null;
|
|
72
|
+
return (appTemplate === null ? defaultSliderTemplate : appTemplate).create();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class Slider extends FlexBox implements DragGestureHost {
|
|
76
|
+
private sliderPresenter: SliderPresenter = createSliderPresenter(null);
|
|
77
|
+
private templateOverride: SliderTemplate | null = null;
|
|
78
|
+
private readonly disposables: Array<Disposable> = new Array<Disposable>();
|
|
79
|
+
private readonly dragGesture!: DragGesture;
|
|
80
|
+
private changedCallback: ((value: f32) => void) | null = null;
|
|
81
|
+
private changedBinding: Callback1<f32> | null = null;
|
|
82
|
+
private disposed: bool = false;
|
|
83
|
+
private focusedState: bool = false;
|
|
84
|
+
private hoveredState: bool = false;
|
|
85
|
+
private minValue: f32 = 0.0;
|
|
86
|
+
private maxValue: f32 = 100.0;
|
|
87
|
+
private stepValue: f32 = 1.0;
|
|
88
|
+
private currentValue: f32 = 0.0;
|
|
89
|
+
private lengthValue: f32 = 180.0;
|
|
90
|
+
private orientationValue: Orientation = Orientation.Horizontal;
|
|
91
|
+
|
|
92
|
+
constructor(value: f32 = 0.0) {
|
|
93
|
+
super();
|
|
94
|
+
this.dragGesture = new DragGesture(this).threshold(0.0);
|
|
95
|
+
this.semanticRole(SemanticRole.Slider);
|
|
96
|
+
this.syncSemanticLabel();
|
|
97
|
+
this.focusable(true);
|
|
98
|
+
this.requireInteractive();
|
|
99
|
+
this.reflectSemanticDisabledFromEnabled();
|
|
100
|
+
this.cursor(CursorStyle.Pointer);
|
|
101
|
+
this.child(this.sliderPresenter.root.positionAbsolute());
|
|
102
|
+
this.track(activeTheme.addAction(new HandlerAction<Slider, Theme>(this, (slider: Slider, _theme: Theme): void => {
|
|
103
|
+
slider.handleThemeChanged();
|
|
104
|
+
})));
|
|
105
|
+
this.track(keyboardFocusVisible.addAction(new HandlerAction<Slider, bool>(this, (slider: Slider, _visible: bool): void => {
|
|
106
|
+
slider.handleThemeChanged();
|
|
107
|
+
})));
|
|
108
|
+
this.track(this.dragGesture.started.bind(this, (slider: Slider, event: DragStartedEvent): void => {
|
|
109
|
+
slider.handleDragStarted(event);
|
|
110
|
+
}));
|
|
111
|
+
this.track(this.dragGesture.delta.bind(this, (slider: Slider, event: DragDeltaEvent): void => {
|
|
112
|
+
slider.handleDragDelta(event);
|
|
113
|
+
}));
|
|
114
|
+
this.track(this.dragGesture.completed.bind(this, (slider: Slider, event: DragCompletedEvent): void => {
|
|
115
|
+
slider.handleDragCompleted(event);
|
|
116
|
+
}));
|
|
117
|
+
this.setValue(value, false);
|
|
118
|
+
this.semanticOrientation(this.orientationValue);
|
|
119
|
+
this.handleThemeChanged();
|
|
120
|
+
this.persistState(SLIDER_PERSISTED_STATE);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get value(): f32 {
|
|
124
|
+
return this.currentValue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
min(value: f32): this {
|
|
128
|
+
this.minValue = value;
|
|
129
|
+
if (this.maxValue < value) {
|
|
130
|
+
this.maxValue = value;
|
|
131
|
+
}
|
|
132
|
+
this.setValue(this.currentValue, false);
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
max(value: f32): this {
|
|
137
|
+
this.maxValue = value;
|
|
138
|
+
if (this.minValue > value) {
|
|
139
|
+
this.minValue = value;
|
|
140
|
+
}
|
|
141
|
+
this.setValue(this.currentValue, false);
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
step(value: f32): this {
|
|
146
|
+
if (value <= 0.0) {
|
|
147
|
+
warn("Layout", "Slider.step() received " + value.toString() + "; clamping to 1.0.");
|
|
148
|
+
}
|
|
149
|
+
this.stepValue = value > 0.0 ? value : 1.0;
|
|
150
|
+
this.setValue(this.currentValue, false);
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
length(value: f32): this {
|
|
155
|
+
const thumbSize = this.sliderPresenter.metrics.thumbSize;
|
|
156
|
+
if (value <= thumbSize) {
|
|
157
|
+
warn(
|
|
158
|
+
"Layout",
|
|
159
|
+
"Slider.length() received " + value.toString() + "; clamping to a value above the thumb size.",
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
this.lengthValue = value > thumbSize ? value : thumbSize + 1.0;
|
|
163
|
+
this.syncPresentation();
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
orientation(value: Orientation): this {
|
|
168
|
+
this.orientationValue = value == Orientation.Vertical ? Orientation.Vertical : Orientation.Horizontal;
|
|
169
|
+
this.semanticOrientation(this.orientationValue);
|
|
170
|
+
this.syncSemanticLabel();
|
|
171
|
+
this.syncPresentation();
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
template(template: SliderTemplate | null): this {
|
|
176
|
+
this.templateOverride = template;
|
|
177
|
+
const nextPresenter = createSliderPresenter(this.templateOverride);
|
|
178
|
+
this.replacePresenter(nextPresenter);
|
|
179
|
+
const thumbSize = this.sliderPresenter.metrics.thumbSize;
|
|
180
|
+
if (this.lengthValue <= thumbSize) {
|
|
181
|
+
warn(
|
|
182
|
+
"Layout",
|
|
183
|
+
"Slider.template() increased the thumb size beyond the current slider length; clamping length to stay interactive.",
|
|
184
|
+
);
|
|
185
|
+
this.lengthValue = thumbSize + 1.0;
|
|
186
|
+
}
|
|
187
|
+
this.syncPresentation();
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
onChanged(callback: ((value: f32) => void) | null): this {
|
|
192
|
+
this.changedCallback = callback;
|
|
193
|
+
this.changedBinding = null;
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
bindChanged<Owner>(owner: Owner, handler: Handler1<Owner, f32>): this {
|
|
198
|
+
this.changedCallback = null;
|
|
199
|
+
this.changedBinding = bind1<Owner, f32>(owner, handler);
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
onChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, f32>): this {
|
|
204
|
+
this.bindChanged(owner, handler);
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
dispose(): void {
|
|
209
|
+
this.disposeControl();
|
|
210
|
+
super.dispose();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
_captureDragPointer(): void {
|
|
214
|
+
this.capturePointer();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
_releaseDragPointer(): void {
|
|
218
|
+
this.releasePointer();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
_handlePointerEvent(eventType: PointerEventType, x: f32, y: f32, modifiers: u32 = 0): void {
|
|
222
|
+
super._handlePointerEvent(eventType, x, y, modifiers);
|
|
223
|
+
if (!this.isEnabled) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (eventType == PointerEventType.Enter) {
|
|
227
|
+
this.hoveredState = true;
|
|
228
|
+
this.syncVisualState();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (eventType == PointerEventType.Leave) {
|
|
232
|
+
this.hoveredState = false;
|
|
233
|
+
if (!this.dragGesture.isDragging) {
|
|
234
|
+
this.syncVisualState();
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (eventType == PointerEventType.Down) {
|
|
239
|
+
this.dragGesture.handlePointerDown(x, y, modifiers);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (eventType == PointerEventType.Move) {
|
|
243
|
+
this.dragGesture.handlePointerMove(x, y, modifiers);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (eventType == PointerEventType.Up) {
|
|
247
|
+
this.dragGesture.handlePointerUp(x, y, modifiers);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
_handleKeyEvent(eventType: KeyEventType, key: string, modifiers: u32): bool {
|
|
252
|
+
const callbackHandled = super._handleKeyEvent(eventType, key, modifiers);
|
|
253
|
+
if (!this.isEnabled || eventType != KeyEventType.Down || modifiers != 0) {
|
|
254
|
+
return callbackHandled;
|
|
255
|
+
}
|
|
256
|
+
if (key == "Home") {
|
|
257
|
+
this.setValue(this.minValue, true);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
if (key == "End") {
|
|
261
|
+
this.setValue(this.maxValue, true);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
let delta: f32 = 0.0;
|
|
265
|
+
if (this.orientationValue == Orientation.Vertical) {
|
|
266
|
+
if (key == "ArrowUp") {
|
|
267
|
+
delta = this.stepValue;
|
|
268
|
+
} else if (key == "ArrowDown") {
|
|
269
|
+
delta = -this.stepValue;
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
if (key == "ArrowRight") {
|
|
273
|
+
delta = this.stepValue;
|
|
274
|
+
} else if (key == "ArrowLeft") {
|
|
275
|
+
delta = -this.stepValue;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (delta != 0.0) {
|
|
279
|
+
this.setValue(this.currentValue + delta, true);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
return callbackHandled;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_handleFocusChanged(focused: bool): void {
|
|
286
|
+
super._handleFocusChanged(focused);
|
|
287
|
+
this.focusedState = focused;
|
|
288
|
+
this.handleThemeChanged();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
protected _onEffectiveEnabledChanged(_isEnabled: bool): void {
|
|
292
|
+
if (!this.isEnabled) {
|
|
293
|
+
this.dragGesture.cancel();
|
|
294
|
+
}
|
|
295
|
+
this.handleThemeChanged();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
_applyPersistedValue(value: f32): void {
|
|
299
|
+
this.setValue(value, true);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private normalizeValue(value: f32): f32 {
|
|
303
|
+
const clamped = clamp(value, this.minValue, this.maxValue);
|
|
304
|
+
if (this.stepValue <= 0.0) {
|
|
305
|
+
return clamped;
|
|
306
|
+
}
|
|
307
|
+
const snapped = <f32>Math.round((clamped - this.minValue) / this.stepValue) * this.stepValue;
|
|
308
|
+
return clamp(this.minValue + snapped, this.minValue, this.maxValue);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private setValue(value: f32, emit: bool): void {
|
|
312
|
+
const normalized = this.normalizeValue(value);
|
|
313
|
+
if (normalized == this.currentValue) {
|
|
314
|
+
this.syncPresentation();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
this.currentValue = normalized;
|
|
318
|
+
this.semanticValueRange(this.currentValue, this.minValue, this.maxValue);
|
|
319
|
+
this.syncPresentation();
|
|
320
|
+
if (emit) {
|
|
321
|
+
this.requestSemanticAnnouncement();
|
|
322
|
+
const callback = this.changedCallback;
|
|
323
|
+
if (callback !== null) {
|
|
324
|
+
callback(this.currentValue);
|
|
325
|
+
}
|
|
326
|
+
const binding = this.changedBinding;
|
|
327
|
+
if (binding !== null) {
|
|
328
|
+
binding.invoke(this.currentValue);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private updateValueFromPointer(x: f32, y: f32, emit: bool): void {
|
|
334
|
+
let localX = x;
|
|
335
|
+
let localY = y;
|
|
336
|
+
if (this.builtHandle != 0) {
|
|
337
|
+
const bounds = ui.tryGetBounds(this.builtHandle);
|
|
338
|
+
if (bounds !== null) {
|
|
339
|
+
localX = x - unchecked(bounds[0]);
|
|
340
|
+
localY = y - unchecked(bounds[1]);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const thumbSize = this.sliderPresenter.metrics.thumbSize;
|
|
344
|
+
const available = this.lengthValue - thumbSize;
|
|
345
|
+
if (available <= 0.0) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const leadingInset = SLIDER_OUTER_INSET + (thumbSize * 0.5);
|
|
349
|
+
const offset = this.orientationValue == Orientation.Vertical
|
|
350
|
+
? available - clamp(localY - leadingInset, 0.0, available)
|
|
351
|
+
: clamp(localX - leadingInset, 0.0, available);
|
|
352
|
+
const fraction = clamp(offset / available, 0.0, 1.0);
|
|
353
|
+
this.setValue(this.minValue + ((this.maxValue - this.minValue) * fraction), emit);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private syncGeometryWithState(state: SliderVisualState): void {
|
|
357
|
+
const metrics = this.sliderPresenter.metrics;
|
|
358
|
+
if (this.orientationValue == Orientation.Vertical) {
|
|
359
|
+
this.width(metrics.thumbSize + (SLIDER_OUTER_INSET * 2.0) + metrics.crossAxisExtra, Unit.Pixel);
|
|
360
|
+
this.height(this.lengthValue + (SLIDER_OUTER_INSET * 2.0), Unit.Pixel);
|
|
361
|
+
} else {
|
|
362
|
+
this.width(this.lengthValue + (SLIDER_OUTER_INSET * 2.0), Unit.Pixel);
|
|
363
|
+
this.height(metrics.thumbSize + (SLIDER_OUTER_INSET * 2.0) + metrics.crossAxisExtra, Unit.Pixel);
|
|
364
|
+
}
|
|
365
|
+
this.sliderPresenter.root
|
|
366
|
+
.positionAbsolute()
|
|
367
|
+
.position(SLIDER_CHILD_INSET, SLIDER_CHILD_INSET);
|
|
368
|
+
this.sliderPresenter.layout(state, this.lengthValue);
|
|
369
|
+
this.semanticValueRange(this.currentValue, this.minValue, this.maxValue);
|
|
370
|
+
this.syncSemanticLabel();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private syncVisualStateWithState(state: SliderVisualState): void {
|
|
374
|
+
const theme = activeTheme.value;
|
|
375
|
+
this.cornerRadius(theme.spacing.sm);
|
|
376
|
+
this.border(
|
|
377
|
+
SLIDER_FOCUS_BORDER_WIDTH,
|
|
378
|
+
theme.colors.background,
|
|
379
|
+
BorderStyle.Solid,
|
|
380
|
+
);
|
|
381
|
+
this.padding(SLIDER_PADDING, SLIDER_PADDING, SLIDER_PADDING, SLIDER_PADDING);
|
|
382
|
+
this.opacity(this.isEnabled ? 1.0 : 0.6);
|
|
383
|
+
this.sliderPresenter.apply(theme, state);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private syncVisualState(): void {
|
|
387
|
+
this.syncVisualStateWithState(this.createVisualState());
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private syncPresentation(): void {
|
|
391
|
+
const state = this.createVisualState();
|
|
392
|
+
this.syncGeometryWithState(state);
|
|
393
|
+
this.syncVisualStateWithState(state);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private createVisualState(): SliderVisualState {
|
|
397
|
+
const normalizedValue = this.maxValue > this.minValue
|
|
398
|
+
? clamp((this.currentValue - this.minValue) / (this.maxValue - this.minValue), 0.0, 1.0)
|
|
399
|
+
: 0.0;
|
|
400
|
+
return new SliderVisualState(
|
|
401
|
+
this.currentValue,
|
|
402
|
+
this.minValue,
|
|
403
|
+
this.maxValue,
|
|
404
|
+
normalizedValue,
|
|
405
|
+
this.orientationValue,
|
|
406
|
+
this.hoveredState,
|
|
407
|
+
this.dragGesture.isDragging,
|
|
408
|
+
this.focusedState,
|
|
409
|
+
this.isEnabled,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private handleDragStarted(event: DragStartedEvent): void {
|
|
414
|
+
this.updateValueFromPointer(event.x, event.y, true);
|
|
415
|
+
this.syncVisualState();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private handleDragDelta(event: DragDeltaEvent): void {
|
|
419
|
+
this.updateValueFromPointer(event.x, event.y, true);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private handleDragCompleted(event: DragCompletedEvent): void {
|
|
423
|
+
this.updateValueFromPointer(event.x, event.y, true);
|
|
424
|
+
this.syncVisualState();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private syncSemanticLabel(): void {
|
|
428
|
+
const baseLabel = this.orientationValue == Orientation.Vertical ? "Vertical slider" : "Slider";
|
|
429
|
+
this.setDefaultSemanticLabel(baseLabel);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private handleThemeChanged(): void {
|
|
433
|
+
if (this.disposed) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
this.cursor(this.isEnabled ? CursorStyle.Pointer : CursorStyle.Default);
|
|
437
|
+
this.syncPresentation();
|
|
438
|
+
this.syncFocusChrome(activeTheme.value);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private replacePresenter(nextPresenter: SliderPresenter): void {
|
|
442
|
+
const previousRoot = this.sliderPresenter.root;
|
|
443
|
+
this.sliderPresenter = nextPresenter;
|
|
444
|
+
const children = new Array<Node>();
|
|
445
|
+
children.push(nextPresenter.root.positionAbsolute());
|
|
446
|
+
this.replaceChildren(children);
|
|
447
|
+
previousRoot.dispose();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
private track(disposable: Disposable): void {
|
|
451
|
+
this.disposables.push(disposable);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private disposeControl(): void {
|
|
455
|
+
if (this.disposed) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
this.disposed = true;
|
|
459
|
+
this.dragGesture.cancel();
|
|
460
|
+
disposeAll(this.disposables);
|
|
461
|
+
FocusAdornerManager.hideOwner(this);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private syncFocusChrome(theme: Theme): void {
|
|
465
|
+
if (this.focusedState && this.isEnabled && keyboardFocusVisible.value) {
|
|
466
|
+
FocusAdornerManager.showStandard(this, theme.spacing.sm);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
FocusAdornerManager.hideOwner(this);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Callback1, Handler1 } from "../core/BoundCallback";
|
|
2
|
+
import { SemanticCheckedState, SemanticRole } from "../core/ffi";
|
|
3
|
+
import { PersistedBoolCodec, PersistedValueState } from "../core/PersistedState";
|
|
4
|
+
import { bind1 } from "../core/bind";
|
|
5
|
+
import { activeTheme } from "../core/Theme";
|
|
6
|
+
import { getControlTemplates } from "./ControlTemplateSet";
|
|
7
|
+
import { PressableLabeledControl } from "./internal/PressableLabeledControl";
|
|
8
|
+
import {
|
|
9
|
+
defaultSwitchIndicatorTemplate,
|
|
10
|
+
SwitchIndicatorPresenter,
|
|
11
|
+
SwitchIndicatorTemplate,
|
|
12
|
+
SwitchIndicatorVisualState,
|
|
13
|
+
} from "./internal/SwitchIndicatorPresenter";
|
|
14
|
+
|
|
15
|
+
const SWITCH_PERSISTED_CODEC = new PersistedBoolCodec();
|
|
16
|
+
|
|
17
|
+
class PersistedSwitchState extends PersistedValueState<Switch, bool> {
|
|
18
|
+
constructor() {
|
|
19
|
+
super("switch-checked", SWITCH_PERSISTED_CODEC, 1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
protected captureValue(node: Switch): bool {
|
|
23
|
+
return node.checked;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protected restoreValue(node: Switch, value: bool): void {
|
|
27
|
+
node._applyPersistedChecked(value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const SWITCH_PERSISTED_STATE = new PersistedSwitchState();
|
|
32
|
+
|
|
33
|
+
function createIndicatorPresenter(template: SwitchIndicatorTemplate | null): SwitchIndicatorPresenter {
|
|
34
|
+
if (template !== null) {
|
|
35
|
+
return template.create();
|
|
36
|
+
}
|
|
37
|
+
const templateSet = getControlTemplates();
|
|
38
|
+
const appTemplate = templateSet !== null ? templateSet.switchIndicator : null;
|
|
39
|
+
return (appTemplate === null ? defaultSwitchIndicatorTemplate : appTemplate).create();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class Switch extends PressableLabeledControl {
|
|
43
|
+
private indicatorPresenter: SwitchIndicatorPresenter;
|
|
44
|
+
private templateOverride: SwitchIndicatorTemplate | null = null;
|
|
45
|
+
private checkedValue: bool = false;
|
|
46
|
+
private changedCallback: ((checked: bool) => void) | null = null;
|
|
47
|
+
private changedBinding: Callback1<bool> | null = null;
|
|
48
|
+
|
|
49
|
+
constructor(label: string) {
|
|
50
|
+
const indicatorPresenter = createIndicatorPresenter(null);
|
|
51
|
+
super(SemanticRole.Switch, label, indicatorPresenter.root);
|
|
52
|
+
this.indicatorPresenter = indicatorPresenter;
|
|
53
|
+
this.semanticChecked(SemanticCheckedState.False);
|
|
54
|
+
this.syncVisualState();
|
|
55
|
+
this.persistState(SWITCH_PERSISTED_STATE);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get checked(): bool {
|
|
59
|
+
return this.checkedValue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
check(flag: bool): this {
|
|
63
|
+
this.updateChecked(flag, false);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
template(template: SwitchIndicatorTemplate | null): this {
|
|
68
|
+
this.templateOverride = template;
|
|
69
|
+
this.indicatorPresenter = createIndicatorPresenter(this.templateOverride);
|
|
70
|
+
this.replaceIndicatorRoot(this.indicatorPresenter.root);
|
|
71
|
+
this.syncVisualState();
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onChanged(callback: ((checked: bool) => void) | null): this {
|
|
76
|
+
this.changedCallback = callback;
|
|
77
|
+
this.changedBinding = null;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
bindChanged<Owner>(owner: Owner, handler: Handler1<Owner, bool>): this {
|
|
82
|
+
this.changedCallback = null;
|
|
83
|
+
this.changedBinding = bind1<Owner, bool>(owner, handler);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, bool>): this {
|
|
88
|
+
this.bindChanged(owner, handler);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected handleActivated(): void {
|
|
93
|
+
this.updateChecked(!this.checkedValue, true);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected syncVisualState(): void {
|
|
97
|
+
this.indicatorPresenter.apply(
|
|
98
|
+
activeTheme.value,
|
|
99
|
+
new SwitchIndicatorVisualState(
|
|
100
|
+
this.checkedValue,
|
|
101
|
+
this.hoveredState,
|
|
102
|
+
this.pressedState,
|
|
103
|
+
this.focusedState,
|
|
104
|
+
this.isEnabled,
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_applyPersistedChecked(next: bool): void {
|
|
110
|
+
this.updateChecked(next, true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private updateChecked(next: bool, emit: bool): void {
|
|
114
|
+
if (this.checkedValue == next) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.checkedValue = next;
|
|
118
|
+
this.semanticChecked(next ? SemanticCheckedState.True : SemanticCheckedState.False);
|
|
119
|
+
this.syncVisualState();
|
|
120
|
+
if (emit) {
|
|
121
|
+
this.requestSemanticAnnouncement();
|
|
122
|
+
const callback = this.changedCallback;
|
|
123
|
+
if (callback !== null) {
|
|
124
|
+
callback(next);
|
|
125
|
+
}
|
|
126
|
+
const binding = this.changedBinding;
|
|
127
|
+
if (binding !== null) {
|
|
128
|
+
binding.invoke(next);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ScrollBarVisibility } from "../nodes";
|
|
2
|
+
import { TextInputCore, multilineTextAreaProfile } from "./internal/TextInputCore";
|
|
3
|
+
|
|
4
|
+
export class TextArea extends TextInputCore {
|
|
5
|
+
constructor(text: string = "") {
|
|
6
|
+
super(multilineTextAreaProfile, text);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
wrapping(flag: bool = true): this {
|
|
10
|
+
return changetype<this>(super.wrapping(flag));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
verticalScrollbarVisibility(mode: ScrollBarVisibility): this {
|
|
14
|
+
return changetype<this>(super.verticalScrollbarVisibility(mode));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
horizontalScrollbarVisibility(mode: ScrollBarVisibility): this {
|
|
18
|
+
return changetype<this>(super.horizontalScrollbarVisibility(mode));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { Button } from "./Button";
|
|
2
|
+
export { Checkbox } from "./Checkbox";
|
|
3
|
+
export { ContextMenu, MenuItem } from "./ContextMenu";
|
|
4
|
+
export { Dialog } from "./Dialog";
|
|
5
|
+
export { Dropdown, DropdownItem } from "./Dropdown";
|
|
6
|
+
export { Form } from "./Form";
|
|
7
|
+
export { NavLink } from "./NavLink";
|
|
8
|
+
export { Popup, PopupPlacement } from "./Popup";
|
|
9
|
+
export { ProgressBar } from "./ProgressBar";
|
|
10
|
+
export { RadioButton } from "./RadioButton";
|
|
11
|
+
export { RadioGroup } from "./RadioGroup";
|
|
12
|
+
export { AntiSelectionArea } from "./AntiSelectionArea";
|
|
13
|
+
export { SelectionArea } from "./SelectionArea";
|
|
14
|
+
export { Slider } from "./Slider";
|
|
15
|
+
export { Switch } from "./Switch";
|
|
16
|
+
export { TextArea } from "./TextArea";
|
|
17
|
+
export { TextInput } from "./TextInput";
|
|
18
|
+
export * from "./templating";
|