fix(ui,runtime): Campaign LA gate-round-2 batch-review fixes F1,F3-F8; file #401

F1 (MUST-FIX): RetailWaitDialogView was the ONE dialog view the 0a7dc7d6
EffectiveCanvasSize sweep missed - the Entering World wait dialog (fires
on ENTER, the char screen primary action) still centered against the raw
window and landed off the visible canvas. Same three-line fix as its three
siblings; the enter-wait test now grows the window over the fixed canvas
and asserts canvas-space centering.

F3/F4: two stale assertions about the DELETED first AD-98 substitution
(the register section-2 header line and the live-DAT oracle test doc) now
describe the completed FixedCanvasSize mechanism - the C4-closeout failure
mode, caught before it cost anything.

F5: RetailDialogData.Confirmation sets ElementAttribute40 itself (retail
MakeConfirmExitDialog writes 0x8E=1, 0xAC=1, 0xC5); the manual set in
GameplayConfirmationController is gone.

F6: MapWindowToCanvas truncates instead of rounding - rounding mapped the
window far edge one past the canvas last valid coordinate, a 1px dead
hit-test band; test updated to truncation semantics + far-edge case.

F7: AD-98 records that the no-letterbox aspect claim has no decomp
citation and is confirmed by the user live gate pass 2026-08-15.

F8: the durable world-name read in StartCore is IsCurrent-gated like every
neighbouring step.

F2 filed as #401 (invert RetailUi to opt-out - product-default decision,
not a gate fix).

App 5100+6 skips, Runtime 1666, green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-15 12:28:06 +02:00
parent 36c14902a8
commit 0baebce262
10 changed files with 87 additions and 19 deletions

View file

@ -261,14 +261,15 @@ public sealed class CharacterManagementLiveDatTests
/// <c>UIElement::UpdateForParentSizeChange</c>, acclient 0x00462640). Retail's
/// own edge-anchor resize mechanism therefore NEVER touches this element's size;
/// it stays a fixed 800x600 rect in retail's own widget tree. This is the pivot
/// fact behind <see cref="AcDream.App.UI.Layout.UiDatElement.StretchOwnBackgroundToFill"/>:
/// since the dat itself asks for no resize, whatever makes the char-select scene
/// fill an arbitrary window resolution in retail (background AND buttons AND
/// listbox together) cannot be a per-element anchor/draw-mode difference — it has
/// to be an out-of-band presentation-time scale of the whole fixed-size frame.
/// acdream instead resizes the MOUNTED root itself (CharacterManagementUiController's
/// constructor) to reach the same visual fill, which is why the background needs
/// its own explicit stretch flag rather than an authored draw-mode bit.
/// fact behind the completed AD-98 substitution
/// (<see cref="AcDream.App.UI.UiRoot.FixedCanvasSize"/>): since the dat itself
/// asks for no resize, whatever makes the char-select scene fill an arbitrary
/// window resolution in retail (background AND buttons AND listbox together)
/// cannot be a per-element anchor/draw-mode difference — it has to be an
/// out-of-band presentation-time scale of the whole fixed-size frame. acdream
/// therefore keeps the mounted root at its authored 800x600 extent and
/// stretches the ENTIRE canvas — widgets, glyphs, art — as one unit at the
/// renderer's quad chokepoint, with inverse mouse mapping.
/// </summary>
[InstalledDatFact]
public void RootAuthorsNoEdgeAnchors_RetailNeverResizesItSelf()

View file

@ -511,6 +511,13 @@ public sealed class CharacterManagementUiControllerTests
Assert.Equal(0u, controller.OperationWaitContext);
Assert.False(environment.Dialogs.IsOpen);
// Gate-round-2 batch review F1: the wait dialog must center in CANVAS
// space like every other dialog view — it fires on ENTER, the
// screen's primary action, and was the one view the EffectiveCanvasSize
// sweep missed. The window is bigger than the fixed canvas here.
environment.Host.Width = 1920f;
environment.Host.Height = 1080f;
controller.Rows[0].OnClick!();
environment.Button(CharacterManagementUiController.EnterElementId).OnClick!();
Assert.Equal(1, environment.Runtime.EnterCalls);
@ -518,8 +525,14 @@ public sealed class CharacterManagementUiControllerTests
RuntimeCharacterSelectionLifecycle.EnteringWorld,
environment.Runtime.View.Snapshot.Lifecycle);
Assert.NotEqual(0u, controller.EnterWaitContext);
Assert.Equal("Entering World", Message(
environment.LastDialog(RetailDialogType.Wait)));
ImportedLayout enterWait = environment.LastDialog(RetailDialogType.Wait);
Assert.Equal("Entering World", Message(enterWait));
Assert.Equal(800f, enterWait.Root.Width);
Assert.Equal(600f, enterWait.Root.Height);
UiElement enterWaitPopup = Assert.Single(
enterWait.Root.Children,
static child => child.Visible && child.Width > 0f);
Assert.InRange(enterWaitPopup.Left + enterWaitPopup.Width * 0.5f, 399f, 401f);
environment.Runtime.SetError("That character is unavailable.");
controller.Tick();

View file

@ -86,11 +86,20 @@ public class UiRootFixedCanvasTests
};
root.AddChild(button);
// Truncation, not rounding (batch review F6): 860/2.4 = 358.33 → 358,
// 750/1.8 = 416.67 → 416. Rounding mapped the window's far edge one
// past the canvas's last valid coordinate (1919 → 800), losing a 1px
// hit-test band at the right/bottom.
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);
Assert.Equal(416, root.MouseY);
// The far window edge maps INSIDE the canvas.
root.OnMouseMove(1919, 1079);
Assert.Equal(799, root.MouseX);
Assert.Equal(599, root.MouseY);
root.OnMouseDown(UiMouseButton.Left, 300, 400);
root.OnMouseUp(UiMouseButton.Left, 300, 400);