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
|
|
@ -32,6 +32,32 @@ public sealed class UiRoot : UiElement
|
|||
/// <summary>Single owner for named retained-window lifecycle and raise policy.</summary>
|
||||
public RetailWindowManager WindowManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Campaign LA gate round 2 (register AD-98): when set, the retained tree
|
||||
/// is laid out in this fixed authored canvas (the char-select screen's
|
||||
/// 800×600) and the whole tree — widgets, glyphs, art — is stretched to
|
||||
/// the window as one unit, matching retail's present-time frame stretch
|
||||
/// for fixed-canvas pre-world screens. Draw applies the scale at the
|
||||
/// renderer's quad chokepoint; the mouse entry points apply the inverse,
|
||||
/// so <see cref="MouseX"/>/<see cref="MouseY"/> and every hit test live
|
||||
/// in canvas space. Null (the in-world default) is native 1:1.
|
||||
/// </summary>
|
||||
public Vector2? FixedCanvasSize { get; set; }
|
||||
|
||||
/// <summary>Window→canvas stretch factor; One when no fixed canvas is set.</summary>
|
||||
public Vector2 CanvasScale =>
|
||||
FixedCanvasSize is { X: > 0f, Y: > 0f } canvas && Width > 0f && Height > 0f
|
||||
? new Vector2(Width / canvas.X, Height / canvas.Y)
|
||||
: Vector2.One;
|
||||
|
||||
private (int x, int y) MapWindowToCanvas(int x, int y)
|
||||
{
|
||||
Vector2 scale = CanvasScale;
|
||||
return scale == Vector2.One
|
||||
? (x, y)
|
||||
: ((int)MathF.Round(x / scale.X), (int)MathF.Round(y / scale.Y));
|
||||
}
|
||||
|
||||
// ── Device-level state ───────────────────────────────────────────────
|
||||
public int MouseX { get; private set; }
|
||||
public int MouseY { get; private set; }
|
||||
|
|
@ -370,6 +396,21 @@ public sealed class UiRoot : UiElement
|
|||
}
|
||||
|
||||
public void Draw(UiRenderContext ctx)
|
||||
{
|
||||
// AD-98 fixed-canvas stretch: scope the renderer's canvas scale to
|
||||
// exactly this tree's draws (world-space HUD stays native).
|
||||
ctx.TextRenderer.CanvasScale = CanvasScale;
|
||||
try
|
||||
{
|
||||
DrawCore(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ctx.TextRenderer.CanvasScale = Vector2.One;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCore(UiRenderContext ctx)
|
||||
{
|
||||
// Render children (panels) sorted by z-order — modal last so it
|
||||
// sits on top.
|
||||
|
|
@ -401,6 +442,7 @@ public sealed class UiRoot : UiElement
|
|||
|
||||
public void OnMouseMove(int x, int y)
|
||||
{
|
||||
(x, y) = MapWindowToCanvas(x, y);
|
||||
int dx = x - MouseX;
|
||||
int dy = y - MouseY;
|
||||
MouseX = x;
|
||||
|
|
@ -552,6 +594,7 @@ public sealed class UiRoot : UiElement
|
|||
|
||||
public void OnMouseDown(UiMouseButton btn, int x, int y, uint flags = 0)
|
||||
{
|
||||
(x, y) = MapWindowToCanvas(x, y);
|
||||
MouseX = x; MouseY = y;
|
||||
UpdateButtonFlag(btn, down: true);
|
||||
_pressX = x; _pressY = y;
|
||||
|
|
@ -707,6 +750,7 @@ public sealed class UiRoot : UiElement
|
|||
|
||||
public void OnMouseUp(UiMouseButton btn, int x, int y, uint flags = 0)
|
||||
{
|
||||
(x, y) = MapWindowToCanvas(x, y);
|
||||
MouseX = x; MouseY = y;
|
||||
UpdateButtonFlag(btn, down: false);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue