@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.
Files changed (137) hide show
  1. package/LICENSE.md +7 -0
  2. package/browser/src/common-harness/host-imports.ts +430 -0
  3. package/browser/src/common-harness/interop.ts +39 -0
  4. package/browser/src/common-harness/managed-harness-bitmap-host.ts +92 -0
  5. package/browser/src/common-harness/managed-harness-fetch-host.ts +201 -0
  6. package/browser/src/common-harness/managed-harness-file-host.ts +1101 -0
  7. package/browser/src/common-harness/managed-harness-file-payloads.ts +143 -0
  8. package/browser/src/common-harness/managed-harness-file-types.ts +106 -0
  9. package/browser/src/common-harness/managed-harness-session.ts +15 -0
  10. package/browser/src/common-harness/managed-harness.ts +1323 -0
  11. package/browser/src/common-harness/managed-history.ts +168 -0
  12. package/browser/src/common-harness/persisted-restore-policy.ts +50 -0
  13. package/browser/src/common-harness/persisted-ui-state-controller.ts +309 -0
  14. package/browser/src/common-harness/text-session-bridge.ts +452 -0
  15. package/browser/src/common-harness/types.ts +205 -0
  16. package/browser/src/common-harness/ui-chrome.ts +191 -0
  17. package/browser/src/common-harness/ui-imports.ts +529 -0
  18. package/browser/src/common-harness/wasm-module-cache.ts +47 -0
  19. package/browser/src/common-harness.ts +27 -0
  20. package/browser/src/file-processing-worker.ts +89 -0
  21. package/browser/src/host-events.ts +97 -0
  22. package/browser/src/host-services.ts +203 -0
  23. package/browser/src/index.ts +62 -0
  24. package/browser/src/persisted-ui-state.ts +206 -0
  25. package/browser/src/routed-harness.ts +198 -0
  26. package/browser/src/worker-bootstrap.ts +483 -0
  27. package/browser/src/worker-manager.ts +230 -0
  28. package/browser/src/worker-types.ts +50 -0
  29. package/package.json +89 -0
  30. package/scripts/build-demo-as.sh +91 -0
  31. package/scripts/build.sh +325 -0
  32. package/scripts/generate-host-events.ts +175 -0
  33. package/scripts/generate-host-services.ts +157 -0
  34. package/src/Fui.ts +205 -0
  35. package/src/FuiExports.ts +55 -0
  36. package/src/FuiPrimitives.ts +15 -0
  37. package/src/FuiWorker.ts +3 -0
  38. package/src/FuiWorkerExports.ts +6 -0
  39. package/src/bindings/ui.ts +531 -0
  40. package/src/color.ts +86 -0
  41. package/src/controls/AntiSelectionArea.ts +23 -0
  42. package/src/controls/Button.ts +750 -0
  43. package/src/controls/Checkbox.ts +181 -0
  44. package/src/controls/ContextMenu.ts +885 -0
  45. package/src/controls/ControlTemplateSet.ts +37 -0
  46. package/src/controls/Dialog.ts +355 -0
  47. package/src/controls/Dropdown.ts +856 -0
  48. package/src/controls/Form.ts +110 -0
  49. package/src/controls/NavLink.ts +211 -0
  50. package/src/controls/Popup.ts +129 -0
  51. package/src/controls/ProgressBar.ts +180 -0
  52. package/src/controls/RadioButton.ts +135 -0
  53. package/src/controls/RadioGroup.ts +244 -0
  54. package/src/controls/SelectionArea.ts +75 -0
  55. package/src/controls/Slider.ts +471 -0
  56. package/src/controls/Switch.ts +132 -0
  57. package/src/controls/TextArea.ts +20 -0
  58. package/src/controls/TextInput.ts +7 -0
  59. package/src/controls/index.ts +18 -0
  60. package/src/controls/internal/ButtonPresenter.ts +95 -0
  61. package/src/controls/internal/CheckboxIndicatorPresenter.ts +93 -0
  62. package/src/controls/internal/DropdownChevronPresenter.ts +67 -0
  63. package/src/controls/internal/DropdownFieldPresenter.ts +110 -0
  64. package/src/controls/internal/DropdownOptionRowPresenter.ts +82 -0
  65. package/src/controls/internal/PopupPresenter.ts +198 -0
  66. package/src/controls/internal/PressableIndicatorPresenter.ts +32 -0
  67. package/src/controls/internal/PressableLabeledControl.ts +221 -0
  68. package/src/controls/internal/RadioIndicatorPresenter.ts +73 -0
  69. package/src/controls/internal/SliderPresenter.ts +157 -0
  70. package/src/controls/internal/SwitchIndicatorPresenter.ts +72 -0
  71. package/src/controls/internal/TextInputCore.ts +695 -0
  72. package/src/controls/internal/TextInputPresenter.ts +72 -0
  73. package/src/controls/templating.ts +54 -0
  74. package/src/core/Action.ts +94 -0
  75. package/src/core/Actions.ts +37 -0
  76. package/src/core/Animation.ts +412 -0
  77. package/src/core/Application.ts +328 -0
  78. package/src/core/Assets.ts +264 -0
  79. package/src/core/AttachedProperties.ts +32 -0
  80. package/src/core/Bitmap.ts +70 -0
  81. package/src/core/BoundCallback.ts +104 -0
  82. package/src/core/Callbacks.ts +17 -0
  83. package/src/core/ContextMenuManager.ts +466 -0
  84. package/src/core/DebugApi.ts +30 -0
  85. package/src/core/Disposable.ts +10 -0
  86. package/src/core/DragDropManager.ts +179 -0
  87. package/src/core/DragGesture.ts +184 -0
  88. package/src/core/DynamicAssetIds.ts +24 -0
  89. package/src/core/Errors.ts +48 -0
  90. package/src/core/EventRouter.ts +408 -0
  91. package/src/core/ExternalDropManager.ts +122 -0
  92. package/src/core/Fetch.ts +264 -0
  93. package/src/core/FetchFfi.ts +15 -0
  94. package/src/core/File.ts +1002 -0
  95. package/src/core/FocusAdornerManager.ts +263 -0
  96. package/src/core/FocusVisibility.ts +36 -0
  97. package/src/core/FrameScheduler.ts +28 -0
  98. package/src/core/KeyboardScroll.ts +161 -0
  99. package/src/core/KeyboardScrollTracker.ts +386 -0
  100. package/src/core/Logger.ts +80 -0
  101. package/src/core/Navigation.ts +13 -0
  102. package/src/core/Node.ts +1708 -0
  103. package/src/core/PersistedState.ts +102 -0
  104. package/src/core/PersistedUiState.ts +142 -0
  105. package/src/core/Platform.ts +219 -0
  106. package/src/core/Signal.ts +89 -0
  107. package/src/core/Theme.ts +365 -0
  108. package/src/core/Timers.ts +129 -0
  109. package/src/core/ToolTip.ts +122 -0
  110. package/src/core/ToolTipManager.ts +459 -0
  111. package/src/core/Transitions.ts +34 -0
  112. package/src/core/Typography.ts +204 -0
  113. package/src/core/Worker.ts +196 -0
  114. package/src/core/bind.ts +37 -0
  115. package/src/core/event_exports.ts +596 -0
  116. package/src/core/ffi.ts +728 -0
  117. package/src/host-services/runtime.ts +25 -0
  118. package/src/nodes/FlexBox.ts +789 -0
  119. package/src/nodes/GradientStop.ts +9 -0
  120. package/src/nodes/Grid.ts +183 -0
  121. package/src/nodes/Image.ts +189 -0
  122. package/src/nodes/Portal.ts +14 -0
  123. package/src/nodes/RichText.ts +312 -0
  124. package/src/nodes/ScrollBar.ts +570 -0
  125. package/src/nodes/ScrollBox.ts +415 -0
  126. package/src/nodes/ScrollState.ts +10 -0
  127. package/src/nodes/ScrollView.ts +511 -0
  128. package/src/nodes/Svg.ts +142 -0
  129. package/src/nodes/Text.ts +145 -0
  130. package/src/nodes/TextCore.ts +558 -0
  131. package/src/nodes/VirtualList.ts +431 -0
  132. package/src/nodes/helpers.ts +25 -0
  133. package/src/nodes/index.ts +14 -0
  134. package/src/tsconfig.json +7 -0
  135. package/src/worker/Worker.ts +169 -0
  136. package/src/worker/WorkerJob.ts +65 -0
  137. package/src/worker/ffi.ts +23 -0
@@ -0,0 +1,750 @@
1
+ import * as ui from "../bindings/ui";
2
+ import {
3
+ AlignItems,
4
+ BorderStyle,
5
+ CursorStyle,
6
+ FlexDirection,
7
+ HandleValue,
8
+ PointerEventType,
9
+ KeyEventType,
10
+ JustifyContent,
11
+ SemanticRole,
12
+ } from "../core/ffi";
13
+ import { Callback0, Callback1, Handler0, Handler1 } from "../core/BoundCallback";
14
+ import { HandlerAction } from "../core/Action";
15
+ import { bind0, bind1 } from "../core/bind";
16
+ import { Disposable, disposeAll } from "../core/Disposable";
17
+ import { FocusAdornerManager } from "../core/FocusAdornerManager";
18
+ import { keyboardFocusVisible } from "../core/FocusVisibility";
19
+ import { Signal } from "../core/Signal";
20
+ import { Theme, activeTheme } from "../core/Theme";
21
+ import { FontFamily, FontStyle, FontWeight } from "../core/Typography";
22
+ import { FlexBox, TextCore } from "../nodes";
23
+ import { getControlTemplates } from "./ControlTemplateSet";
24
+ import {
25
+ ButtonPresenter,
26
+ ButtonTemplate,
27
+ ButtonVisualState,
28
+ defaultButtonTemplate,
29
+ } from "./internal/ButtonPresenter";
30
+
31
+ function isSpaceKey(key: string): bool {
32
+ return key == " " || key == "Space" || key == "Spacebar";
33
+ }
34
+
35
+ function isActivationKey(key: string): bool {
36
+ return key == "Enter" || isSpaceKey(key);
37
+ }
38
+
39
+ class ButtonPresenterHostState {
40
+ constructor(
41
+ readonly backgroundOverridden: bool,
42
+ readonly normalBackgroundColorValue: u32,
43
+ readonly cornerRadiusOverridden: bool,
44
+ readonly focusCornerTopLeft: f32,
45
+ readonly focusCornerTopRight: f32,
46
+ readonly focusCornerBottomRight: f32,
47
+ readonly focusCornerBottomLeft: f32,
48
+ readonly borderOverridden: bool,
49
+ readonly borderWidthValue: f32,
50
+ readonly borderColorValue: u32,
51
+ readonly borderStyleValue: BorderStyle,
52
+ readonly borderDashOnValue: f32,
53
+ readonly borderDashOffValue: f32,
54
+ readonly borderDashedValue: bool,
55
+ readonly shadowOverridden: bool,
56
+ readonly shadowColorValue: u32,
57
+ readonly shadowOffsetXValue: f32,
58
+ readonly shadowOffsetYValue: f32,
59
+ readonly shadowBlurValue: f32,
60
+ readonly shadowSpreadValue: f32,
61
+ readonly paddingOverridden: bool,
62
+ readonly paddingLeftValue: f32,
63
+ readonly paddingTopValue: f32,
64
+ readonly paddingRightValue: f32,
65
+ readonly paddingBottomValue: f32,
66
+ ) {}
67
+ }
68
+
69
+ export class Button extends FlexBox {
70
+ private presenter: ButtonPresenter = changetype<ButtonPresenter>(0);
71
+ private labelNode: TextCore = changetype<TextCore>(0);
72
+ private labelValue: string;
73
+ private readonly hovered: Signal<bool> = new Signal<bool>(false);
74
+ private readonly pressed: Signal<bool> = new Signal<bool>(false);
75
+ private readonly disposables: Array<Disposable> = new Array<Disposable>();
76
+ private action: (() => void) | null = null;
77
+ private actionBinding: Callback0 | null = null;
78
+ private hoverChanged: ((hovered: bool) => void) | null = null;
79
+ private hoverChangedBinding: Callback1<bool> | null = null;
80
+ private disposed: bool = false;
81
+ private focusedState: bool = false;
82
+ private opacityBeforeDisabled: f32 = 1.0;
83
+ private normalBackgroundColorValue: u32 = activeTheme.value.colors.accent;
84
+ private hoverBackgroundColorValue: u32 = activeTheme.value.colors.accentHovered;
85
+ private pressedBackgroundColorValue: u32 = activeTheme.value.colors.accentPressed;
86
+ private backgroundOverridden: bool = false;
87
+ private hoverBackgroundOverridden: bool = false;
88
+ private pressedBackgroundOverridden: bool = false;
89
+ private borderOverridden: bool = false;
90
+ private cornerRadiusOverridden: bool = false;
91
+ private paddingOverridden: bool = false;
92
+ private fontOverridden: bool = false;
93
+ private textColorOverridden: bool = false;
94
+ private shadowOverridden: bool = false;
95
+ private keyboardArmedKey: string | null = null;
96
+ private focusCornerTopLeft: f32 = 0.0;
97
+ private focusCornerTopRight: f32 = 0.0;
98
+ private focusCornerBottomRight: f32 = 0.0;
99
+ private focusCornerBottomLeft: f32 = 0.0;
100
+ private borderWidthValue: f32 = 1.0;
101
+ private borderColorValue: u32 = activeTheme.value.colors.border;
102
+ private borderStyleValue: BorderStyle = BorderStyle.Solid;
103
+ private borderDashOnValue: f32 = 0.0;
104
+ private borderDashOffValue: f32 = 0.0;
105
+ private borderDashedValue: bool = false;
106
+ private paddingLeftValue: f32 = activeTheme.value.spacing.md;
107
+ private paddingTopValue: f32 = activeTheme.value.spacing.sm;
108
+ private paddingRightValue: f32 = activeTheme.value.spacing.md;
109
+ private paddingBottomValue: f32 = activeTheme.value.spacing.sm;
110
+ private fontFamilyValue: FontFamily = activeTheme.value.fonts.bodyFamily;
111
+ private fontWeightValue: FontWeight = FontWeight.Regular;
112
+ private fontStyleValue: FontStyle = FontStyle.Normal;
113
+ private fontSizeValue: f32 = activeTheme.value.fonts.sizeBody;
114
+ private fontIdValue: u32 = 0;
115
+ private hasFontIdOverride: bool = false;
116
+ private textColorValue: u32 = activeTheme.value.colors.textPrimary;
117
+ private shadowColorValue: u32 = 0x00000000;
118
+ private shadowOffsetXValue: f32 = 0.0;
119
+ private shadowOffsetYValue: f32 = 0.0;
120
+ private shadowBlurValue: f32 = 0.0;
121
+ private shadowSpreadValue: f32 = 0.0;
122
+ private templateValue: ButtonTemplate | null = null;
123
+ private presenterNeedsRefresh: bool = false;
124
+
125
+ constructor(label: string) {
126
+ super();
127
+ this.labelValue = label;
128
+ this.presenter = this.createPresenter(null);
129
+ this.labelNode = this.presenter.labelNode;
130
+ this.presenter.bindHost(this);
131
+ this.labelNode.text(label);
132
+ this.addChildNode(this.presenter.contentRoot);
133
+ this.track(this.hovered.addAction(new HandlerAction<Button, bool>(this, (button: Button, _value: bool): void => {
134
+ button.handleStateSignalChanged();
135
+ })));
136
+ this.track(this.pressed.addAction(new HandlerAction<Button, bool>(this, (button: Button, _value: bool): void => {
137
+ button.handleStateSignalChanged();
138
+ })));
139
+ this.track(activeTheme.addAction(new HandlerAction<Button, Theme>(this, (button: Button, _value: Theme): void => {
140
+ button.handleThemeSignalChanged();
141
+ })));
142
+ this.track(keyboardFocusVisible.addAction(new HandlerAction<Button, bool>(this, (button: Button, _visible: bool): void => {
143
+ button.syncFocusChrome();
144
+ })));
145
+
146
+ this.semanticRole(SemanticRole.Button);
147
+ this.semanticLabel(label);
148
+ this.cursor(CursorStyle.Pointer);
149
+ this.focusable(true);
150
+ this.requireInteractive();
151
+ this.flexDirection(FlexDirection.Row);
152
+ this.justifyContent(JustifyContent.Center);
153
+ this.alignItems(AlignItems.Center);
154
+ this.syncThemeState(activeTheme.value);
155
+ this.applyBackground();
156
+ }
157
+
158
+ onClick(cb: () => void): this {
159
+ this.action = cb;
160
+ this.actionBinding = null;
161
+ return this;
162
+ }
163
+
164
+ bindClick<Owner>(owner: Owner, handler: Handler0<Owner>): this {
165
+ this.action = null;
166
+ this.actionBinding = bind0<Owner>(owner, handler);
167
+ return this;
168
+ }
169
+
170
+ onClickWith<Owner>(owner: Owner, handler: Handler0<Owner>): this {
171
+ this.bindClick(owner, handler);
172
+ return this;
173
+ }
174
+
175
+ onHoverChanged(cb: (hovered: bool) => void): this {
176
+ this.hoverChanged = cb;
177
+ this.hoverChangedBinding = null;
178
+ return this;
179
+ }
180
+
181
+ bindHoverChanged<Owner>(owner: Owner, handler: Handler1<Owner, bool>): this {
182
+ this.hoverChanged = null;
183
+ this.hoverChangedBinding = bind1<Owner, bool>(owner, handler);
184
+ return this;
185
+ }
186
+
187
+ onHoverChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, bool>): this {
188
+ this.bindHoverChanged(owner, handler);
189
+ return this;
190
+ }
191
+
192
+ label(label: string): this {
193
+ this.labelValue = label;
194
+ this.semanticLabel(label);
195
+ this.labelNode.text(label);
196
+ return this;
197
+ }
198
+
199
+ template(template: ButtonTemplate | null): this {
200
+ this.templateValue = template;
201
+ this.replacePresenter(this.createPresenter(template));
202
+ this.handleThemeSignalChanged();
203
+ return this;
204
+ }
205
+
206
+ bgColor(color: u32): this {
207
+ this.backgroundOverridden = true;
208
+ this.normalBackgroundColorValue = color;
209
+ this.applyBackground();
210
+ return this;
211
+ }
212
+
213
+ hoverBgColor(color: u32): this {
214
+ this.hoverBackgroundOverridden = true;
215
+ this.hoverBackgroundColorValue = color;
216
+ this.applyBackground();
217
+ return this;
218
+ }
219
+
220
+ pressedBgColor(color: u32): this {
221
+ this.pressedBackgroundOverridden = true;
222
+ this.pressedBackgroundColorValue = color;
223
+ this.applyBackground();
224
+ return this;
225
+ }
226
+
227
+ cornerRadius(radius: f32): this {
228
+ this.cornerRadiusOverridden = true;
229
+ this.focusCornerTopLeft = radius;
230
+ this.focusCornerTopRight = radius;
231
+ this.focusCornerBottomRight = radius;
232
+ this.focusCornerBottomLeft = radius;
233
+ this.applyCurrentCorners();
234
+ this.syncFocusChrome();
235
+ return this;
236
+ }
237
+
238
+ corners(tl: f32, tr: f32, br: f32, bl: f32): this {
239
+ this.cornerRadiusOverridden = true;
240
+ this.focusCornerTopLeft = tl;
241
+ this.focusCornerTopRight = tr;
242
+ this.focusCornerBottomRight = br;
243
+ this.focusCornerBottomLeft = bl;
244
+ this.applyCurrentCorners();
245
+ this.syncFocusChrome();
246
+ return this;
247
+ }
248
+
249
+ border(width: f32, color: u32, style: BorderStyle = BorderStyle.Solid): this {
250
+ this.borderOverridden = true;
251
+ this.borderWidthValue = width;
252
+ this.borderColorValue = color;
253
+ this.borderStyleValue = style;
254
+ this.borderDashedValue = false;
255
+ this.applyCurrentBorder();
256
+ return this;
257
+ }
258
+
259
+ borderDashed(on: f32, off: f32): this {
260
+ this.borderOverridden = true;
261
+ this.borderDashOnValue = on;
262
+ this.borderDashOffValue = off;
263
+ this.borderDashedValue = true;
264
+ super.borderDashed(on, off);
265
+ return this;
266
+ }
267
+
268
+ dropShadow(color: u32, offsetX: f32, offsetY: f32, blur: f32, spread: f32 = 0.0): this {
269
+ this.shadowOverridden = true;
270
+ this.shadowColorValue = color;
271
+ this.shadowOffsetXValue = offsetX;
272
+ this.shadowOffsetYValue = offsetY;
273
+ this.shadowBlurValue = blur;
274
+ this.shadowSpreadValue = spread;
275
+ this.applyCurrentShadow();
276
+ this.syncFocusChrome();
277
+ return this;
278
+ }
279
+
280
+ padding(left: f32, top: f32 = left, right: f32 = left, bottom: f32 = top): this {
281
+ this.paddingOverridden = true;
282
+ this.paddingLeftValue = left;
283
+ this.paddingTopValue = top;
284
+ this.paddingRightValue = right;
285
+ this.paddingBottomValue = bottom;
286
+ this.applyCurrentPadding();
287
+ return this;
288
+ }
289
+
290
+ font(fontId: u32, size: f32): this {
291
+ this.fontOverridden = true;
292
+ this.hasFontIdOverride = true;
293
+ this.fontIdValue = fontId;
294
+ this.fontSizeValue = size;
295
+ this.labelNode.font(fontId, size);
296
+ return this;
297
+ }
298
+
299
+ fontFamily(family: FontFamily): this {
300
+ this.fontOverridden = true;
301
+ this.hasFontIdOverride = false;
302
+ this.fontFamilyValue = family;
303
+ this.labelNode.fontFamily(family);
304
+ return this;
305
+ }
306
+
307
+ fontWeight(weight: FontWeight): this {
308
+ this.fontOverridden = true;
309
+ this.hasFontIdOverride = false;
310
+ this.fontWeightValue = weight;
311
+ this.labelNode.fontWeight(weight);
312
+ return this;
313
+ }
314
+
315
+ fontStyle(style: FontStyle): this {
316
+ this.fontOverridden = true;
317
+ this.hasFontIdOverride = false;
318
+ this.fontStyleValue = style;
319
+ this.labelNode.fontStyle(style);
320
+ return this;
321
+ }
322
+
323
+ fontSize(size: f32): this {
324
+ this.fontOverridden = true;
325
+ this.fontSizeValue = size;
326
+ if (this.hasFontIdOverride) {
327
+ this.labelNode.font(this.fontIdValue, size);
328
+ return this;
329
+ }
330
+ this.labelNode.fontSize(size);
331
+ return this;
332
+ }
333
+
334
+ textColor(color: u32): this {
335
+ this.textColorOverridden = true;
336
+ this.textColorValue = color;
337
+ this.labelNode.textColor(color);
338
+ return this;
339
+ }
340
+
341
+ _setAction(cb: () => void): void {
342
+ this.action = cb;
343
+ this.actionBinding = null;
344
+ }
345
+
346
+ beginPress(): void {
347
+ this.pressed.value = true;
348
+ }
349
+
350
+ endPress(fireAction: bool = true): void {
351
+ const wasPressed = this.pressed.value;
352
+ this.pressed.value = false;
353
+ if (!wasPressed || !fireAction) {
354
+ return;
355
+ }
356
+ const callback = this.action;
357
+ if (callback !== null) {
358
+ callback();
359
+ }
360
+ const binding = this.actionBinding;
361
+ if (binding !== null) {
362
+ binding.invoke();
363
+ }
364
+ }
365
+
366
+ cancelPress(): void {
367
+ this.pressed.value = false;
368
+ }
369
+
370
+ build(): u64 {
371
+ if (this.presenterNeedsRefresh) {
372
+ this.recreatePresenterTree();
373
+ }
374
+ return super.build();
375
+ }
376
+
377
+ dispose(): void {
378
+ this.disposeControl();
379
+ super.dispose();
380
+ this.presenterNeedsRefresh = true;
381
+ }
382
+
383
+ private disposeControl(): void {
384
+ if (this.disposed) {
385
+ return;
386
+ }
387
+ this.disposed = true;
388
+ disposeAll(this.disposables);
389
+ }
390
+
391
+ _handlePointerEvent(eventType: PointerEventType, x: f32, y: f32, modifiers: u32): void {
392
+ if (!this.isEnabled) {
393
+ return;
394
+ }
395
+ super._handlePointerEvent(eventType, x, y, modifiers);
396
+ if (eventType == PointerEventType.Enter) {
397
+ this.setHovered(true);
398
+ return;
399
+ }
400
+ if (eventType == PointerEventType.Leave) {
401
+ this.setHovered(false);
402
+ if (this.keyboardArmedKey === null) {
403
+ this.cancelPress();
404
+ }
405
+ return;
406
+ }
407
+ if (eventType == PointerEventType.Down) {
408
+ this.setHovered(true);
409
+ this.beginPress();
410
+ return;
411
+ }
412
+ if (eventType == PointerEventType.Up && this.pressed.value) {
413
+ this.endPress(true);
414
+ }
415
+ }
416
+
417
+ _handleFocusChanged(focused: bool): void {
418
+ super._handleFocusChanged(focused);
419
+ if (!focused && this.keyboardArmedKey !== null) {
420
+ this.cancelKeyboardPress();
421
+ }
422
+ if (!this.isEnabled) {
423
+ if (this.focusedState) {
424
+ this.focusedState = false;
425
+ this.handleThemeSignalChanged();
426
+ }
427
+ return;
428
+ }
429
+ if (this.focusedState == focused) {
430
+ return;
431
+ }
432
+ this.focusedState = focused;
433
+ this.handleThemeSignalChanged();
434
+ }
435
+
436
+ _handleKeyEvent(eventType: KeyEventType, key: string, modifiers: u32): bool {
437
+ const callbackHandled = super._handleKeyEvent(eventType, key, modifiers);
438
+ if (!this.isEnabled || modifiers != 0 || !isActivationKey(key)) {
439
+ return callbackHandled;
440
+ }
441
+ if (eventType == KeyEventType.Down) {
442
+ if (this.keyboardArmedKey !== null) {
443
+ return true;
444
+ }
445
+ this.keyboardArmedKey = key;
446
+ this.beginPress();
447
+ return true;
448
+ }
449
+ if (eventType == KeyEventType.Up && this.keyboardArmedKey !== null && changetype<string>(this.keyboardArmedKey) == key) {
450
+ this.keyboardArmedKey = null;
451
+ this.endPress(true);
452
+ return true;
453
+ }
454
+ return callbackHandled;
455
+ }
456
+
457
+ handleStateSignalChanged(): void {
458
+ if (this.disposed) {
459
+ return;
460
+ }
461
+ this.syncThemeState(activeTheme.value);
462
+ this.applyBackground();
463
+ }
464
+
465
+ handleThemeSignalChanged(): void {
466
+ if (this.disposed) {
467
+ return;
468
+ }
469
+ const theme = activeTheme.value;
470
+ this.syncThemeState(theme);
471
+ this.applyBackground();
472
+ }
473
+
474
+ private computeBackground(): u32 {
475
+ if (!this.isEnabled) {
476
+ return this.normalBackgroundColorValue;
477
+ }
478
+ if (this.pressed.value) {
479
+ return this.pressedBackgroundColorValue;
480
+ }
481
+ if (this.hovered.value) {
482
+ return this.hoverBackgroundColorValue;
483
+ }
484
+ return this.normalBackgroundColorValue;
485
+ }
486
+
487
+ private setHovered(next: bool): void {
488
+ if (!this.hovered.set(next)) {
489
+ return;
490
+ }
491
+ const callback = this.hoverChanged;
492
+ if (callback !== null) {
493
+ callback(next);
494
+ }
495
+ const binding = this.hoverChangedBinding;
496
+ if (binding !== null) {
497
+ binding.invoke(next);
498
+ }
499
+ }
500
+
501
+ private syncThemeState(theme: Theme): void {
502
+ if (!this.backgroundOverridden) {
503
+ this.normalBackgroundColorValue = theme.colors.accent;
504
+ }
505
+ if (!this.hoverBackgroundOverridden) {
506
+ this.hoverBackgroundColorValue = theme.colors.accentHovered;
507
+ }
508
+ if (!this.pressedBackgroundOverridden) {
509
+ this.pressedBackgroundColorValue = theme.colors.accentPressed;
510
+ }
511
+ if (!this.cornerRadiusOverridden) {
512
+ this.focusCornerTopLeft = theme.spacing.sm;
513
+ this.focusCornerTopRight = theme.spacing.sm;
514
+ this.focusCornerBottomRight = theme.spacing.sm;
515
+ this.focusCornerBottomLeft = theme.spacing.sm;
516
+ }
517
+ if (!this.borderOverridden) {
518
+ this.borderWidthValue = 1.0;
519
+ this.borderColorValue = theme.colors.border;
520
+ this.borderStyleValue = BorderStyle.Solid;
521
+ this.borderDashedValue = false;
522
+ }
523
+ if (!this.shadowOverridden) {
524
+ this.shadowColorValue = 0x00000000;
525
+ this.shadowOffsetXValue = 0.0;
526
+ this.shadowOffsetYValue = 0.0;
527
+ this.shadowBlurValue = 0.0;
528
+ this.shadowSpreadValue = 0.0;
529
+ }
530
+ if (!this.paddingOverridden) {
531
+ const paddingY = theme.spacing.sm;
532
+ const paddingX = theme.spacing.md;
533
+ this.paddingLeftValue = paddingX;
534
+ this.paddingTopValue = paddingY;
535
+ this.paddingRightValue = paddingX;
536
+ this.paddingBottomValue = paddingY;
537
+ }
538
+ if (!this.fontOverridden) {
539
+ this.hasFontIdOverride = false;
540
+ this.fontFamilyValue = theme.fonts.bodyFamily;
541
+ this.fontWeightValue = FontWeight.Regular;
542
+ this.fontStyleValue = FontStyle.Normal;
543
+ this.fontSizeValue = theme.fonts.sizeBody;
544
+ }
545
+ if (!this.textColorOverridden) {
546
+ this.textColorValue = theme.colors.textPrimary;
547
+ }
548
+ const presenterHostState = new ButtonPresenterHostState(
549
+ this.backgroundOverridden,
550
+ this.normalBackgroundColorValue,
551
+ this.cornerRadiusOverridden,
552
+ this.focusCornerTopLeft,
553
+ this.focusCornerTopRight,
554
+ this.focusCornerBottomRight,
555
+ this.focusCornerBottomLeft,
556
+ this.borderOverridden,
557
+ this.borderWidthValue,
558
+ this.borderColorValue,
559
+ this.borderStyleValue,
560
+ this.borderDashOnValue,
561
+ this.borderDashOffValue,
562
+ this.borderDashedValue,
563
+ this.shadowOverridden,
564
+ this.shadowColorValue,
565
+ this.shadowOffsetXValue,
566
+ this.shadowOffsetYValue,
567
+ this.shadowBlurValue,
568
+ this.shadowSpreadValue,
569
+ this.paddingOverridden,
570
+ this.paddingLeftValue,
571
+ this.paddingTopValue,
572
+ this.paddingRightValue,
573
+ this.paddingBottomValue,
574
+ );
575
+ this.presenter.apply(theme, this.createVisualState());
576
+ this.backgroundOverridden = presenterHostState.backgroundOverridden;
577
+ this.normalBackgroundColorValue = presenterHostState.normalBackgroundColorValue;
578
+ this.cornerRadiusOverridden = presenterHostState.cornerRadiusOverridden;
579
+ this.focusCornerTopLeft = presenterHostState.focusCornerTopLeft;
580
+ this.focusCornerTopRight = presenterHostState.focusCornerTopRight;
581
+ this.focusCornerBottomRight = presenterHostState.focusCornerBottomRight;
582
+ this.focusCornerBottomLeft = presenterHostState.focusCornerBottomLeft;
583
+ this.borderOverridden = presenterHostState.borderOverridden;
584
+ this.borderWidthValue = presenterHostState.borderWidthValue;
585
+ this.borderColorValue = presenterHostState.borderColorValue;
586
+ this.borderStyleValue = presenterHostState.borderStyleValue;
587
+ this.borderDashOnValue = presenterHostState.borderDashOnValue;
588
+ this.borderDashOffValue = presenterHostState.borderDashOffValue;
589
+ this.borderDashedValue = presenterHostState.borderDashedValue;
590
+ this.shadowOverridden = presenterHostState.shadowOverridden;
591
+ this.shadowColorValue = presenterHostState.shadowColorValue;
592
+ this.shadowOffsetXValue = presenterHostState.shadowOffsetXValue;
593
+ this.shadowOffsetYValue = presenterHostState.shadowOffsetYValue;
594
+ this.shadowBlurValue = presenterHostState.shadowBlurValue;
595
+ this.shadowSpreadValue = presenterHostState.shadowSpreadValue;
596
+ this.paddingOverridden = presenterHostState.paddingOverridden;
597
+ this.paddingLeftValue = presenterHostState.paddingLeftValue;
598
+ this.paddingTopValue = presenterHostState.paddingTopValue;
599
+ this.paddingRightValue = presenterHostState.paddingRightValue;
600
+ this.paddingBottomValue = presenterHostState.paddingBottomValue;
601
+ this.applyCurrentCorners();
602
+ this.applyCurrentBorder();
603
+ this.applyCurrentShadow();
604
+ this.applyCurrentPadding();
605
+ this.applyCurrentLabelTypography();
606
+ this.applyCurrentLabelTextColor();
607
+ this.syncFocusChrome();
608
+ }
609
+
610
+ protected _onEffectiveEnabledChanged(isEnabled: bool): void {
611
+ if (!isEnabled) {
612
+ this.opacityBeforeDisabled = this.currentOpacity;
613
+ this.cancelKeyboardPress();
614
+ this.focusedState = false;
615
+ super.opacity(0.38);
616
+ } else {
617
+ super.opacity(this.opacityBeforeDisabled);
618
+ }
619
+ this.cursor(isEnabled ? CursorStyle.Pointer : CursorStyle.Default);
620
+ this.handleThemeSignalChanged();
621
+ }
622
+
623
+ private track(disposable: Disposable): void {
624
+ this.disposables.push(disposable);
625
+ }
626
+
627
+ private cancelKeyboardPress(): void {
628
+ this.keyboardArmedKey = null;
629
+ this.cancelPress();
630
+ }
631
+
632
+ private createPresenter(template: ButtonTemplate | null): ButtonPresenter {
633
+ if (template !== null) {
634
+ return template.create();
635
+ }
636
+ const templateSet = getControlTemplates();
637
+ const appTemplate = templateSet !== null ? templateSet.button : null;
638
+ return (appTemplate === null ? defaultButtonTemplate : appTemplate).create();
639
+ }
640
+
641
+ private replacePresenter(nextPresenter: ButtonPresenter): void {
642
+ nextPresenter.bindHost(this);
643
+ nextPresenter.labelNode.text(this.labelValue);
644
+ const previousPresenter = this.presenter;
645
+ this.presenter = nextPresenter;
646
+ this.labelNode = nextPresenter.labelNode;
647
+ if (previousPresenter === nextPresenter) {
648
+ return;
649
+ }
650
+ this.addChildNode(nextPresenter.contentRoot);
651
+ this.removeChildNode(previousPresenter.contentRoot);
652
+ previousPresenter.contentRoot.dispose();
653
+ }
654
+
655
+ private recreatePresenterTree(): void {
656
+ this.presenter = this.createPresenter(this.templateValue);
657
+ this.presenter.bindHost(this);
658
+ this.labelNode = this.presenter.labelNode;
659
+ this.labelNode.text(this.labelValue);
660
+ this.addChildNode(this.presenter.contentRoot);
661
+ this.presenterNeedsRefresh = false;
662
+ this.syncThemeState(activeTheme.value);
663
+ this.applyBackground();
664
+ }
665
+
666
+ private createVisualState(): ButtonVisualState {
667
+ return new ButtonVisualState(
668
+ this.hovered.value,
669
+ this.pressed.value,
670
+ this.focusedState,
671
+ this.isEnabled,
672
+ );
673
+ }
674
+
675
+ private applyBackground(): void {
676
+ const color = this.computeBackground();
677
+ super.bgColor(color);
678
+ if (this.builtHandle != <u64>HandleValue.Invalid) {
679
+ ui.setBackgroundColor(this.builtHandle, color);
680
+ }
681
+ }
682
+
683
+ private applyCurrentCorners(): void {
684
+ super.corners(
685
+ this.focusCornerTopLeft,
686
+ this.focusCornerTopRight,
687
+ this.focusCornerBottomRight,
688
+ this.focusCornerBottomLeft,
689
+ );
690
+ }
691
+
692
+ private applyCurrentBorder(): void {
693
+ super.border(this.borderWidthValue, this.borderColorValue, this.borderStyleValue);
694
+ if (this.borderDashedValue) {
695
+ super.borderDashed(this.borderDashOnValue, this.borderDashOffValue);
696
+ }
697
+ }
698
+
699
+ private applyCurrentPadding(): void {
700
+ super.padding(
701
+ this.paddingLeftValue,
702
+ this.paddingTopValue,
703
+ this.paddingRightValue,
704
+ this.paddingBottomValue,
705
+ );
706
+ }
707
+
708
+ private applyCurrentShadow(): void {
709
+ super.dropShadow(
710
+ this.shadowColorValue,
711
+ this.shadowOffsetXValue,
712
+ this.shadowOffsetYValue,
713
+ this.shadowBlurValue,
714
+ this.shadowSpreadValue,
715
+ );
716
+ }
717
+
718
+ private applyCurrentLabelTypography(): void {
719
+ if (this.hasFontIdOverride) {
720
+ this.labelNode.font(this.fontIdValue, this.fontSizeValue);
721
+ return;
722
+ }
723
+ this.labelNode
724
+ .fontFamily(this.fontFamilyValue)
725
+ .fontWeight(this.fontWeightValue)
726
+ .fontStyle(this.fontStyleValue)
727
+ .fontSize(this.fontSizeValue);
728
+ }
729
+
730
+ private applyCurrentLabelTextColor(): void {
731
+ this.labelNode.textColor(this.textColorValue);
732
+ }
733
+
734
+ private syncFocusChrome(): void {
735
+ if (this.disposed) {
736
+ return;
737
+ }
738
+ if (this.focusedState && this.isEnabled && keyboardFocusVisible.value) {
739
+ FocusAdornerManager.showStandardCorners(
740
+ this,
741
+ this.focusCornerTopLeft,
742
+ this.focusCornerTopRight,
743
+ this.focusCornerBottomRight,
744
+ this.focusCornerBottomLeft,
745
+ );
746
+ return;
747
+ }
748
+ FocusAdornerManager.hideOwner(this);
749
+ }
750
+ }