fix #381: Options-panel footer needs an opaque backing field
Root cause: a live-DAT probe found retail authors NO backing element behind the Character/Chat/Config tabs' Apply/Reset/Defaults footer — each page root has exactly five children (the row ListBox, its scrollbar, and the three buttons) with zero direct-state media on the root itself. Scrolled row content therefore bled through visibly between/behind the three buttons; the bleed-through is a rendering gap in our own composition, not a missing import. Fix: new minimal widget UiSolidSpriteFill tiles RetailChromeSprites.CenterFill (the SAME panel-background sprite the Options window's own chrome already draws behind everything, not an invented color) across the footer strip's rect, derived from the three buttons' own resolved Top/Height and z-ordered strictly behind every other child so it can never intercept input or occlude the buttons. Register row AP-205 records the synthesis. Regressed by OptionsPanelControllerTests. Bind_SynthesizesOneOpaqueFooterBacking_PerPageWithApplyResetDefaults, which pins exactly one backing field per page, sized from the live button rects, z-ordered behind every sibling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2a248c0d48
commit
a31fd631ad
7 changed files with 234 additions and 17 deletions
|
|
@ -59,6 +59,67 @@ public sealed class OptionsPanelControllerTests
|
|||
Assert.Empty(controller.ConfigPage.Rows);
|
||||
}
|
||||
|
||||
/// <summary>Every <see cref="UiSolidSpriteFill"/> under a subtree, in
|
||||
/// build (document) order.</summary>
|
||||
private static List<UiSolidSpriteFill> CollectFooterBackings(UiElement root)
|
||||
{
|
||||
var found = new List<UiSolidSpriteFill>();
|
||||
Walk(root, found);
|
||||
return found;
|
||||
|
||||
static void Walk(UiElement node, List<UiSolidSpriteFill> acc)
|
||||
{
|
||||
if (node is UiSolidSpriteFill fill) acc.Add(fill);
|
||||
foreach (UiElement child in node.Children) Walk(child, acc);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #381 regression (2026-08-11, gate 4): before this fix, the
|
||||
/// Character/Chat/Config pages drew their Apply/Reset/Defaults buttons
|
||||
/// directly over the ListBox content with nothing behind them — a live-
|
||||
/// DAT probe found retail authors no backing element either (page root
|
||||
/// has exactly 5 children, zero direct-state media), so this is a
|
||||
/// genuine acdream synthesis (register row AP-205), not a missing
|
||||
/// import. Pins that exactly one opaque field is synthesized per page,
|
||||
/// sized/positioned from the buttons' OWN resolved rects, and z-ordered
|
||||
/// strictly behind everything else on that page (so it can never
|
||||
/// occlude the buttons it backs).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Bind_SynthesizesOneOpaqueFooterBacking_PerPageWithApplyResetDefaults()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost();
|
||||
var calls = new List<string>();
|
||||
|
||||
OptionsPanelController? controller = OptionsPanelController.Bind(
|
||||
layout, MakeCallbacks(calls), resolveSprite: _ => (1u, 8, 8));
|
||||
|
||||
Assert.NotNull(controller);
|
||||
// Page slot ids — the SAME literals ConfigOptionsPageController's
|
||||
// own PageSlotElementId/ChatOptionsPageController's own
|
||||
// PageSlotElementId cite, and this file's own TabSwitch tests
|
||||
// already use for Character (0x10000211).
|
||||
foreach (uint pageSlotId in new[] { 0x10000211u, 0x1000050Cu, 0x10000213u })
|
||||
{
|
||||
UiElement pageRoot = UiElement.FindDescendant(controller!.TabPanel, pageSlotId)!;
|
||||
Assert.NotNull(pageRoot);
|
||||
List<UiSolidSpriteFill> backings = CollectFooterBackings(pageRoot);
|
||||
UiSolidSpriteFill backing = Assert.Single(backings);
|
||||
|
||||
Assert.Equal(RetailChromeSprites.CenterFill, backing.SpriteId);
|
||||
Assert.NotNull(backing.SpriteResolve);
|
||||
Assert.True(backing.ClickThrough);
|
||||
Assert.Equal(pageRoot.Width, backing.Width);
|
||||
|
||||
// Drawn BEHIND every sibling already on the page (buttons,
|
||||
// ListBox, scrollbar) — lowest ZOrder wins the painter's-algorithm
|
||||
// back-to-front sort (UiElement.cs's own convention).
|
||||
Assert.All(pageRoot.Children.Where(c => !ReferenceEquals(c, backing)),
|
||||
sibling => Assert.True(backing.ZOrder < sibling.ZOrder));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActivateTabs_SelectsGameplayAsDefault_ButNeverFlushesIt()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue