From 997b72045572e250049e590cc0b0f6867f77f2a1 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 17 Aug 2026 10:52:13 +0200 Subject: [PATCH] fix(test): UI probe pointer commands convert canvas to window coordinates; char-select Enter logs its outcome MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The automation probe fed element-center CANVAS coordinates straight into UiRoot.OnMouseMove/Down/Up, which take WINDOW coordinates and map window->canvas internally (UiRoot.MapWindowToCanvas). The two spaces are identical on every screen without UiRoot.FixedCanvasSize — every prior probe gate passed — but the character-select/chargen screens stretch an authored 800x600 canvas across the window, so every synthetic click and hover landed at canvas*(canvas/window): nowhere near the target. The enter-world connected gate's 'click element 0x100003A2' silently did nothing for two full rounds. Element-derived pointer paths (ClickAt, DragAt, HoverElement) now convert canvas->window via UiRoot.CanvasScale; raw 'mousemove x y' stays a passthrough. CharacterManagementUiController.EnterSelected also logs a once-per-click outcome line ('[UI] character enter accepted/rejected status=...') — a refused Enter was previously indistinguishable from a click that never dispatched (both silent), which cost a connected-gate round to tell apart. Co-Authored-By: Claude Fable 5 --- .../Layout/CharacterManagementUiController.cs | 7 +++++ .../UI/Testing/RetailUiAutomationProbe.cs | 29 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/AcDream.App/UI/Layout/CharacterManagementUiController.cs b/src/AcDream.App/UI/Layout/CharacterManagementUiController.cs index 963d5e23..f42e241a 100644 --- a/src/AcDream.App/UI/Layout/CharacterManagementUiController.cs +++ b/src/AcDream.App/UI/Layout/CharacterManagementUiController.cs @@ -580,6 +580,13 @@ internal sealed class CharacterManagementUiController : IDisposable // remains authoritative and closes it on InWorld/error/reset. EnsureEnterWait(); RuntimeCommandResult result = _bindings.Enter(); + // Enter-world round (2026-08-17): once-per-click outcome line — a + // refused Enter was previously indistinguishable from a click that + // never dispatched (both silent), which cost a full connected-gate + // round to tell apart. + Console.WriteLine(result.Accepted + ? "[UI] character enter accepted" + : $"[UI] character enter rejected status={result.Status}"); if (!result.Accepted) CloseContext(ref _enterWaitContext, suppressCallback: true); InvalidateAndTick(); diff --git a/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs b/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs index d3ab64c1..f9a76784 100644 --- a/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs +++ b/src/AcDream.App/UI/Testing/RetailUiAutomationProbe.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Numerics; using System.Text; using AcDream.Core.Items; @@ -147,7 +148,9 @@ public sealed class RetailUiAutomationProbe { var target = FindByDatElementId(datElementId); if (target is null) return Fail($"element 0x{datElementId:X8} not found"); - _root.OnMouseMove((int)target.CenterX, (int)target.CenterY); + // Same canvas→window conversion as ClickAt — see CanvasToWindow. + (int x, int y) = CanvasToWindow((int)target.CenterX, (int)target.CenterY); + _root.OnMouseMove(x, y); return true; } @@ -282,8 +285,30 @@ public sealed class RetailUiAutomationProbe } } + /// + /// Enter-world round (2026-08-17): element coordinates from the probe's + /// tree walk are CANVAS coordinates, but / + /// OnMouseDown/OnMouseUp take WINDOW coordinates and map + /// window→canvas internally (UiRoot.MapWindowToCanvas). The two + /// spaces are identical on every ordinary screen (no + /// ), which is why every prior probe + /// gate passed — the character-select/chargen screens are the first + /// STRETCHED canvases this apparatus drove, and there the mismatch sent + /// every synthetic click to canvas·(canvas/window), i.e. nowhere near the + /// target element. Element-derived pointer paths therefore convert + /// canvas→window here before touching the root. + /// + private (int x, int y) CanvasToWindow(int x, int y) + { + Vector2 scale = _root.CanvasScale; + return scale == Vector2.One + ? (x, y) + : ((int)MathF.Round(x * scale.X), (int)MathF.Round(y * scale.Y)); + } + private void ClickAt(int x, int y) { + (x, y) = CanvasToWindow(x, y); Advance(16); _root.OnMouseMove(x, y); _root.OnMouseDown(UiMouseButton.Left, x, y); @@ -294,6 +319,8 @@ public sealed class RetailUiAutomationProbe private void DragAt(int startX, int startY, int endX, int endY) { + (startX, startY) = CanvasToWindow(startX, startY); + (endX, endY) = CanvasToWindow(endX, endY); Advance(16); _root.OnMouseMove(startX, startY); _root.OnMouseDown(UiMouseButton.Left, startX, startY);