acdream/tests/AcDream.App.Tests/UI/UiTemplateListBoxViewportTests.cs
Erik 057d8cd703 fix(ui): #372 — Options tabs no longer blank; UiTemplateListBox viewport fills its ListBox
ROOT CAUSE (proven, not guessed): the lazily-created row viewport was
constructed 0x0 with Left|Top|Right|Bottom fill-anchors. Its first
ApplyAnchor captured mR = parentW - (0+0) = parentW, so
ComputeAnchoredRect's l&&r branch (w = parentW - mR - mL) kept it 0x0
forever. A 0-tall viewport makes UiScrollablePanel.LayoutScrollableChildren
cull every row, so Character/Chat/Config rendered blank while Gameplay
(no viewport — authored static children sized at Build) worked. This is
the exact Gameplay-vs-rest split the user's first connected gate found.

FIX: seed the viewport to the ListBox's current extent at creation, so the
fill-anchor baseline is mR = parentW - parentW = 0 and the viewport tracks
the parent. The ListBox is a static dat child sized at Build, so its extent
is authored by the time the viewport is lazily created during Bind.
Dormancy preserved — the viewport is still created only on the first row.

Reproduced RED then GREEN by UiTemplateListBoxViewportTests (viewport fills;
rows stay visible after the anchor+cull layout pass) — the layout path the
whole fixture conformance suite structurally never drove, which is why
every OP2-OP6 test was green over a live-only blank-tab failure. Full
Release suite 13,131 / 4 skips / 0 failed.

Still owed (NOT fixed here, no evidence yet): the 'only Exit Game worked'
Gameplay-buttons observation needs a re-gate (two buttons are correctly
INERT; the other four have dialog/chat effects that may have gone
unnoticed); and the 13 ID_ChatOption_TextFilter_* labels fail to resolve
(blank captions, behaviour unaffected). See #372.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 09:58:42 +02:00

79 lines
3.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AcDream.App.UI;
using AcDream.App.UI.Layout;
namespace AcDream.App.Tests.UI;
/// <summary>
/// #372 regression: the Options panel's Character/Chat/Config tabs rendered
/// BLANK at the first connected gate while Gameplay (which has no ListBox)
/// worked. Root cause: <see cref="UiTemplateListBox"/> creates its row
/// viewport at 0×0 with Left|Top|Right|Bottom fill-anchors; the anchor
/// baseline captured from that 0-size rect makes
/// <c>ComputeAnchoredRect</c> keep it 0×0 forever (mR=parentW, so
/// w=parentW-mR-mL=0), and <see cref="UiScrollablePanel.LayoutScrollableChildren"/>
/// then culls every row (top+height ≤ 0 is false). These tests pin that the
/// viewport fills its ListBox and the rows stay visible after a layout pass.
/// </summary>
public sealed class UiTemplateListBoxViewportTests
{
private static UiTemplateListBox MakeListBox(float width, float height)
{
// A ListBox element authoring one template entry, sized like the
// Options-panel Character ListBox (0x100001FA, 276×560 authored).
var info = new ElementInfo { Id = 0x100001FAu, Type = 5u };
var box = new UiTemplateListBox(
info,
_ => (0u, 0, 0),
new[] { new UiTemplateListEntry(0x2100002Bu, 0x10000218u) },
scrollbarElementId: 0x100001FBu)
{
Width = width,
Height = height,
};
return box;
}
[Fact]
public void Viewport_FillsTheListBox_NotCollapsedToZero()
{
var box = MakeListBox(276f, 560f);
// Add a couple of rows via the prebuilt path (no DAT needed).
box.AddPrebuiltRow(new UiText { Width = 260f, Height = 20f });
box.AddPrebuiltRow(new UiText { Width = 260f, Height = 20f });
UiScrollablePanel? viewport = box.ViewportForTest;
Assert.NotNull(viewport);
// Drive the per-frame layout the draw traversal runs: the parent
// ListBox re-anchors its children (UiElement.DrawSelfAndChildren
// line ~500), sizing the fill-anchored viewport.
viewport!.ApplyAnchor(box.Width, box.Height);
// The whole point of #372: the viewport must FILL the ListBox, not
// collapse. Pre-fix this was 0×0.
Assert.Equal(276f, viewport.Width, 3);
Assert.Equal(560f, viewport.Height, 3);
}
[Fact]
public void Rows_StayVisible_AfterLayout_WhenTheyFitTheViewport()
{
var box = MakeListBox(276f, 560f);
var row0 = new UiText { Width = 260f, Height = 20f };
var row1 = new UiText { Width = 260f, Height = 20f };
var row2 = new UiText { Width = 260f, Height = 20f };
box.AddPrebuiltRow(row0);
box.AddPrebuiltRow(row1);
box.AddPrebuiltRow(row2);
UiScrollablePanel viewport = box.ViewportForTest!;
viewport.ApplyAnchor(box.Width, box.Height); // size the viewport
viewport.LayoutScrollableChildren(); // the cull pass (runs each OnDraw)
// All three rows sit at y 0/20/40 inside a 560px viewport → visible.
// Pre-fix the viewport was 0px tall and every row was culled → blank tab.
Assert.True(row0.Visible, "row 0 culled — the #372 blank-tab bug");
Assert.True(row1.Visible, "row 1 culled — the #372 blank-tab bug");
Assert.True(row2.Visible, "row 2 culled — the #372 blank-tab bug");
}
}