diff --git a/src/AcDream.App/UI/Layout/CharacterStatController.cs b/src/AcDream.App/UI/Layout/CharacterStatController.cs index fa79cd85..fccf8508 100644 --- a/src/AcDream.App/UI/Layout/CharacterStatController.cs +++ b/src/AcDream.App/UI/Layout/CharacterStatController.cs @@ -76,6 +76,32 @@ public static class CharacterStatController rows.LinesProvider = () => AttributeRows(data()); list.AddChild(rows); } + + // ── Active-page selection (fixes the "dark overlay") ────────────────── + // 0x2100002E is a tabbed window: all three tab pages (Attributes / Skills / Titles) mount + // their content as separate root children that OVERLAP in the same content rect. Retail + // shows only the active page; acdream draws them all, so the inactive pages' backdrops paint + // over the active content (the dim "almost opaque" cover). The shared gmStatManagement ids + // are duplicated across pages and acdream's _byId keeps the LAST-mounted copy, so every widget + // we just bound lives in exactly ONE page. Keep that page visible; hide the rest. + if (layout.FindElement(NameId) is { } anchor && layout.Root is { } root) + { + foreach (var page in root.Children) + { + if (page.Children.Count == 0) continue; // tab-bar buttons are leaves — leave them + page.Visible = ContainsWidget(page, anchor); // only the bound page stays visible + } + } + } + + /// Depth-first reference search: is somewhere under + /// ? Used to find which overlapping tab page owns the bound widgets. + private static bool ContainsWidget(UiElement node, UiElement target) + { + if (ReferenceEquals(node, target)) return true; + foreach (var c in node.Children) + if (ContainsWidget(c, target)) return true; + return false; } private const float LineHeight = 18f;