@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,789 @@
1
+ import * as ui from "../bindings/ui";
2
+ import {
3
+ animateColorWith,
4
+ animateFloatWith,
5
+ Animation,
6
+ AnimationTiming,
7
+ } from "../core/Animation";
8
+ import { Node } from "../core/Node";
9
+ import { warn } from "../core/Logger";
10
+ import { throwNullArgument } from "../core/Errors";
11
+ import { NodeTransitions } from "../core/Transitions";
12
+ import {
13
+ AlignItems,
14
+ BorderStyle,
15
+ FlexDirection,
16
+ HandleValue,
17
+ JustifyContent,
18
+ NodeType,
19
+ PositionType,
20
+ Unit,
21
+ } from "../core/ffi";
22
+ import { GradientStop } from "./GradientStop";
23
+
24
+ const LAYOUT_WARNING_FULL_MAIN_AXIS_PERCENT: u8 = 1 << 0;
25
+ const LAYOUT_WARNING_PERCENT_OVERFLOW: u8 = 1 << 1;
26
+ const MAIN_AXIS_PERCENT_FULL_THRESHOLD: f32 = 99.99;
27
+ const MAIN_AXIS_PERCENT_OVERFLOW_THRESHOLD: f32 = 100.01;
28
+
29
+ function applyAnimatedBackgroundColor(owner: FlexBox, color: u32): void {
30
+ owner._applyAnimatedBackgroundColor(color);
31
+ }
32
+
33
+ function applyAnimatedOpacity(owner: FlexBox, value: f32): void {
34
+ owner._applyAnimatedOpacity(value);
35
+ }
36
+
37
+ export class FlexBoxProps {
38
+ widthValue: f32 = 0.0;
39
+ widthUnit: Unit = Unit.Pixel;
40
+ hasWidth: bool = false;
41
+ heightValue: f32 = 0.0;
42
+ heightUnit: Unit = Unit.Pixel;
43
+ hasHeight: bool = false;
44
+ flexBasisValue: f32 = 0.0;
45
+ hasFlexBasis: bool = false;
46
+ backgroundColor: u32 = 0;
47
+ hasBackgroundColor: bool = false;
48
+ flexDirectionValue: FlexDirection = FlexDirection.Column;
49
+ hasFlexDirection: bool = false;
50
+ flexGrowValue: f32 = 0.0;
51
+ hasFlexGrow: bool = false;
52
+ justifyContentValue: JustifyContent = JustifyContent.Start;
53
+ hasJustifyContent: bool = false;
54
+ alignItemsValue: AlignItems = AlignItems.Start;
55
+ hasAlignItems: bool = false;
56
+ paddingTop: f32 = 0.0;
57
+ paddingRight: f32 = 0.0;
58
+ paddingBottom: f32 = 0.0;
59
+ paddingLeft: f32 = 0.0;
60
+ hasPadding: bool = false;
61
+ marginTop: f32 = 0.0;
62
+ marginRight: f32 = 0.0;
63
+ marginBottom: f32 = 0.0;
64
+ marginLeft: f32 = 0.0;
65
+ hasMargin: bool = false;
66
+ clipEnabled: bool = true;
67
+ hasClip: bool = true;
68
+ readonly childNodes: Array<Node> = new Array<Node>();
69
+ }
70
+
71
+ export class FlexBox extends Node {
72
+ private widthValue: f32 = 0.0;
73
+ private widthUnit: Unit = Unit.Pixel;
74
+ private hasWidth: bool = false;
75
+ private heightValue: f32 = 0.0;
76
+ private heightUnit: Unit = Unit.Pixel;
77
+ private hasHeight: bool = false;
78
+ private flexBasisValue: f32 = 0.0;
79
+ private hasFlexBasis: bool = false;
80
+ private backgroundColor: u32 = 0;
81
+ private hasBackgroundColor: bool = false;
82
+ private cornerTopLeft: f32 = 0.0;
83
+ private cornerTopRight: f32 = 0.0;
84
+ private cornerBottomRight: f32 = 0.0;
85
+ private cornerBottomLeft: f32 = 0.0;
86
+ private hasBoxStyle: bool = false;
87
+ private borderWidth: f32 = 0.0;
88
+ private borderColor: u32 = 0;
89
+ private borderStyle: BorderStyle = BorderStyle.Solid;
90
+ private borderDashOn: f32 = 0.0;
91
+ private borderDashOff: f32 = 0.0;
92
+ private flexDirectionValue: FlexDirection = FlexDirection.Column;
93
+ private hasFlexDirection: bool = false;
94
+ private flexGrowValue: f32 = 0.0;
95
+ private hasFlexGrow: bool = false;
96
+ private justifyContentValue: JustifyContent = JustifyContent.Start;
97
+ private hasJustifyContent: bool = false;
98
+ private alignItemsValue: AlignItems = AlignItems.Start;
99
+ private hasAlignItems: bool = false;
100
+ private paddingTop: f32 = 0.0;
101
+ private paddingRight: f32 = 0.0;
102
+ private paddingBottom: f32 = 0.0;
103
+ private paddingLeft: f32 = 0.0;
104
+ private hasPadding: bool = false;
105
+ private marginTop: f32 = 0.0;
106
+ private marginRight: f32 = 0.0;
107
+ private marginBottom: f32 = 0.0;
108
+ private marginLeft: f32 = 0.0;
109
+ private hasMargin: bool = false;
110
+ private positionTypeValue: PositionType = PositionType.Relative;
111
+ private hasPositionType: bool = false;
112
+ private positionTop: f32 = NaN;
113
+ private positionRight: f32 = NaN;
114
+ private positionBottom: f32 = NaN;
115
+ private positionLeft: f32 = NaN;
116
+ private hasPosition: bool = false;
117
+ private clipEnabled: bool = true;
118
+ private hasClip: bool = true;
119
+ private opacityValue: f32 = 1.0;
120
+ private blurSigmaValue: f32 = 0.0;
121
+ private dropShadowColorValue: u32 = 0;
122
+ private dropShadowOffsetXValue: f32 = 0.0;
123
+ private dropShadowOffsetYValue: f32 = 0.0;
124
+ private dropShadowBlurSigmaValue: f32 = 0.0;
125
+ private dropShadowSpreadValue: f32 = 0.0;
126
+ private backgroundBlurSigmaValue: f32 = 0.0;
127
+ private blendModeValue: u32 = 0;
128
+ private hasLayerEffect: bool = false;
129
+ private hasDropShadow: bool = false;
130
+ private hasBackgroundBlur: bool = false;
131
+ private gradientStartX: f32 = 0.0;
132
+ private gradientStartY: f32 = 0.0;
133
+ private gradientEndX: f32 = 0.0;
134
+ private gradientEndY: f32 = 0.0;
135
+ private gradientOffsets: Float32Array | null = null;
136
+ private gradientColors: Uint32Array | null = null;
137
+ private hasGradient: bool = false;
138
+ private layoutWarningMask: u8 = 0;
139
+ private transitionsValue: NodeTransitions | null = null;
140
+ private opacityTransitionAnimation: Animation | null = null;
141
+ private backgroundColorTransitionAnimation: Animation | null = null;
142
+
143
+ static from(props: FlexBoxProps): FlexBox {
144
+ const box = new FlexBox();
145
+ return box.applyProps(props);
146
+ }
147
+
148
+ width(value: f32, unit: Unit = Unit.Pixel): this {
149
+ this.widthValue = value;
150
+ this.widthUnit = unit;
151
+ this.hasWidth = true;
152
+ if (this.hasBuiltHandle()) {
153
+ ui.setWidth(this.handle, value, <u32>unit);
154
+ this.notifyRetainedLayoutMutation();
155
+ }
156
+ return this;
157
+ }
158
+
159
+ height(value: f32, unit: Unit = Unit.Pixel): this {
160
+ this.heightValue = value;
161
+ this.heightUnit = unit;
162
+ this.hasHeight = true;
163
+ if (this.hasBuiltHandle()) {
164
+ ui.setHeight(this.handle, value, <u32>unit);
165
+ this.notifyRetainedLayoutMutation();
166
+ }
167
+ return this;
168
+ }
169
+
170
+ fillWidth(): this {
171
+ this.width(100.0, Unit.Percent);
172
+ return this;
173
+ }
174
+
175
+ fillHeight(): this {
176
+ this.height(100.0, Unit.Percent);
177
+ return this;
178
+ }
179
+
180
+ fillSize(): this {
181
+ this.fillWidth();
182
+ this.fillHeight();
183
+ return this;
184
+ }
185
+
186
+ flexBasis(value: f32): this {
187
+ this.flexBasisValue = value;
188
+ this.hasFlexBasis = true;
189
+ if (this.hasBuiltHandle()) {
190
+ ui.setFlexBasis(this.handle, value);
191
+ this.notifyRetainedLayoutMutation();
192
+ }
193
+ return this;
194
+ }
195
+
196
+ bgColor(color: u32): this {
197
+ this.cancelBackgroundColorTransition();
198
+ if (this.shouldAnimateBackgroundColor(color)) {
199
+ const transitions = changetype<NodeTransitions>(this.transitionsValue);
200
+ const timing = changetype<AnimationTiming>(transitions.backgroundColorTiming);
201
+ this.backgroundColorTransitionAnimation = animateColorWith(
202
+ this,
203
+ this.backgroundColor,
204
+ color,
205
+ timing,
206
+ applyAnimatedBackgroundColor,
207
+ );
208
+ return this;
209
+ }
210
+ this._applyAnimatedBackgroundColor(color);
211
+ return this;
212
+ }
213
+
214
+ cornerRadius(radius: f32): this {
215
+ this.cornerTopLeft = radius;
216
+ this.cornerTopRight = radius;
217
+ this.cornerBottomRight = radius;
218
+ this.cornerBottomLeft = radius;
219
+ this.hasBoxStyle = true;
220
+ if (this.hasBuiltHandle()) {
221
+ this.applyVisualStyle();
222
+ this.notifyRetainedMutation();
223
+ }
224
+ return this;
225
+ }
226
+
227
+ corners(tl: f32, tr: f32, br: f32, bl: f32): this {
228
+ this.cornerTopLeft = tl;
229
+ this.cornerTopRight = tr;
230
+ this.cornerBottomRight = br;
231
+ this.cornerBottomLeft = bl;
232
+ this.hasBoxStyle = true;
233
+ if (this.hasBuiltHandle()) {
234
+ this.applyVisualStyle();
235
+ this.notifyRetainedMutation();
236
+ }
237
+ return this;
238
+ }
239
+
240
+ border(width: f32, color: u32, style: BorderStyle = BorderStyle.Solid): this {
241
+ this.borderWidth = width;
242
+ this.borderColor = color;
243
+ this.borderStyle = style;
244
+ this.borderDashOn = 0.0;
245
+ this.borderDashOff = 0.0;
246
+ this.hasBoxStyle = true;
247
+ if (this.hasBuiltHandle()) {
248
+ this.applyVisualStyle();
249
+ this.notifyRetainedMutation();
250
+ }
251
+ return this;
252
+ }
253
+
254
+ borderDashed(on: f32, off: f32): this {
255
+ this.borderStyle = BorderStyle.Dashed;
256
+ this.borderDashOn = on;
257
+ this.borderDashOff = off;
258
+ this.hasBoxStyle = true;
259
+ if (this.hasBuiltHandle()) {
260
+ this.applyVisualStyle();
261
+ this.notifyRetainedMutation();
262
+ }
263
+ return this;
264
+ }
265
+
266
+ flexDirection(direction: FlexDirection): this {
267
+ this.flexDirectionValue = direction;
268
+ this.hasFlexDirection = true;
269
+ if (this.hasBuiltHandle()) {
270
+ ui.setFlexDirection(this.handle, <u32>direction);
271
+ this.notifyRetainedLayoutMutation();
272
+ this.onRetainedChildLayoutChanged();
273
+ }
274
+ return this;
275
+ }
276
+
277
+ flexGrow(grow: f32): this {
278
+ this.flexGrowValue = grow;
279
+ this.hasFlexGrow = true;
280
+ if (this.hasBuiltHandle()) {
281
+ ui.setFlexGrow(this.handle, grow);
282
+ this.notifyRetainedLayoutMutation();
283
+ }
284
+ return this;
285
+ }
286
+
287
+ grow(grow: f32 = 1.0): this {
288
+ this.flexGrow(grow);
289
+ return this;
290
+ }
291
+
292
+ justifyContent(justify: JustifyContent): this {
293
+ this.justifyContentValue = justify;
294
+ this.hasJustifyContent = true;
295
+ if (this.hasBuiltHandle()) {
296
+ ui.setJustifyContent(this.handle, <u32>justify);
297
+ this.notifyRetainedMutation();
298
+ }
299
+ return this;
300
+ }
301
+
302
+ alignItems(align: AlignItems): this {
303
+ this.alignItemsValue = align;
304
+ this.hasAlignItems = true;
305
+ if (this.hasBuiltHandle()) {
306
+ ui.setAlignItems(this.handle, <u32>align);
307
+ this.notifyRetainedMutation();
308
+ }
309
+ return this;
310
+ }
311
+
312
+ padding(left: f32, top: f32, right: f32, bottom: f32): this {
313
+ this.paddingLeft = left;
314
+ this.paddingTop = top;
315
+ this.paddingRight = right;
316
+ this.paddingBottom = bottom;
317
+ this.hasPadding = true;
318
+ if (this.hasBuiltHandle()) {
319
+ ui.setPadding(this.handle, left, top, right, bottom);
320
+ this.notifyRetainedMutation();
321
+ }
322
+ return this;
323
+ }
324
+
325
+ margin(left: f32, top: f32 = left, right: f32 = left, bottom: f32 = top): this {
326
+ this.marginLeft = left;
327
+ this.marginTop = top;
328
+ this.marginRight = right;
329
+ this.marginBottom = bottom;
330
+ this.hasMargin = true;
331
+ if (this.hasBuiltHandle()) {
332
+ ui.setMargin(this.handle, left, top, right, bottom);
333
+ this.notifyRetainedLayoutMutation();
334
+ }
335
+ return this;
336
+ }
337
+
338
+ positionType(positionType: PositionType): this {
339
+ this.positionTypeValue = positionType;
340
+ this.hasPositionType = true;
341
+ if (this.hasBuiltHandle()) {
342
+ ui.setPositionType(this.handle, <u32>positionType);
343
+ this.notifyRetainedLayoutMutation();
344
+ }
345
+ return this;
346
+ }
347
+
348
+ positionAbsolute(): this {
349
+ this.positionType(PositionType.Absolute);
350
+ return this;
351
+ }
352
+
353
+ position(left: f32, top: f32): this {
354
+ this.positionLeft = left;
355
+ this.positionTop = top;
356
+ this.positionRight = NaN;
357
+ this.positionBottom = NaN;
358
+ this.hasPosition = true;
359
+ if (this.hasBuiltHandle()) {
360
+ ui.setPosition(this.handle, left, top, NaN, NaN);
361
+ this.notifyRetainedMutation();
362
+ }
363
+ return this;
364
+ }
365
+
366
+ clipToBounds(flag: bool): this {
367
+ this.clipEnabled = flag;
368
+ this.hasClip = true;
369
+ if (this.hasBuiltHandle()) {
370
+ ui.setClipToBounds(this.handle, flag);
371
+ this.notifyRetainedMutation();
372
+ }
373
+ return this;
374
+ }
375
+
376
+ opacity(value: f32): this {
377
+ this.cancelOpacityTransition();
378
+ if (this.shouldAnimateOpacity(value)) {
379
+ const transitions = changetype<NodeTransitions>(this.transitionsValue);
380
+ const timing = changetype<AnimationTiming>(transitions.opacityTiming);
381
+ this.opacityTransitionAnimation = animateFloatWith(
382
+ this,
383
+ this.opacityValue,
384
+ value,
385
+ timing,
386
+ applyAnimatedOpacity,
387
+ );
388
+ return this;
389
+ }
390
+ this._applyAnimatedOpacity(value);
391
+ return this;
392
+ }
393
+
394
+ protected get currentOpacity(): f32 {
395
+ return this.opacityValue;
396
+ }
397
+
398
+ blur(sigma: f32): this {
399
+ this.blurSigmaValue = sigma;
400
+ this.hasLayerEffect = true;
401
+ if (this.hasBuiltHandle()) {
402
+ this.applyVisualStyle();
403
+ this.notifyRetainedMutation();
404
+ }
405
+ return this;
406
+ }
407
+
408
+ dropShadow(color: u32, offsetX: f32, offsetY: f32, blurSigma: f32, spread: f32 = 0.0): this {
409
+ this.dropShadowColorValue = color;
410
+ this.dropShadowOffsetXValue = offsetX;
411
+ this.dropShadowOffsetYValue = offsetY;
412
+ this.dropShadowBlurSigmaValue = blurSigma;
413
+ this.dropShadowSpreadValue = spread;
414
+ this.hasDropShadow = true;
415
+ if (this.hasBuiltHandle()) {
416
+ this.applyVisualStyle();
417
+ this.notifyRetainedMutation();
418
+ }
419
+ return this;
420
+ }
421
+
422
+ backgroundBlur(sigma: f32): this {
423
+ this.backgroundBlurSigmaValue = sigma;
424
+ this.hasBackgroundBlur = true;
425
+ if (this.hasBuiltHandle()) {
426
+ this.applyVisualStyle();
427
+ this.notifyRetainedMutation();
428
+ }
429
+ return this;
430
+ }
431
+
432
+ linearGradient(startX: f32, startY: f32, endX: f32, endY: f32, stops: Array<GradientStop>): this {
433
+ if (stops.length == 0) {
434
+ warn("Layout", "FlexBox.linearGradient() received an empty stop list.");
435
+ return this;
436
+ }
437
+ const offsets = new Float32Array(stops.length);
438
+ const colors = new Uint32Array(stops.length);
439
+ for (let i = 0; i < stops.length; ++i) {
440
+ const stop = unchecked(stops[i]);
441
+ unchecked(offsets[i] = stop.offset);
442
+ unchecked(colors[i] = stop.color);
443
+ }
444
+ this.gradientStartX = startX;
445
+ this.gradientStartY = startY;
446
+ this.gradientEndX = endX;
447
+ this.gradientEndY = endY;
448
+ this.gradientOffsets = offsets;
449
+ this.gradientColors = colors;
450
+ this.hasGradient = true;
451
+ if (this.hasBuiltHandle()) {
452
+ this.applyVisualStyle();
453
+ this.notifyRetainedMutation();
454
+ }
455
+ return this;
456
+ }
457
+
458
+ onClick(cb: () => void): this {
459
+ super.onClick(cb);
460
+ return this;
461
+ }
462
+
463
+ onPointerEnter(cb: () => void): this {
464
+ super.onPointerEnter(cb);
465
+ return this;
466
+ }
467
+
468
+ onPointerLeave(cb: () => void): this {
469
+ super.onPointerLeave(cb);
470
+ return this;
471
+ }
472
+
473
+ transitions(transitions: NodeTransitions | null): this {
474
+ this.transitionsValue = transitions;
475
+ return this;
476
+ }
477
+
478
+ child(node: Node): this {
479
+ if (node == null) {
480
+ throwNullArgument("FlexBox.child", "node");
481
+ }
482
+ this.addChildNode(node);
483
+ return this;
484
+ }
485
+
486
+ children(nodes: Array<Node>): this {
487
+ this.replaceChildren(nodes);
488
+ return this;
489
+ }
490
+
491
+ build(): u64 {
492
+ this.buildStyledNode(NodeType.FlexBox);
493
+ return this.handle;
494
+ }
495
+
496
+ protected buildStyledNode(type: NodeType, includeChildren: bool = true): u64 {
497
+ if (this.hasBuiltHandle()) {
498
+ return this.handle;
499
+ }
500
+
501
+ this.handle = ui.createNode(<u32>type);
502
+ this.applyNodeMetadata();
503
+ this.finishBuild();
504
+ this.applyLayoutStyle();
505
+ this.applyVisualStyle();
506
+ this.emitDeveloperLayoutWarnings();
507
+ if (includeChildren) {
508
+ this.buildChildren();
509
+ }
510
+ return this.handle;
511
+ }
512
+
513
+ dispose(): void {
514
+ this.cancelActiveTransitions();
515
+ this.disposeTree();
516
+ }
517
+
518
+ _debugMainAxisPercentValue(isHorizontal: bool): f32 {
519
+ if (isHorizontal) {
520
+ return this.hasWidth && this.widthUnit == Unit.Percent ? this.widthValue : -1.0;
521
+ }
522
+ return this.hasHeight && this.heightUnit == Unit.Percent ? this.heightValue : -1.0;
523
+ }
524
+
525
+ _debugIsAbsolutelyPositioned(): bool {
526
+ return this.hasPositionType && this.positionTypeValue == PositionType.Absolute;
527
+ }
528
+
529
+ protected applyLayoutStyle(): void {
530
+ if (this.hasWidth) {
531
+ ui.setWidth(this.handle, this.widthValue, <u32>this.widthUnit);
532
+ }
533
+ if (this.hasHeight) {
534
+ ui.setHeight(this.handle, this.heightValue, <u32>this.heightUnit);
535
+ }
536
+ if (this.hasFlexDirection) {
537
+ ui.setFlexDirection(this.handle, <u32>this.flexDirectionValue);
538
+ }
539
+ if (this.hasFlexGrow) {
540
+ ui.setFlexGrow(this.handle, this.flexGrowValue);
541
+ }
542
+ if (this.hasFlexBasis) {
543
+ ui.setFlexBasis(this.handle, this.flexBasisValue);
544
+ }
545
+ if (this.hasJustifyContent) {
546
+ ui.setJustifyContent(this.handle, <u32>this.justifyContentValue);
547
+ }
548
+ if (this.hasAlignItems) {
549
+ ui.setAlignItems(this.handle, <u32>this.alignItemsValue);
550
+ }
551
+ if (this.hasMargin) {
552
+ ui.setMargin(this.handle, this.marginLeft, this.marginTop, this.marginRight, this.marginBottom);
553
+ }
554
+ if (this.hasPadding) {
555
+ ui.setPadding(this.handle, this.paddingLeft, this.paddingTop, this.paddingRight, this.paddingBottom);
556
+ }
557
+ if (this.hasPositionType) {
558
+ ui.setPositionType(this.handle, <u32>this.positionTypeValue);
559
+ }
560
+ if (this.hasPosition) {
561
+ ui.setPosition(this.handle, this.positionLeft, this.positionTop, this.positionRight, this.positionBottom);
562
+ }
563
+ if (this.hasClip) {
564
+ ui.setClipToBounds(this.handle, this.clipEnabled);
565
+ }
566
+ }
567
+
568
+ protected applyProps(props: FlexBoxProps): this {
569
+ if (props.hasWidth) {
570
+ this.width(props.widthValue, props.widthUnit);
571
+ }
572
+ if (props.hasHeight) {
573
+ this.height(props.heightValue, props.heightUnit);
574
+ }
575
+ if (props.hasFlexBasis) {
576
+ this.flexBasis(props.flexBasisValue);
577
+ }
578
+ if (props.hasBackgroundColor) {
579
+ this.bgColor(props.backgroundColor);
580
+ }
581
+ if (props.hasFlexDirection) {
582
+ this.flexDirection(props.flexDirectionValue);
583
+ }
584
+ if (props.hasFlexGrow) {
585
+ this.flexGrow(props.flexGrowValue);
586
+ }
587
+ if (props.hasJustifyContent) {
588
+ this.justifyContent(props.justifyContentValue);
589
+ }
590
+ if (props.hasAlignItems) {
591
+ this.alignItems(props.alignItemsValue);
592
+ }
593
+ if (props.hasPadding) {
594
+ this.padding(props.paddingLeft, props.paddingTop, props.paddingRight, props.paddingBottom);
595
+ }
596
+ if (props.hasMargin) {
597
+ this.margin(props.marginLeft, props.marginTop, props.marginRight, props.marginBottom);
598
+ }
599
+ if (props.hasClip) {
600
+ this.clipToBounds(props.clipEnabled);
601
+ }
602
+ if (props.childNodes.length > 0) {
603
+ this.children(props.childNodes);
604
+ }
605
+ return this;
606
+ }
607
+
608
+ protected usesFlexChildLayoutDiagnostics(): bool {
609
+ return true;
610
+ }
611
+
612
+ protected onRetainedChildLayoutChanged(): void {
613
+ this.emitDeveloperLayoutWarnings();
614
+ }
615
+
616
+ private emitDeveloperLayoutWarnings(): void {
617
+ if (!this.usesFlexChildLayoutDiagnostics()) {
618
+ return;
619
+ }
620
+
621
+ const isRow = this.flexDirectionValue == FlexDirection.Row;
622
+ let inFlowChildCount = 0;
623
+ let fullMainAxisPercentChildCount = 0;
624
+ let mainAxisPercentTotal: f32 = 0.0;
625
+
626
+ for (let i = 0; i < this.childNodes.length; ++i) {
627
+ const child = unchecked(this.childNodes[i]);
628
+ if (child._debugIsAbsolutelyPositioned()) {
629
+ continue;
630
+ }
631
+
632
+ inFlowChildCount += 1;
633
+ const percent = child._debugMainAxisPercentValue(isRow);
634
+ if (percent < 0.0) {
635
+ continue;
636
+ }
637
+
638
+ mainAxisPercentTotal += percent;
639
+ if (percent >= MAIN_AXIS_PERCENT_FULL_THRESHOLD) {
640
+ fullMainAxisPercentChildCount += 1;
641
+ }
642
+ }
643
+
644
+ if (inFlowChildCount < 2) {
645
+ return;
646
+ }
647
+
648
+ if (fullMainAxisPercentChildCount > 0) {
649
+ if ((this.layoutWarningMask & LAYOUT_WARNING_FULL_MAIN_AXIS_PERCENT) == 0) {
650
+ this.layoutWarningMask |= LAYOUT_WARNING_FULL_MAIN_AXIS_PERCENT;
651
+ warn("Layout", this.buildFullMainAxisPercentWarning(isRow));
652
+ }
653
+ return;
654
+ }
655
+
656
+ if (mainAxisPercentTotal > MAIN_AXIS_PERCENT_OVERFLOW_THRESHOLD
657
+ && (this.layoutWarningMask & LAYOUT_WARNING_PERCENT_OVERFLOW) == 0) {
658
+ this.layoutWarningMask |= LAYOUT_WARNING_PERCENT_OVERFLOW;
659
+ warn("Layout", this.buildMainAxisPercentOverflowWarning(isRow));
660
+ }
661
+ }
662
+
663
+ _applyAnimatedBackgroundColor(color: u32): void {
664
+ this.backgroundColor = color;
665
+ this.hasBackgroundColor = true;
666
+ this.hasBoxStyle = true;
667
+ if (this.hasBuiltHandle()) {
668
+ this.applyVisualStyle();
669
+ this.notifyRetainedMutation();
670
+ }
671
+ }
672
+
673
+ _applyAnimatedOpacity(value: f32): void {
674
+ this.opacityValue = value;
675
+ this.hasLayerEffect = true;
676
+ if (this.hasBuiltHandle()) {
677
+ this.applyVisualStyle();
678
+ this.notifyRetainedMutation();
679
+ }
680
+ }
681
+
682
+ private shouldAnimateOpacity(nextValue: f32): bool {
683
+ const transitions = this.transitionsValue;
684
+ if (transitions === null || transitions.opacityTiming === null) {
685
+ return false;
686
+ }
687
+ if (!this.hasBuiltHandle()) {
688
+ return false;
689
+ }
690
+ return this.opacityValue != nextValue;
691
+ }
692
+
693
+ private shouldAnimateBackgroundColor(nextColor: u32): bool {
694
+ const transitions = this.transitionsValue;
695
+ if (transitions === null || transitions.backgroundColorTiming === null) {
696
+ return false;
697
+ }
698
+ if (!this.hasBuiltHandle()) {
699
+ return false;
700
+ }
701
+ return this.backgroundColor != nextColor;
702
+ }
703
+
704
+ private cancelOpacityTransition(): void {
705
+ const animation = this.opacityTransitionAnimation;
706
+ if (animation !== null) {
707
+ animation.cancel();
708
+ }
709
+ }
710
+
711
+ private cancelBackgroundColorTransition(): void {
712
+ const animation = this.backgroundColorTransitionAnimation;
713
+ if (animation !== null) {
714
+ animation.cancel();
715
+ }
716
+ }
717
+
718
+ private cancelActiveTransitions(): void {
719
+ this.cancelOpacityTransition();
720
+ this.cancelBackgroundColorTransition();
721
+ }
722
+
723
+ private buildFullMainAxisPercentWarning(isRow: bool): string {
724
+ if (isRow) {
725
+ return "A row container has an in-flow child using width(100.0, Unit.Percent) or fillWidth() alongside siblings. Unit.Percent is literal parent-relative sizing, not flex sharing. Use grow() when siblings should share the row.";
726
+ }
727
+ return "A column container has an in-flow child using height(100.0, Unit.Percent) or fillHeight() alongside siblings. Unit.Percent is literal parent-relative sizing, not flex sharing. Use grow() when siblings should share the column.";
728
+ }
729
+
730
+ private buildMainAxisPercentOverflowWarning(isRow: bool): string {
731
+ if (isRow) {
732
+ return "A row container has in-flow children whose explicit width percentages exceed 100% in total. Unit.Percent is literal parent-relative sizing, not flex sharing. Use grow() when siblings should share the row, or reduce the percentages so they fit.";
733
+ }
734
+ return "A column container has in-flow children whose explicit height percentages exceed 100% in total. Unit.Percent is literal parent-relative sizing, not flex sharing. Use grow() when siblings should share the column, or reduce the percentages so they fit.";
735
+ }
736
+
737
+ protected applyVisualStyle(): void {
738
+ if (this.hasBoxStyle) {
739
+ ui.setBoxStyle(
740
+ this.handle,
741
+ this.backgroundColor,
742
+ this.cornerTopLeft,
743
+ this.cornerTopRight,
744
+ this.cornerBottomRight,
745
+ this.cornerBottomLeft,
746
+ this.borderWidth,
747
+ this.borderColor,
748
+ <u32>this.borderStyle,
749
+ this.borderDashOn,
750
+ this.borderDashOff,
751
+ );
752
+ } else if (this.hasBackgroundColor) {
753
+ ui.setBackgroundColor(this.handle, this.backgroundColor);
754
+ }
755
+
756
+ if (this.hasLayerEffect) {
757
+ ui.setLayerEffect(this.handle, this.opacityValue, this.blurSigmaValue, this.blendModeValue);
758
+ }
759
+
760
+ if (this.hasDropShadow) {
761
+ ui.setDropShadow(
762
+ this.handle,
763
+ this.dropShadowColorValue,
764
+ this.dropShadowOffsetXValue,
765
+ this.dropShadowOffsetYValue,
766
+ this.dropShadowBlurSigmaValue,
767
+ this.dropShadowSpreadValue,
768
+ );
769
+ }
770
+
771
+ if (this.hasBackgroundBlur) {
772
+ ui.setBackgroundBlur(this.handle, this.backgroundBlurSigmaValue);
773
+ }
774
+
775
+ const gradientOffsets = this.gradientOffsets;
776
+ const gradientColors = this.gradientColors;
777
+ if (this.hasGradient && gradientOffsets !== null && gradientColors !== null) {
778
+ ui.setLinearGradient(
779
+ this.handle,
780
+ this.gradientStartX,
781
+ this.gradientStartY,
782
+ this.gradientEndX,
783
+ this.gradientEndY,
784
+ gradientOffsets,
785
+ gradientColors,
786
+ );
787
+ }
788
+ }
789
+ }