From fc79fd519d868d9fe0aa7e871a17405e279a9b3d Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 15 Jun 2026 13:45:38 +0200 Subject: [PATCH] =?UTF-8?q?refactor(D.2b):=20DatWidgetFactory=20review=20f?= =?UTF-8?q?ixes=20=E2=80=94=20single=20lookup=20+=20malformed-meter=20trac?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix 1: SliceIds now projects the File id during Select rather than calling TryGetValue twice (once in Where, once in the local File() helper). Added a comment noting that OrderBy is stable so X-tie order follows insertion order. Fix 2: BuildMeter emits a [D.2b] Console.WriteLine when the Type-3 container count is not exactly 2, surfacing malformed or non-vitals meter elements during Task 8 conformance testing without disturbing the existing solid-color fallback. Fix 3: Test 5 adds two explicit NotEqual assertions confirming the ShowDetail-only overlay sprite (OverlayFile = 0x06007490) did not leak into FrontRight or FrontTile. 5/5 tests pass, 0 warnings. Co-Authored-By: Claude Sonnet 4.6 --- src/AcDream.App/UI/Layout/DatWidgetFactory.cs | 17 ++++++++++------- .../UI/Layout/DatWidgetFactoryTests.cs | 3 +++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/AcDream.App/UI/Layout/DatWidgetFactory.cs b/src/AcDream.App/UI/Layout/DatWidgetFactory.cs index e8791e44..15ba9a85 100644 --- a/src/AcDream.App/UI/Layout/DatWidgetFactory.cs +++ b/src/AcDream.App/UI/Layout/DatWidgetFactory.cs @@ -115,6 +115,9 @@ public static class DatWidgetFactory .OrderBy(c => c.ReadOrder) .ToList(); + if (containers.Count != 2) + Console.WriteLine($"[D.2b] meter 0x{info.Id:X8}: {containers.Count} Type-3 slice containers (expected 2) — bars may render as solid-color fallback."); + if (containers.Count >= 1) { var (l, t, r) = SliceIds(containers[0]); @@ -150,17 +153,17 @@ public static class DatWidgetFactory { // Only children that have a non-zero DirectState image are slice candidates. // The expand-detail overlay has NO DirectState entry, so it's excluded here. + // Project the File during filtering to avoid a second TryGetValue lookup. + // Stable sort: on an X tie, original Children insertion order (dat key-sort order) wins. var slices = container.Children .Where(c => c.StateMedia.TryGetValue("", out var med) && med.File != 0) - .OrderBy(c => c.X) + .Select(c => (c.X, File: c.StateMedia[""].File)) + .OrderBy(t => t.X) .ToList(); - static uint File(ElementInfo e) - => e.StateMedia.TryGetValue("", out var med) ? med.File : 0u; - - uint left = slices.Count > 0 ? File(slices[0]) : 0u; - uint tile = slices.Count > 1 ? File(slices[1]) : 0u; - uint right = slices.Count > 2 ? File(slices[2]) : 0u; + uint left = slices.Count > 0 ? slices[0].File : 0u; + uint tile = slices.Count > 1 ? slices[1].File : 0u; + uint right = slices.Count > 2 ? slices[2].File : 0u; return (left, tile, right); } diff --git a/tests/AcDream.App.Tests/UI/Layout/DatWidgetFactoryTests.cs b/tests/AcDream.App.Tests/UI/Layout/DatWidgetFactoryTests.cs index 6a1ef9c1..4258d0b6 100644 --- a/tests/AcDream.App.Tests/UI/Layout/DatWidgetFactoryTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/DatWidgetFactoryTests.cs @@ -108,5 +108,8 @@ public class DatWidgetFactoryTests Assert.Equal(FrontL, m.FrontLeft); Assert.Equal(FrontT, m.FrontTile); Assert.Equal(FrontR, m.FrontRight); + // Overlay (ShowDetail-only, no DirectState "") must not leak into any slice slot. + Assert.NotEqual(OverlayFile, m.FrontRight); + Assert.NotEqual(OverlayFile, m.FrontTile); } }