Decomp-first per the block's rule: the research doc
(docs/research/2026-08-13-retail-ui-display-change.md, committed here)
pulled retail's actual mechanism before any code. A display change runs
UIElementManager::RefreshEvent @0x0045C530 ->
UIElement::UpdateForParentSizeChange @0x00462640, which unconditionally
re-applies every floating window's own clamping MoveTo override
(x = max(0, min(x, parentW - selfW)) - top-left priority, oversized
windows pin to 0), then broadcasts global message 0xE whose sole
listener reloads the per-resolution auto layout. No proportional moves,
no resets; retail saves layouts only via @saveui.
Port: RetailWindowLayoutPersistence.ClampAllToScreen() is the cascade
clamp (no store I/O; _restoring suppresses the per-move save so a live
drag-resize cannot write settings.json per frame), and
RetailUiRuntime.Draw carries a two-step screen-size edge detector:
change frame -> clamp; first stable frame -> one
RestoreAll(saveBack:false) per-resolution reload (the 0xE analog; no
lazy save-back, matching retail's save-only-on-command). The login
restore path already used retail's exact clamp math (Apply) - the live
trigger was the missing half, which is precisely the stranding the user
reported.
Deliberate deviation, register AD-91: retail's gmFloatyChatUI windows
have NO clamp and can strand; the block's requirement ("UI windows must
stay reachable") clamps every registered window uniformly.
Tests: 5 new persistence facts (clamp/top-left-pin/no-move/no-save-on-
clamp/no-save-on-live-reload). App suite 4,967/3 skips. Gate script
section D3 filled in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
4.4 KiB
Markdown
83 lines
4.4 KiB
Markdown
# Display & resolution block — connected-gate test script
|
||
|
||
**Status: slices D1 (#389 FOV) and D2 (#391 curated list) are LANDED and
|
||
testable now; D3 (#390 UI reposition) and D4–D6 (fullscreen family
|
||
#377/#376/#388) fill in as they land.** Launch with `ACDREAM_RETAIL_UI=1`
|
||
against local ACE, **Release** build for anything you judge by feel or
|
||
frame rate. The resize/display chain logs evidence permanently — after any
|
||
step you can cross-check `display: resolution pick`, `window: framebuffer
|
||
resize event`, and `vulkan: swapchain recreated` lines in the launch log.
|
||
|
||
---
|
||
|
||
## §D1 — Widescreen FOV feel (#389, SmartboxFOV)
|
||
|
||
The world camera now uses retail's law: your horizontal view stays roughly
|
||
constant (~84° at 16:9) and wide screens trim the vertical slice instead of
|
||
bloating the sides. Your stored Field of View migrates 60 → 90 (the retail
|
||
default) on first load.
|
||
|
||
1. **Just look at the world** on your 16:9 window at your usual spot
|
||
(Holtburg outdoors is ideal). Compared to yesterday: the view should
|
||
feel slightly more "zoomed"/framed — retail's framing — rather than the
|
||
old wide-angle look. If you have retail side-by-side, the framing should
|
||
now match it at the same window shape.
|
||
2. **Resize the window across shapes** (drag it narrow, then wide). The
|
||
world must stay geometrically true at every shape — no squish, no
|
||
stretch, circles stay circles (pre-#389, mid-session resizes distorted
|
||
the world in chase mode; that bug is dead).
|
||
3. **F11 → Config → Field of View slider.** It now means retail's gameFOV
|
||
in degrees (10–160, default 90) and applies LIVE as you drag —
|
||
the world widens/narrows immediately, no restart. Defaults restores 90.
|
||
4. Set it back to 90 when done (or click Defaults).
|
||
|
||
## §D2 — Curated resolution list (#391)
|
||
|
||
1. **F11 → Config → Resolution dropdown.** The list is now your monitor's
|
||
real modes, modern widescreen only: no 800x600, no 1024x768, nothing
|
||
your 2560x1440 desktop can't host (no 3840x2160 entry). Expect roughly:
|
||
1280x720 / 1366x768 / 1600x900 / 1920x1080 / 2560x1440 — exactly what
|
||
your monitor reports, one entry per size.
|
||
2. **Pick a few.** Every pick must land exactly (window resizes AND
|
||
re-renders crisp — §the #387 chain), including 2560x1440.
|
||
3. **Click Defaults.** The Resolution row restores **2560x1440** (your
|
||
desktop's own mode — the deliberate replacement for retail's 800x600
|
||
default; register row IA-22), and the dropdown shows it selected.
|
||
|
||
## §D3 — UI windows stay reachable on downscale (#390)
|
||
|
||
Retail's rule, ported exactly: on a display change every floating window
|
||
re-clamps to `x = max(0, min(x, screenW − windowW))` — top-left priority —
|
||
and the per-resolution saved layout for the NEW size reloads (without
|
||
saving anything until you save).
|
||
|
||
1. **At 2560x1440, drag several windows to the edges** — chat to the
|
||
bottom-right corner, the Options panel far right, a floating chat too.
|
||
2. **Pick 1280x720 in the Resolution dropdown.** Every window must end up
|
||
fully inside the new screen — anything that was past the new bounds
|
||
slides in flush with the edge; nothing needs a window-resize to fetch
|
||
back. (This includes floating chat windows — retail actually strands
|
||
those; we clamp them by the block's own requirement, register AD-91.)
|
||
3. **Drag-resize the window edge smaller** (not via the dropdown). Windows
|
||
must stay reachable continuously while you drag — the clamp runs live.
|
||
4. **Per-resolution memory:** at 1280x720 move a window somewhere
|
||
distinctive, pick 2560x1440, then pick 1280x720 again — the window
|
||
returns to where 1280x720 last had it (each resolution keeps its own
|
||
layout, like retail's per-resolution auto layouts — YOUR drags save
|
||
immediately under the current resolution; the automatic clamp/reload on
|
||
a resolution change never writes anything).
|
||
|
||
## §D4–§D6 — Fullscreen family (#377/#376/#388) — PENDING SLICES
|
||
|
||
*(To be filled in when the fullscreen slices land. Will include: fullscreen
|
||
on/off via the Config checkbox, resolution picks while fullscreen becoming
|
||
REAL display-mode switches — **[BLACK-SCREEN RISK]** steps will be flagged:
|
||
have Alt+F4 / the physical power of patience ready — and the #388 crash
|
||
scenario surviving: a fullscreen-state settings apply must never take the
|
||
client down.)*
|
||
|
||
---
|
||
|
||
**Carried context for the gate:** the FPS cost of large windows is real
|
||
physics (more pixels = more GPU work) — judge frame rate in Release only.
|
||
The title bar shows live FPS.
|