@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,221 @@
|
|
|
1
|
+
import { rgba } from "../../color";
|
|
2
|
+
import { HandlerAction } from "../../core/Action";
|
|
3
|
+
import { Disposable, disposeAll } from "../../core/Disposable";
|
|
4
|
+
import { FocusAdornerManager } from "../../core/FocusAdornerManager";
|
|
5
|
+
import { keyboardFocusVisible } from "../../core/FocusVisibility";
|
|
6
|
+
import { Node } from "../../core/Node";
|
|
7
|
+
import {
|
|
8
|
+
AlignItems,
|
|
9
|
+
BorderStyle,
|
|
10
|
+
CursorStyle,
|
|
11
|
+
FlexDirection,
|
|
12
|
+
KeyEventType,
|
|
13
|
+
PointerEventType,
|
|
14
|
+
SemanticRole,
|
|
15
|
+
Unit,
|
|
16
|
+
} from "../../core/ffi";
|
|
17
|
+
import { Theme, activeTheme } from "../../core/Theme";
|
|
18
|
+
import { FlexBox, TextCore } from "../../nodes";
|
|
19
|
+
|
|
20
|
+
const TRANSPARENT: u32 = rgba(0x00, 0x00, 0x00, 0x00);
|
|
21
|
+
|
|
22
|
+
function isSpaceKey(key: string): bool {
|
|
23
|
+
return key == " " || key == "Spacebar";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class PressableLabeledControl extends FlexBox {
|
|
27
|
+
private indicatorRoot: FlexBox;
|
|
28
|
+
protected readonly labelNode: TextCore;
|
|
29
|
+
private readonly gapNode: FlexBox;
|
|
30
|
+
private readonly labelHost: FlexBox;
|
|
31
|
+
private readonly disposables: Array<Disposable> = new Array<Disposable>();
|
|
32
|
+
private disposed: bool = false;
|
|
33
|
+
protected hoveredState: bool = false;
|
|
34
|
+
protected pressedState: bool = false;
|
|
35
|
+
protected focusedState: bool = false;
|
|
36
|
+
private keyPressedState: bool = false;
|
|
37
|
+
private pointerPressedState: bool = false;
|
|
38
|
+
|
|
39
|
+
constructor(role: SemanticRole, label: string, indicatorPresenterRoot: FlexBox) {
|
|
40
|
+
super();
|
|
41
|
+
this.indicatorRoot = indicatorPresenterRoot;
|
|
42
|
+
this.labelNode = new TextCore(label);
|
|
43
|
+
this.gapNode = new FlexBox()
|
|
44
|
+
.width(activeTheme.value.spacing.sm, Unit.Pixel)
|
|
45
|
+
.height(1.0, Unit.Pixel);
|
|
46
|
+
this.labelHost = new FlexBox()
|
|
47
|
+
.flexBasis(0.0)
|
|
48
|
+
.flexGrow(1.0);
|
|
49
|
+
this.labelNode.width(100.0, Unit.Percent);
|
|
50
|
+
this.labelHost.child(this.labelNode);
|
|
51
|
+
|
|
52
|
+
this.semanticRole(role);
|
|
53
|
+
this.semanticLabel(label);
|
|
54
|
+
this.focusable(true);
|
|
55
|
+
this.requireInteractive();
|
|
56
|
+
this.reflectSemanticDisabledFromEnabled();
|
|
57
|
+
this.flexDirection(FlexDirection.Row);
|
|
58
|
+
this.alignItems(AlignItems.Center);
|
|
59
|
+
this.child(indicatorPresenterRoot);
|
|
60
|
+
this.child(this.gapNode);
|
|
61
|
+
this.child(this.labelHost);
|
|
62
|
+
this.track(activeTheme.addAction(new HandlerAction<PressableLabeledControl, Theme>(this, (control: PressableLabeledControl, _theme: Theme): void => {
|
|
63
|
+
control.handleThemeChanged();
|
|
64
|
+
})));
|
|
65
|
+
this.track(keyboardFocusVisible.addAction(new HandlerAction<PressableLabeledControl, bool>(this, (control: PressableLabeledControl, _visible: bool): void => {
|
|
66
|
+
control.handleThemeChanged();
|
|
67
|
+
})));
|
|
68
|
+
this.syncBaseTheme(activeTheme.value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected updateLabel(label: string): this {
|
|
72
|
+
this.semanticLabel(label);
|
|
73
|
+
this.labelNode.text(label);
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dispose(): void {
|
|
78
|
+
this.disposeControl();
|
|
79
|
+
super.dispose();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_handlePointerEvent(eventType: PointerEventType, x: f32, y: f32, modifiers: u32): void {
|
|
83
|
+
super._handlePointerEvent(eventType, x, y, modifiers);
|
|
84
|
+
if (!this.isEnabled) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (eventType == PointerEventType.Enter) {
|
|
88
|
+
this.hoveredState = true;
|
|
89
|
+
this.syncVisualState();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (eventType == PointerEventType.Leave) {
|
|
93
|
+
this.hoveredState = false;
|
|
94
|
+
this.pointerPressedState = false;
|
|
95
|
+
this.pressedState = false;
|
|
96
|
+
this.syncVisualState();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (eventType == PointerEventType.Down) {
|
|
100
|
+
this.pointerPressedState = true;
|
|
101
|
+
this.pressedState = true;
|
|
102
|
+
this.syncVisualState();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (eventType == PointerEventType.Up && this.pointerPressedState) {
|
|
106
|
+
this.pointerPressedState = false;
|
|
107
|
+
this.pressedState = false;
|
|
108
|
+
this.syncVisualState();
|
|
109
|
+
this.handleActivated();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_handleKeyEvent(eventType: KeyEventType, key: string, modifiers: u32): bool {
|
|
114
|
+
const callbackHandled = super._handleKeyEvent(eventType, key, modifiers);
|
|
115
|
+
if (!this.isEnabled || !isSpaceKey(key) || modifiers != 0) {
|
|
116
|
+
return callbackHandled;
|
|
117
|
+
}
|
|
118
|
+
if (eventType == KeyEventType.Down) {
|
|
119
|
+
this.keyPressedState = true;
|
|
120
|
+
this.pressedState = true;
|
|
121
|
+
this.syncVisualState();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (eventType == KeyEventType.Up && this.keyPressedState) {
|
|
125
|
+
this.keyPressedState = false;
|
|
126
|
+
this.pressedState = false;
|
|
127
|
+
this.syncVisualState();
|
|
128
|
+
this.handleActivated();
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return callbackHandled;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_handleFocusChanged(focused: bool): void {
|
|
135
|
+
super._handleFocusChanged(focused);
|
|
136
|
+
if (this.focusedState == focused) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.focusedState = focused;
|
|
140
|
+
if (!focused) {
|
|
141
|
+
this.keyPressedState = false;
|
|
142
|
+
this.pressedState = false;
|
|
143
|
+
}
|
|
144
|
+
this.syncBaseTheme(activeTheme.value);
|
|
145
|
+
this.syncVisualState();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
protected _onEffectiveEnabledChanged(_isEnabled: bool): void {
|
|
149
|
+
this.pointerPressedState = false;
|
|
150
|
+
this.keyPressedState = false;
|
|
151
|
+
this.pressedState = false;
|
|
152
|
+
if (!this.isEnabled) {
|
|
153
|
+
this.hoveredState = false;
|
|
154
|
+
}
|
|
155
|
+
this.syncBaseTheme(activeTheme.value);
|
|
156
|
+
this.syncVisualState();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected handleActivated(): void {}
|
|
160
|
+
|
|
161
|
+
protected syncVisualState(): void {}
|
|
162
|
+
|
|
163
|
+
protected replaceIndicatorRoot(nextRoot: FlexBox): void {
|
|
164
|
+
if (nextRoot === this.indicatorRoot) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const previousRoot = this.indicatorRoot;
|
|
168
|
+
this.indicatorRoot = nextRoot;
|
|
169
|
+
const children = new Array<Node>();
|
|
170
|
+
children.push(nextRoot);
|
|
171
|
+
children.push(this.gapNode);
|
|
172
|
+
children.push(this.labelHost);
|
|
173
|
+
this.replaceChildren(children);
|
|
174
|
+
previousRoot.dispose();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
protected syncBaseTheme(theme: Theme): void {
|
|
178
|
+
this.cursor(this.isEnabled ? CursorStyle.Pointer : CursorStyle.Default);
|
|
179
|
+
this.cornerRadius(theme.spacing.sm);
|
|
180
|
+
this.border(
|
|
181
|
+
2.0,
|
|
182
|
+
TRANSPARENT,
|
|
183
|
+
BorderStyle.Solid,
|
|
184
|
+
);
|
|
185
|
+
this.padding(theme.spacing.xs, theme.spacing.xs, theme.spacing.xs, theme.spacing.xs);
|
|
186
|
+
this.opacity(this.isEnabled ? 1.0 : 0.6);
|
|
187
|
+
this.gapNode.width(theme.spacing.sm, Unit.Pixel);
|
|
188
|
+
this.labelNode.font(theme.fonts.body, theme.fonts.sizeBody);
|
|
189
|
+
this.labelNode.textColor(this.isEnabled ? theme.colors.textPrimary : theme.colors.textMuted);
|
|
190
|
+
this.syncFocusChrome(theme);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected handleThemeChanged(): void {
|
|
194
|
+
if (this.disposed) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
this.syncBaseTheme(activeTheme.value);
|
|
198
|
+
this.syncVisualState();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private track(disposable: Disposable): void {
|
|
202
|
+
this.disposables.push(disposable);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private disposeControl(): void {
|
|
206
|
+
if (this.disposed) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.disposed = true;
|
|
210
|
+
disposeAll(this.disposables);
|
|
211
|
+
FocusAdornerManager.hideOwner(this);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private syncFocusChrome(theme: Theme): void {
|
|
215
|
+
if (this.focusedState && this.isEnabled && keyboardFocusVisible.value) {
|
|
216
|
+
FocusAdornerManager.showStandard(this, theme.spacing.sm);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
FocusAdornerManager.hideOwner(this);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BorderStyle, Unit } from "../../core/ffi";
|
|
2
|
+
import { Theme } from "../../core/Theme";
|
|
3
|
+
import { FlexBox } from "../../nodes";
|
|
4
|
+
import {
|
|
5
|
+
PressableIndicatorMetrics,
|
|
6
|
+
PressableIndicatorPresenter,
|
|
7
|
+
PressableIndicatorVisualState,
|
|
8
|
+
} from "./PressableIndicatorPresenter";
|
|
9
|
+
|
|
10
|
+
export class RadioIndicatorVisualState extends PressableIndicatorVisualState {
|
|
11
|
+
constructor(
|
|
12
|
+
readonly checked: bool,
|
|
13
|
+
hovered: bool,
|
|
14
|
+
pressed: bool,
|
|
15
|
+
focused: bool,
|
|
16
|
+
enabled: bool,
|
|
17
|
+
) {
|
|
18
|
+
super(hovered, pressed, focused, enabled);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export abstract class RadioIndicatorPresenter extends PressableIndicatorPresenter {
|
|
23
|
+
protected constructor(root: FlexBox, metrics: PressableIndicatorMetrics) {
|
|
24
|
+
super(root, metrics);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
abstract apply(theme: Theme, state: RadioIndicatorVisualState): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export abstract class RadioIndicatorTemplate {
|
|
31
|
+
abstract create(): RadioIndicatorPresenter;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class DefaultRadioIndicatorPresenter extends RadioIndicatorPresenter {
|
|
35
|
+
private readonly dotNode: FlexBox;
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
const root = new FlexBox()
|
|
39
|
+
.width(20.0, Unit.Pixel)
|
|
40
|
+
.height(20.0, Unit.Pixel)
|
|
41
|
+
.alignItems(1)
|
|
42
|
+
.justifyContent(1);
|
|
43
|
+
super(root, new PressableIndicatorMetrics(20.0, 20.0));
|
|
44
|
+
const dotNode = new FlexBox()
|
|
45
|
+
.positionAbsolute()
|
|
46
|
+
.position(5.0, 5.0)
|
|
47
|
+
.width(8.0, Unit.Pixel)
|
|
48
|
+
.height(8.0, Unit.Pixel);
|
|
49
|
+
this.dotNode = dotNode;
|
|
50
|
+
root.child(dotNode);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
apply(theme: Theme, state: RadioIndicatorVisualState): void {
|
|
54
|
+
const outerColor = state.checked
|
|
55
|
+
? (state.pressed ? theme.colors.accentPressed : (state.hovered ? theme.colors.accentHovered : theme.colors.accent))
|
|
56
|
+
: theme.colors.border;
|
|
57
|
+
this.root.cornerRadius(10.0);
|
|
58
|
+
this.root.border(1.0, outerColor, BorderStyle.Solid);
|
|
59
|
+
this.root.bgColor(theme.colors.surface);
|
|
60
|
+
this.dotNode.cornerRadius(4.0);
|
|
61
|
+
this.dotNode.position(5.0, 5.0);
|
|
62
|
+
this.dotNode.bgColor(outerColor);
|
|
63
|
+
this.dotNode.opacity(state.checked ? 1.0 : 0.0);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class DefaultRadioIndicatorTemplate extends RadioIndicatorTemplate {
|
|
68
|
+
create(): RadioIndicatorPresenter {
|
|
69
|
+
return new DefaultRadioIndicatorPresenter();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const defaultRadioIndicatorTemplate = new DefaultRadioIndicatorTemplate();
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { BorderStyle, Orientation, Unit } from "../../core/ffi";
|
|
2
|
+
import { Theme } from "../../core/Theme";
|
|
3
|
+
import { FlexBox } from "../../nodes";
|
|
4
|
+
|
|
5
|
+
function clamp(value: f32, min: f32, max: f32): f32 {
|
|
6
|
+
if (value < min) {
|
|
7
|
+
return min;
|
|
8
|
+
}
|
|
9
|
+
if (value > max) {
|
|
10
|
+
return max;
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class SliderPresenterMetrics {
|
|
16
|
+
constructor(
|
|
17
|
+
readonly thumbSize: f32,
|
|
18
|
+
readonly trackThickness: f32,
|
|
19
|
+
readonly crossAxisExtra: f32 = 2.0,
|
|
20
|
+
) {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class SliderVisualState {
|
|
24
|
+
constructor(
|
|
25
|
+
readonly value: f32,
|
|
26
|
+
readonly min: f32,
|
|
27
|
+
readonly max: f32,
|
|
28
|
+
readonly normalizedValue: f32,
|
|
29
|
+
readonly orientation: Orientation,
|
|
30
|
+
readonly hovered: bool,
|
|
31
|
+
readonly dragging: bool,
|
|
32
|
+
readonly focused: bool,
|
|
33
|
+
readonly enabled: bool,
|
|
34
|
+
) {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export abstract class SliderPresenter {
|
|
38
|
+
protected constructor(
|
|
39
|
+
private readonly rootValue: FlexBox,
|
|
40
|
+
private readonly metricsValue: SliderPresenterMetrics,
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
get root(): FlexBox {
|
|
44
|
+
return this.rootValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get metrics(): SliderPresenterMetrics {
|
|
48
|
+
return this.metricsValue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
abstract layout(state: SliderVisualState, length: f32): void;
|
|
52
|
+
abstract apply(theme: Theme, state: SliderVisualState): void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export abstract class SliderTemplate {
|
|
56
|
+
abstract create(): SliderPresenter;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class DefaultSliderPresenter extends SliderPresenter {
|
|
60
|
+
private readonly trackNode: FlexBox;
|
|
61
|
+
private readonly fillNode: FlexBox;
|
|
62
|
+
private readonly thumbNode: FlexBox;
|
|
63
|
+
|
|
64
|
+
constructor() {
|
|
65
|
+
const root = new FlexBox();
|
|
66
|
+
super(root, new SliderPresenterMetrics(18.0, 6.0, 2.0));
|
|
67
|
+
const trackNode = new FlexBox().positionAbsolute();
|
|
68
|
+
const fillNode = new FlexBox().positionAbsolute();
|
|
69
|
+
const thumbNode = new FlexBox()
|
|
70
|
+
.positionAbsolute()
|
|
71
|
+
.width(18.0, Unit.Pixel)
|
|
72
|
+
.height(18.0, Unit.Pixel);
|
|
73
|
+
this.trackNode = trackNode;
|
|
74
|
+
this.fillNode = fillNode;
|
|
75
|
+
this.thumbNode = thumbNode;
|
|
76
|
+
root
|
|
77
|
+
.child(trackNode)
|
|
78
|
+
.child(fillNode)
|
|
79
|
+
.child(thumbNode);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
layout(state: SliderVisualState, length: f32): void {
|
|
83
|
+
const metrics = this.metrics;
|
|
84
|
+
const available = length > metrics.thumbSize ? length - metrics.thumbSize : 0.0;
|
|
85
|
+
const fraction = clamp(state.normalizedValue, 0.0, 1.0);
|
|
86
|
+
const crossAxisInset = metrics.crossAxisExtra * 0.5;
|
|
87
|
+
if (state.orientation == Orientation.Vertical) {
|
|
88
|
+
this.root
|
|
89
|
+
.width(metrics.thumbSize + metrics.crossAxisExtra, Unit.Pixel)
|
|
90
|
+
.height(length, Unit.Pixel);
|
|
91
|
+
this.trackNode
|
|
92
|
+
.width(metrics.trackThickness, Unit.Pixel)
|
|
93
|
+
.height(available, Unit.Pixel)
|
|
94
|
+
.position(
|
|
95
|
+
crossAxisInset + ((metrics.thumbSize - metrics.trackThickness) * 0.5),
|
|
96
|
+
metrics.thumbSize * 0.5,
|
|
97
|
+
);
|
|
98
|
+
this.fillNode
|
|
99
|
+
.width(metrics.trackThickness, Unit.Pixel)
|
|
100
|
+
.height(available * fraction, Unit.Pixel)
|
|
101
|
+
.position(
|
|
102
|
+
crossAxisInset + ((metrics.thumbSize - metrics.trackThickness) * 0.5),
|
|
103
|
+
metrics.thumbSize * 0.5 + (available * (1.0 - fraction)),
|
|
104
|
+
);
|
|
105
|
+
this.thumbNode.position(
|
|
106
|
+
crossAxisInset,
|
|
107
|
+
available - (available * fraction),
|
|
108
|
+
);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.root
|
|
113
|
+
.width(length, Unit.Pixel)
|
|
114
|
+
.height(metrics.thumbSize + metrics.crossAxisExtra, Unit.Pixel);
|
|
115
|
+
this.trackNode
|
|
116
|
+
.width(available, Unit.Pixel)
|
|
117
|
+
.height(metrics.trackThickness, Unit.Pixel)
|
|
118
|
+
.position(
|
|
119
|
+
metrics.thumbSize * 0.5,
|
|
120
|
+
crossAxisInset + ((metrics.thumbSize - metrics.trackThickness) * 0.5),
|
|
121
|
+
);
|
|
122
|
+
this.fillNode
|
|
123
|
+
.width(available * fraction, Unit.Pixel)
|
|
124
|
+
.height(metrics.trackThickness, Unit.Pixel)
|
|
125
|
+
.position(
|
|
126
|
+
metrics.thumbSize * 0.5,
|
|
127
|
+
crossAxisInset + ((metrics.thumbSize - metrics.trackThickness) * 0.5),
|
|
128
|
+
);
|
|
129
|
+
this.thumbNode.position(
|
|
130
|
+
available * fraction,
|
|
131
|
+
crossAxisInset,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
apply(theme: Theme, state: SliderVisualState): void {
|
|
136
|
+
const accent = state.dragging
|
|
137
|
+
? theme.colors.accentPressed
|
|
138
|
+
: (state.hovered ? theme.colors.accentHovered : theme.colors.accent);
|
|
139
|
+
const metrics = this.metrics;
|
|
140
|
+
const trackRadius = metrics.trackThickness * 0.5;
|
|
141
|
+
this.trackNode.cornerRadius(trackRadius);
|
|
142
|
+
this.trackNode.bgColor(theme.colors.scrollbarTrack);
|
|
143
|
+
this.fillNode.cornerRadius(trackRadius);
|
|
144
|
+
this.fillNode.bgColor(accent);
|
|
145
|
+
this.thumbNode.cornerRadius(metrics.thumbSize * 0.5);
|
|
146
|
+
this.thumbNode.bgColor(accent);
|
|
147
|
+
this.thumbNode.border(1.0, theme.colors.surface, BorderStyle.Solid);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class DefaultSliderTemplate extends SliderTemplate {
|
|
152
|
+
create(): SliderPresenter {
|
|
153
|
+
return new DefaultSliderPresenter();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export const defaultSliderTemplate = new DefaultSliderTemplate();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { BorderStyle, Unit } from "../../core/ffi";
|
|
2
|
+
import { Theme } from "../../core/Theme";
|
|
3
|
+
import { FlexBox } from "../../nodes";
|
|
4
|
+
import {
|
|
5
|
+
PressableIndicatorMetrics,
|
|
6
|
+
PressableIndicatorPresenter,
|
|
7
|
+
PressableIndicatorVisualState,
|
|
8
|
+
} from "./PressableIndicatorPresenter";
|
|
9
|
+
|
|
10
|
+
export class SwitchIndicatorVisualState extends PressableIndicatorVisualState {
|
|
11
|
+
constructor(
|
|
12
|
+
readonly checked: bool,
|
|
13
|
+
hovered: bool,
|
|
14
|
+
pressed: bool,
|
|
15
|
+
focused: bool,
|
|
16
|
+
enabled: bool,
|
|
17
|
+
) {
|
|
18
|
+
super(hovered, pressed, focused, enabled);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export abstract class SwitchIndicatorPresenter extends PressableIndicatorPresenter {
|
|
23
|
+
protected constructor(root: FlexBox, metrics: PressableIndicatorMetrics) {
|
|
24
|
+
super(root, metrics);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
abstract apply(theme: Theme, state: SwitchIndicatorVisualState): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export abstract class SwitchIndicatorTemplate {
|
|
31
|
+
abstract create(): SwitchIndicatorPresenter;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class DefaultSwitchIndicatorPresenter extends SwitchIndicatorPresenter {
|
|
35
|
+
private readonly thumbNode: FlexBox;
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
const root = new FlexBox()
|
|
39
|
+
.width(44.0, Unit.Pixel)
|
|
40
|
+
.height(26.0, Unit.Pixel)
|
|
41
|
+
.clipToBounds(true);
|
|
42
|
+
super(root, new PressableIndicatorMetrics(44.0, 26.0));
|
|
43
|
+
const thumbNode = new FlexBox()
|
|
44
|
+
.positionAbsolute()
|
|
45
|
+
.position(3.0, 2.0)
|
|
46
|
+
.width(20.0, Unit.Pixel)
|
|
47
|
+
.height(20.0, Unit.Pixel);
|
|
48
|
+
this.thumbNode = thumbNode;
|
|
49
|
+
root.alignItems(1).child(thumbNode);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
apply(theme: Theme, state: SwitchIndicatorVisualState): void {
|
|
53
|
+
const trackColor = state.checked
|
|
54
|
+
? (state.pressed ? theme.colors.accentPressed : (state.hovered ? theme.colors.accentHovered : theme.colors.accent))
|
|
55
|
+
: (state.hovered ? theme.colors.background : theme.colors.surface);
|
|
56
|
+
this.root.cornerRadius(13.0);
|
|
57
|
+
this.root.border(1.0, state.checked ? trackColor : theme.colors.border, BorderStyle.Solid);
|
|
58
|
+
this.root.bgColor(trackColor);
|
|
59
|
+
this.thumbNode.position(state.checked ? 21.0 : 3.0, 2.0);
|
|
60
|
+
this.thumbNode.cornerRadius(10.0);
|
|
61
|
+
this.thumbNode.bgColor(theme.colors.surface);
|
|
62
|
+
this.thumbNode.border(1.0, state.checked ? trackColor : theme.colors.border, BorderStyle.Solid);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class DefaultSwitchIndicatorTemplate extends SwitchIndicatorTemplate {
|
|
67
|
+
create(): SwitchIndicatorPresenter {
|
|
68
|
+
return new DefaultSwitchIndicatorPresenter();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const defaultSwitchIndicatorTemplate = new DefaultSwitchIndicatorTemplate();
|