@effindomv2/fui-as 0.1.8 → 0.1.10
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/package.json +2 -2
- package/src/controls/Dropdown.ts +73 -4
- package/src/controls/internal/CheckboxIndicatorPresenter.ts +1 -2
- package/src/controls/internal/DropdownChevronPresenter.ts +2 -4
- package/src/controls/internal/DropdownOptionRowPresenter.ts +1 -2
- package/src/core/EventRouter.ts +2 -0
- package/src/core/FocusAdornerManager.ts +2 -16
- package/src/core/Node.ts +32 -0
- package/src/core/ScrollHooks.ts +13 -0
- package/src/core/Theme.ts +5 -2
- package/src/nodes/FlexBox.ts +54 -6
- package/src/nodes/VirtualList.ts +3 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effindomv2/fui-as",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
|
|
6
6
|
"description": "EffinDom v2 AssemblyScript frontend framework SDK and browser harness",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@assemblyscript/loader": "^0.28.17",
|
|
81
|
-
"@effindomv2/runtime": "0.1.
|
|
81
|
+
"@effindomv2/runtime": "0.1.3"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@as-pect/assembly": "8.1.0",
|
package/src/controls/Dropdown.ts
CHANGED
|
@@ -19,7 +19,8 @@ import { Theme, activeTheme } from "../core/Theme";
|
|
|
19
19
|
import { warn } from "../core/Logger";
|
|
20
20
|
import { Node } from "../core/Node";
|
|
21
21
|
import { PersistedInt32Codec, PersistedValueState } from "../core/PersistedState";
|
|
22
|
-
import {
|
|
22
|
+
import { registerScrollHook } from "../core/ScrollHooks";
|
|
23
|
+
import { FlexBox, Portal, ScrollBarVisibility, ScrollBox, ScrollView } from "../nodes";
|
|
23
24
|
import { bind2 } from "../core/bind";
|
|
24
25
|
import { getControlTemplates } from "./ControlTemplateSet";
|
|
25
26
|
import {
|
|
@@ -173,14 +174,13 @@ class DropdownOptionNode extends FlexBox {
|
|
|
173
174
|
|
|
174
175
|
private syncPresenterLayout(): void {
|
|
175
176
|
this.height(this.presenter.metrics.height, Unit.Pixel);
|
|
176
|
-
this.presenter.root
|
|
177
|
-
.width(100.0, Unit.Percent)
|
|
178
|
-
.height(100.0, Unit.Percent);
|
|
177
|
+
this.presenter.root.fillSize();
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
export class Dropdown extends FlexBox implements GlobalKeyHandler {
|
|
183
182
|
private static activeInstance: Dropdown | null = null;
|
|
183
|
+
private static scrollHookRegistered: bool = false;
|
|
184
184
|
|
|
185
185
|
private fieldTemplateValue: DropdownFieldTemplate | null = null;
|
|
186
186
|
private chevronTemplateValue: DropdownChevronTemplate | null = null;
|
|
@@ -214,6 +214,7 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
|
|
|
214
214
|
|
|
215
215
|
constructor() {
|
|
216
216
|
super();
|
|
217
|
+
Dropdown.ensureScrollHook();
|
|
217
218
|
const fieldPresenter = createFieldPresenter(null);
|
|
218
219
|
const chevronPresenter = createChevronPresenter(null);
|
|
219
220
|
const popupRoot = new Portal()
|
|
@@ -396,6 +397,24 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
|
|
|
396
397
|
}
|
|
397
398
|
}
|
|
398
399
|
|
|
400
|
+
static dismissActiveDropdownIfTriggerOutOfViewport(): void {
|
|
401
|
+
const dropdown = Dropdown.activeInstance;
|
|
402
|
+
if (dropdown === null) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (!dropdown.isTriggerVisibleInViewport()) {
|
|
406
|
+
dropdown.close();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private static ensureScrollHook(): void {
|
|
411
|
+
if (Dropdown.scrollHookRegistered) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
registerScrollHook((): void => Dropdown.dismissActiveDropdownIfTriggerOutOfViewport());
|
|
415
|
+
Dropdown.scrollHookRegistered = true;
|
|
416
|
+
}
|
|
417
|
+
|
|
399
418
|
highlightIndex(index: i32): void {
|
|
400
419
|
if (index < 0 || index >= this.itemsValue.length || this.highlightedIndexValue == index) {
|
|
401
420
|
if (index < 0 || index >= this.itemsValue.length) {
|
|
@@ -605,6 +624,56 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
|
|
|
605
624
|
return ui.tryGetBounds(this.builtHandle);
|
|
606
625
|
}
|
|
607
626
|
|
|
627
|
+
private findContainingScrollView(): ScrollView | null {
|
|
628
|
+
let current: Node | null = this.parentNode;
|
|
629
|
+
while (current !== null) {
|
|
630
|
+
if (current instanceof ScrollView) {
|
|
631
|
+
return current as ScrollView;
|
|
632
|
+
}
|
|
633
|
+
current = current.parentNode;
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
private isTriggerVisibleInViewport(): bool {
|
|
639
|
+
const bounds = this.tryGetViewportBounds();
|
|
640
|
+
if (bounds === null) {
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
const x = unchecked(bounds[0]);
|
|
644
|
+
const y = unchecked(bounds[1]);
|
|
645
|
+
const width = unchecked(bounds[2]);
|
|
646
|
+
const height = unchecked(bounds[3]);
|
|
647
|
+
if (width <= 0.0 || height <= 0.0) {
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
const right = x + width;
|
|
651
|
+
const bottom = y + height;
|
|
652
|
+
|
|
653
|
+
const scrollView = this.findContainingScrollView();
|
|
654
|
+
if (scrollView === null) {
|
|
655
|
+
const viewportWidth = ui.getViewportWidth();
|
|
656
|
+
const viewportHeight = ui.getViewportHeight();
|
|
657
|
+
return right > 0.0 && bottom > 0.0 && x < viewportWidth && y < viewportHeight;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const scrollViewBounds = ui.tryGetBounds(scrollView.builtHandle);
|
|
661
|
+
if (scrollViewBounds === null) {
|
|
662
|
+
const viewportWidth = ui.getViewportWidth();
|
|
663
|
+
const viewportHeight = ui.getViewportHeight();
|
|
664
|
+
return right > 0.0 && bottom > 0.0 && x < viewportWidth && y < viewportHeight;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const svX = unchecked(scrollViewBounds[0]);
|
|
668
|
+
const svY = unchecked(scrollViewBounds[1]);
|
|
669
|
+
const svWidth = unchecked(scrollViewBounds[2]);
|
|
670
|
+
const svHeight = unchecked(scrollViewBounds[3]);
|
|
671
|
+
const svRight = svX + svWidth;
|
|
672
|
+
const svBottom = svY + svHeight;
|
|
673
|
+
|
|
674
|
+
return right > svX && bottom > svY && x < svRight && y < svBottom;
|
|
675
|
+
}
|
|
676
|
+
|
|
608
677
|
private positionPanel(triggerX: f32, triggerY: f32, triggerWidth: f32, triggerHeight: f32): void {
|
|
609
678
|
const popupWidth = this.resolvePopupWidth(triggerWidth);
|
|
610
679
|
const panelHeight = this.resolveViewportClampedPanelOuterHeight();
|
|
@@ -44,8 +44,7 @@ class DefaultCheckboxIndicatorPresenter extends CheckboxIndicatorPresenter {
|
|
|
44
44
|
.justifyContent(1);
|
|
45
45
|
super(root, new PressableIndicatorMetrics(20.0, 20.0));
|
|
46
46
|
const markHost = new FlexBox()
|
|
47
|
-
.
|
|
48
|
-
.height(100.0, Unit.Percent)
|
|
47
|
+
.fillSize()
|
|
49
48
|
.alignItems(1)
|
|
50
49
|
.justifyContent(1);
|
|
51
50
|
const markNode = new Svg();
|
|
@@ -34,8 +34,7 @@ class DefaultDropdownChevronPresenter extends DropdownChevronPresenter {
|
|
|
34
34
|
|
|
35
35
|
constructor() {
|
|
36
36
|
const root = new FlexBox()
|
|
37
|
-
.
|
|
38
|
-
.height(100.0, Unit.Percent)
|
|
37
|
+
.fillSize()
|
|
39
38
|
.alignItems(AlignItems.Center)
|
|
40
39
|
.justifyContent(JustifyContent.Center);
|
|
41
40
|
const iconNode = new Svg()
|
|
@@ -48,8 +47,7 @@ class DefaultDropdownChevronPresenter extends DropdownChevronPresenter {
|
|
|
48
47
|
|
|
49
48
|
apply(theme: Theme, state: DropdownChevronVisualState): void {
|
|
50
49
|
this.root
|
|
51
|
-
.
|
|
52
|
-
.height(100.0, Unit.Percent)
|
|
50
|
+
.fillSize()
|
|
53
51
|
.alignItems(AlignItems.Center)
|
|
54
52
|
.justifyContent(JustifyContent.Center);
|
|
55
53
|
this.iconNode
|
|
@@ -51,8 +51,7 @@ class DefaultDropdownOptionRowPresenter extends DropdownOptionRowPresenter {
|
|
|
51
51
|
.wrapping(false) as Text;
|
|
52
52
|
labelNode.overflowFade(true, false);
|
|
53
53
|
const root = new FlexBox()
|
|
54
|
-
.
|
|
55
|
-
.height(100.0, Unit.Percent)
|
|
54
|
+
.fillSize()
|
|
56
55
|
.alignItems(AlignItems.Center)
|
|
57
56
|
.child(labelNode);
|
|
58
57
|
super(root, labelNode, new DropdownOptionRowMetrics(34.0));
|
package/src/core/EventRouter.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { DragDropEffects, DragSession, ExternalDropItemInfo, Node } from "./Node
|
|
|
15
15
|
import { DragDropManager } from "./DragDropManager";
|
|
16
16
|
import { ExternalDragEventType, ExternalDropManager } from "./ExternalDropManager";
|
|
17
17
|
import { isCoarsePointer } from "./Platform";
|
|
18
|
+
import { runScrollHooks } from "./ScrollHooks";
|
|
18
19
|
|
|
19
20
|
export interface GlobalKeyHandler {
|
|
20
21
|
handleGlobalKeyEvent(eventType: KeyEventType, key: string, modifiers: u32): bool;
|
|
@@ -161,6 +162,7 @@ export class EventRouter {
|
|
|
161
162
|
viewportHeight: f32,
|
|
162
163
|
): void {
|
|
163
164
|
ToolTipManager.handleScroll();
|
|
165
|
+
runScrollHooks();
|
|
164
166
|
const node = this.resolveNode(handle);
|
|
165
167
|
if (node === null) {
|
|
166
168
|
return;
|
|
@@ -2,7 +2,7 @@ import * as ui from "../bindings/ui";
|
|
|
2
2
|
import { BorderStyle, Unit } from "./ffi";
|
|
3
3
|
import { Node } from "./Node";
|
|
4
4
|
import { activeTheme } from "./Theme";
|
|
5
|
-
import { Portal,
|
|
5
|
+
import { Portal, FlexBox } from "../nodes";
|
|
6
6
|
|
|
7
7
|
const STANDARD_FOCUS_RING_WIDTH: f32 = 2.0;
|
|
8
8
|
const STANDARD_FOCUS_RING_OUTSET: f32 = 2.0;
|
|
@@ -59,7 +59,7 @@ export class FocusAdornerManager {
|
|
|
59
59
|
.position(0.0, 0.0)
|
|
60
60
|
.width(0.0, Unit.Pixel)
|
|
61
61
|
.height(0.0, Unit.Pixel)
|
|
62
|
-
.clipToBounds(
|
|
62
|
+
.clipToBounds(false) as Portal;
|
|
63
63
|
this.hostRoot = hostRoot;
|
|
64
64
|
this.ringNode = ringNode;
|
|
65
65
|
return hostRoot;
|
|
@@ -239,20 +239,6 @@ export class FocusAdornerManager {
|
|
|
239
239
|
maxX = <f32>Math.min(maxX, ui.getViewportWidth());
|
|
240
240
|
maxY = <f32>Math.min(maxY, ui.getViewportHeight());
|
|
241
241
|
|
|
242
|
-
let ancestor = owner.parentNode;
|
|
243
|
-
while (ancestor !== null) {
|
|
244
|
-
if (ancestor instanceof ScrollView && ancestor.builtHandle != 0) {
|
|
245
|
-
const viewportBounds = ui.tryGetBounds(ancestor.builtHandle);
|
|
246
|
-
if (viewportBounds !== null) {
|
|
247
|
-
minX = <f32>Math.max(minX, unchecked(viewportBounds[0]));
|
|
248
|
-
minY = <f32>Math.max(minY, unchecked(viewportBounds[1]));
|
|
249
|
-
maxX = <f32>Math.min(maxX, unchecked(viewportBounds[0]) + unchecked(viewportBounds[2]));
|
|
250
|
-
maxY = <f32>Math.min(maxY, unchecked(viewportBounds[1]) + unchecked(viewportBounds[3]));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
ancestor = ancestor.parentNode;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
242
|
const width = maxX - minX;
|
|
257
243
|
const height = maxY - minY;
|
|
258
244
|
if (width <= 0.0 || height <= 0.0) {
|
package/src/core/Node.ts
CHANGED
|
@@ -1343,6 +1343,38 @@ export abstract class Node implements DragGestureHost, Disposable {
|
|
|
1343
1343
|
return false;
|
|
1344
1344
|
}
|
|
1345
1345
|
|
|
1346
|
+
_debugNodeId(): string | null {
|
|
1347
|
+
return this.nodeIdValue;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
_debugTreePath(): string {
|
|
1351
|
+
const segments = new Array<i32>();
|
|
1352
|
+
let current: Node | null = this;
|
|
1353
|
+
while (current !== null) {
|
|
1354
|
+
const parent = current.retainedParent;
|
|
1355
|
+
if (parent === null) {
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
let childIndex = -1;
|
|
1359
|
+
for (let index = 0; index < parent.childNodes.length; ++index) {
|
|
1360
|
+
if (unchecked(parent.childNodes[index]) === current) {
|
|
1361
|
+
childIndex = index;
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (childIndex < 0) {
|
|
1366
|
+
break;
|
|
1367
|
+
}
|
|
1368
|
+
segments.push(childIndex);
|
|
1369
|
+
current = parent;
|
|
1370
|
+
}
|
|
1371
|
+
let path = "root";
|
|
1372
|
+
for (let index = segments.length - 1; index >= 0; --index) {
|
|
1373
|
+
path += "/" + unchecked(segments[index]).toString();
|
|
1374
|
+
}
|
|
1375
|
+
return path;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1346
1378
|
protected capturePointer(): void {
|
|
1347
1379
|
if (!this.hasBuiltHandle()) {
|
|
1348
1380
|
return;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type ScrollHook = () => void;
|
|
2
|
+
|
|
3
|
+
const hooks: Array<ScrollHook> = new Array<ScrollHook>();
|
|
4
|
+
|
|
5
|
+
export function registerScrollHook(hook: ScrollHook): void {
|
|
6
|
+
hooks.push(hook);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function runScrollHooks(): void {
|
|
10
|
+
for (let index = 0; index < hooks.length; ++index) {
|
|
11
|
+
unchecked(hooks[index])();
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/core/Theme.ts
CHANGED
|
@@ -38,6 +38,7 @@ export class Colors {
|
|
|
38
38
|
readonly scrollbarThumb: u32,
|
|
39
39
|
readonly dialogBackdrop: u32,
|
|
40
40
|
readonly dialogShadow: u32,
|
|
41
|
+
readonly panelShadow: u32,
|
|
41
42
|
readonly focusRing: u32,
|
|
42
43
|
) {}
|
|
43
44
|
}
|
|
@@ -231,16 +232,17 @@ export function generateTheme(isDark: bool, accentColor: u32 = DEFAULT_ACCENT_CO
|
|
|
231
232
|
: mixColor(accent, surface, 0.40);
|
|
232
233
|
const dialogBackdrop = isDark ? rgba(0x00, 0x00, 0x00, 0x24) : rgba(0x00, 0x00, 0x00, 0x18);
|
|
233
234
|
const dialogShadow = isDark ? rgba(0x00, 0x00, 0x00, 0xd8) : rgba(0x00, 0x00, 0x00, 0x88);
|
|
235
|
+
const panelShadow = withAlpha(dialogShadow, <u32>Math.round(<f32>colorAlpha(dialogShadow) * 0.30));
|
|
234
236
|
const focusRing = accent;
|
|
235
237
|
const contextMenuPanelBackground = isDark ? rgba(0x18, 0x1d, 0x26, 0xd8) : rgba(0xff, 0xff, 0xff, 0xdc);
|
|
236
238
|
const contextMenuPanelBorderColor = isDark ? rgba(0xff, 0xff, 0xff, 0x10) : rgba(0x0f, 0x17, 0x2a, 0x14);
|
|
237
|
-
const contextMenuPanelShadowColor =
|
|
239
|
+
const contextMenuPanelShadowColor = panelShadow;
|
|
238
240
|
const contextMenuItemBackground = rgba(0x00, 0x00, 0x00, 0x00);
|
|
239
241
|
const contextMenuItemHover = isDark ? rgba(0xff, 0xff, 0xff, 0x0c) : rgba(0x0f, 0x17, 0x2a, 0x08);
|
|
240
242
|
const contextMenuSeparatorColor = isDark ? rgba(0xff, 0xff, 0xff, 0x10) : rgba(0x0f, 0x17, 0x2a, 0x12);
|
|
241
243
|
const toolTipPanelBackground = isDark ? rgba(0x11, 0x17, 0x20, 0xf0) : rgba(0xff, 0xff, 0xff, 0xf8);
|
|
242
244
|
const toolTipPanelBorderColor = isDark ? rgba(0xff, 0xff, 0xff, 0x12) : rgba(0x0f, 0x17, 0x2a, 0x12);
|
|
243
|
-
const toolTipPanelShadowColor =
|
|
245
|
+
const toolTipPanelShadowColor = panelShadow;
|
|
244
246
|
|
|
245
247
|
return new Theme(
|
|
246
248
|
new Colors(
|
|
@@ -257,6 +259,7 @@ export function generateTheme(isDark: bool, accentColor: u32 = DEFAULT_ACCENT_CO
|
|
|
257
259
|
scrollbarThumb,
|
|
258
260
|
dialogBackdrop,
|
|
259
261
|
dialogShadow,
|
|
262
|
+
panelShadow,
|
|
260
263
|
focusRing,
|
|
261
264
|
),
|
|
262
265
|
DEFAULT_SPACING,
|
package/src/nodes/FlexBox.ts
CHANGED
|
@@ -623,6 +623,8 @@ export class FlexBox extends Node {
|
|
|
623
623
|
let inFlowChildCount = 0;
|
|
624
624
|
let fullMainAxisPercentChildCount = 0;
|
|
625
625
|
let mainAxisPercentTotal: f32 = 0.0;
|
|
626
|
+
let firstFullMainAxisPercentChild: Node | null = null;
|
|
627
|
+
let firstFullMainAxisPercentChildIndex: i32 = -1;
|
|
626
628
|
|
|
627
629
|
for (let i = 0; i < this.childNodes.length; ++i) {
|
|
628
630
|
const child = unchecked(this.childNodes[i]);
|
|
@@ -639,6 +641,10 @@ export class FlexBox extends Node {
|
|
|
639
641
|
mainAxisPercentTotal += percent;
|
|
640
642
|
if (percent >= MAIN_AXIS_PERCENT_FULL_THRESHOLD) {
|
|
641
643
|
fullMainAxisPercentChildCount += 1;
|
|
644
|
+
if (firstFullMainAxisPercentChild === null) {
|
|
645
|
+
firstFullMainAxisPercentChild = child;
|
|
646
|
+
firstFullMainAxisPercentChildIndex = i;
|
|
647
|
+
}
|
|
642
648
|
}
|
|
643
649
|
}
|
|
644
650
|
|
|
@@ -649,7 +655,11 @@ export class FlexBox extends Node {
|
|
|
649
655
|
if (fullMainAxisPercentChildCount > 0) {
|
|
650
656
|
if ((this.layoutWarningMask & LAYOUT_WARNING_FULL_MAIN_AXIS_PERCENT) == 0) {
|
|
651
657
|
this.layoutWarningMask |= LAYOUT_WARNING_FULL_MAIN_AXIS_PERCENT;
|
|
652
|
-
warn("Layout", this.buildFullMainAxisPercentWarning(
|
|
658
|
+
warn("Layout", this.buildFullMainAxisPercentWarning(
|
|
659
|
+
isRow,
|
|
660
|
+
firstFullMainAxisPercentChild,
|
|
661
|
+
firstFullMainAxisPercentChildIndex,
|
|
662
|
+
));
|
|
653
663
|
}
|
|
654
664
|
return;
|
|
655
665
|
}
|
|
@@ -721,18 +731,56 @@ export class FlexBox extends Node {
|
|
|
721
731
|
this.cancelBackgroundColorTransition();
|
|
722
732
|
}
|
|
723
733
|
|
|
724
|
-
private buildFullMainAxisPercentWarning(isRow: bool): string {
|
|
734
|
+
private buildFullMainAxisPercentWarning(isRow: bool, child: Node | null, childIndex: i32): string {
|
|
735
|
+
let message = "";
|
|
725
736
|
if (isRow) {
|
|
726
|
-
|
|
737
|
+
message = "A row container has an in-flow child using width(100.0, Unit.Percent) alongside siblings. Unit.Percent is literal parent-relative sizing, not flex sharing. Use fillWidth() when the child should take remaining row space.";
|
|
738
|
+
} else {
|
|
739
|
+
message = "A column container has an in-flow child using height(100.0, Unit.Percent) alongside siblings. Unit.Percent is literal parent-relative sizing, not flex sharing. Use fillHeight() when the child should take remaining column space.";
|
|
727
740
|
}
|
|
728
|
-
return
|
|
741
|
+
return message + this.buildWarningContextSuffix(child, childIndex);
|
|
729
742
|
}
|
|
730
743
|
|
|
731
744
|
private buildMainAxisPercentOverflowWarning(isRow: bool): string {
|
|
745
|
+
let message = "";
|
|
732
746
|
if (isRow) {
|
|
733
|
-
|
|
747
|
+
message = "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 fillWidth() for the child that should expand, or reduce the percentages so they fit.";
|
|
748
|
+
} else {
|
|
749
|
+
message = "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 fillHeight() for the child that should expand, or reduce the percentages so they fit.";
|
|
750
|
+
}
|
|
751
|
+
return message + this.buildWarningContextSuffix(null, -1);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private buildWarningContextSuffix(child: Node | null, childIndex: i32): string {
|
|
755
|
+
const containerNodeId = this._debugNodeId();
|
|
756
|
+
const childNodeId = child !== null ? child._debugNodeId() : null;
|
|
757
|
+
const containerPath = this._debugTreePath();
|
|
758
|
+
const childPath = child !== null ? child._debugTreePath() : null;
|
|
759
|
+
const hasContainerNodeId = containerNodeId !== null && containerNodeId.length > 0;
|
|
760
|
+
const hasChildNodeId = childNodeId !== null && childNodeId.length > 0;
|
|
761
|
+
const hasChildPath = childPath !== null;
|
|
762
|
+
const hasChildIndex = childIndex >= 0;
|
|
763
|
+
let suffix = " [containerPath=" + containerPath;
|
|
764
|
+
let needsSeparator = true;
|
|
765
|
+
if (hasChildPath) {
|
|
766
|
+
suffix += ", childPath=" + changetype<string>(childPath);
|
|
767
|
+
}
|
|
768
|
+
if (hasContainerNodeId) {
|
|
769
|
+
suffix += ", containerNodeId=" + changetype<string>(containerNodeId);
|
|
770
|
+
needsSeparator = true;
|
|
771
|
+
}
|
|
772
|
+
if (hasChildNodeId) {
|
|
773
|
+
suffix += ", childNodeId=" + changetype<string>(childNodeId);
|
|
774
|
+
needsSeparator = true;
|
|
775
|
+
}
|
|
776
|
+
if (hasChildIndex) {
|
|
777
|
+
if (needsSeparator) {
|
|
778
|
+
suffix += ", ";
|
|
779
|
+
}
|
|
780
|
+
suffix += "childIndex=" + childIndex.toString();
|
|
734
781
|
}
|
|
735
|
-
|
|
782
|
+
suffix += "]";
|
|
783
|
+
return suffix;
|
|
736
784
|
}
|
|
737
785
|
|
|
738
786
|
protected applyVisualStyle(): void {
|
package/src/nodes/VirtualList.ts
CHANGED
|
@@ -99,9 +99,7 @@ export class VirtualList extends FlexBox {
|
|
|
99
99
|
.child(topSpacerValue);
|
|
100
100
|
|
|
101
101
|
for (let index = 0; index < poolSizeValue; ++index) {
|
|
102
|
-
const container = new FlexBox()
|
|
103
|
-
.width(FULL_SIZE, Unit.Percent)
|
|
104
|
-
.height(FULL_SIZE, Unit.Percent);
|
|
102
|
+
const container = new FlexBox().fillSize();
|
|
105
103
|
const rowArea = new SelectionArea()
|
|
106
104
|
.width(FULL_SIZE, Unit.Percent)
|
|
107
105
|
.height(0.0, Unit.Pixel)
|
|
@@ -117,8 +115,7 @@ export class VirtualList extends FlexBox {
|
|
|
117
115
|
.scrollEnabledY(true)
|
|
118
116
|
.scrollOffset(scrollStateValue.offsetX.value, scrollStateValue.offsetY.value)
|
|
119
117
|
.scrollContentSize(-1.0, <f32>totalItemsValue * itemHeightValue)
|
|
120
|
-
.
|
|
121
|
-
.height(FULL_SIZE, Unit.Percent)
|
|
118
|
+
.fillSize()
|
|
122
119
|
.child(contentValue) as ScrollBox;
|
|
123
120
|
scrollStateValue.contentHeight.value = <f32>totalItemsValue * itemHeightValue;
|
|
124
121
|
|
|
@@ -132,8 +129,7 @@ export class VirtualList extends FlexBox {
|
|
|
132
129
|
|
|
133
130
|
this.flexDirection(FlexDirection.Column)
|
|
134
131
|
.child(this.scrollBoxValue)
|
|
135
|
-
.
|
|
136
|
-
.height(FULL_SIZE, Unit.Percent);
|
|
132
|
+
.fillSize();
|
|
137
133
|
this.attachListeners();
|
|
138
134
|
}
|
|
139
135
|
|