fix(ui): Campaign LA gate round 2 — character-select scales as one authored canvas
Third iteration on the screen, completing AD-98. The previous substitution
stretched only the root BACKGROUND while the child widgets stayed at their
authored 800x600 pixel positions - and the background painting carries
visual anchors (the World/Characters captions are art), so the user gate
showed captions overlapping the listbox and every widget misaligned
against the stretched art.
Retail model (established at 71bf24fb): fixed-canvas pre-world screens
render at authored 800x600 and the whole composed frame stretches once at
presentation; the blitter has no stretch mode. Our equivalent now does the
same one stage earlier:
- UiRoot.FixedCanvasSize: while the char-select screen is active, the
retained tree lays out in its authored canvas and Draw scopes a uniform
scale onto TextRenderer.CanvasScale; the mouse entry points apply the
exact inverse so MouseX/MouseY and every hit test live in canvas space.
- TextRenderer.AppendQuad is the single emission chokepoint - sprites,
rects, AND glyphs scale together, including retail-authentic non-uniform
aspect distortion and stretched text. World-space HUD stays native (the
scale resets outside UiRoot.Draw).
- CharacterManagementUiController stops resizing Root to the viewport;
activate/deactivate/dispose set and clear the host canvas.
- UiDatElement returns to retail-pure copy-or-tile; the interim
StretchOwnBackgroundToFill flag is deleted.
- AD-98 updated to describe the completed substitution.
Tests: canvas-scale quad math, inverse input mapping (window click lands
on the canvas-space widget), degenerate-size guards, controller keeps
authored extent + sets/clears the canvas. App suite 5085/6 skips; live-DAT
char-select probes 3/3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
71bf24fb6f
commit
73041d7015
8 changed files with 277 additions and 106 deletions
121
tests/AcDream.App.Tests/UI/UiRootFixedCanvasTests.cs
Normal file
121
tests/AcDream.App.Tests/UI/UiRootFixedCanvasTests.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.App.Tests.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Campaign LA gate round 2 (register AD-98): retail renders fixed-canvas
|
||||
/// pre-world screens (char select, authored 800×600) at authored size and
|
||||
/// stretches the whole composed frame at presentation — its UI blitter has no
|
||||
/// stretch mode. acdream's equivalent is <see cref="UiRoot.FixedCanvasSize"/>:
|
||||
/// draw applies one uniform scale at the renderer's quad chokepoint, and the
|
||||
/// mouse entry points apply the exact inverse so hit-testing lives in canvas
|
||||
/// space. These tests pin the scale math and the inverse input mapping — the
|
||||
/// half that, if wrong, makes the user click on art and hit nothing.
|
||||
/// </summary>
|
||||
public class UiRootFixedCanvasTests
|
||||
{
|
||||
[Fact]
|
||||
public void CanvasScale_IsOne_WithoutFixedCanvas()
|
||||
{
|
||||
var root = new UiRoot { Width = 1920f, Height = 1080f };
|
||||
Assert.Equal(Vector2.One, root.CanvasScale);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanvasScale_IsWindowOverCanvas_WhenFixed()
|
||||
{
|
||||
var root = new UiRoot
|
||||
{
|
||||
Width = 1920f,
|
||||
Height = 1080f,
|
||||
FixedCanvasSize = new Vector2(800f, 600f),
|
||||
};
|
||||
Assert.Equal(new Vector2(2.4f, 1.8f), root.CanvasScale);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanvasScale_IsOne_ForDegenerateCanvasOrWindow()
|
||||
{
|
||||
var zeroCanvas = new UiRoot
|
||||
{
|
||||
Width = 1920f,
|
||||
Height = 1080f,
|
||||
FixedCanvasSize = new Vector2(0f, 600f),
|
||||
};
|
||||
Assert.Equal(Vector2.One, zeroCanvas.CanvasScale);
|
||||
|
||||
var zeroWindow = new UiRoot
|
||||
{
|
||||
Width = 0f,
|
||||
Height = 0f,
|
||||
FixedCanvasSize = new Vector2(800f, 600f),
|
||||
};
|
||||
Assert.Equal(Vector2.One, zeroWindow.CanvasScale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The load-bearing inverse: a click at WINDOW coordinates must land on the
|
||||
/// widget whose authored CANVAS rect the user visually clicked. The button
|
||||
/// sits at canvas (300,400)+(120×40); at a 1920×1080 window over an 800×600
|
||||
/// canvas it appears at window (720,720)-(1008,792). Clicking window
|
||||
/// (860,750) — canvas (358,417) — must click it; clicking window (300,400)
|
||||
/// — canvas (125,222), visually empty — must not.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MouseInput_MapsWindowCoordsToCanvasSpace()
|
||||
{
|
||||
var root = new UiRoot
|
||||
{
|
||||
Width = 1920f,
|
||||
Height = 1080f,
|
||||
FixedCanvasSize = new Vector2(800f, 600f),
|
||||
};
|
||||
int clicks = 0;
|
||||
var button = new UiButton(
|
||||
new ElementInfo { Width = 120, Height = 40 },
|
||||
_ => (0u, 0, 0))
|
||||
{
|
||||
Left = 300f,
|
||||
Top = 400f,
|
||||
Width = 120f,
|
||||
Height = 40f,
|
||||
OnClick = () => clicks++,
|
||||
};
|
||||
root.AddChild(button);
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 860, 750);
|
||||
root.OnMouseUp(UiMouseButton.Left, 860, 750);
|
||||
Assert.Equal(1, clicks);
|
||||
Assert.Equal(358, root.MouseX);
|
||||
Assert.Equal(417, root.MouseY);
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 300, 400);
|
||||
root.OnMouseUp(UiMouseButton.Left, 300, 400);
|
||||
Assert.Equal(1, clicks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MouseInput_IsUntouched_WithoutFixedCanvas()
|
||||
{
|
||||
var root = new UiRoot { Width = 1920f, Height = 1080f };
|
||||
int clicks = 0;
|
||||
var button = new UiButton(
|
||||
new ElementInfo { Width = 120, Height = 40 },
|
||||
_ => (0u, 0, 0))
|
||||
{
|
||||
Left = 300f,
|
||||
Top = 400f,
|
||||
Width = 120f,
|
||||
Height = 40f,
|
||||
OnClick = () => clicks++,
|
||||
};
|
||||
root.AddChild(button);
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 360, 420);
|
||||
root.OnMouseUp(UiMouseButton.Left, 360, 420);
|
||||
Assert.Equal(1, clicks);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue