- Quest dialogs are tooltips. A tooltip now keeps the offset it gets when it becomes visible (the hovered window's, or centred for dialogs) and records it for hit tests; its own event handler is shifted too. Hidden windows no longer count as hovered. - Esc closes a visible dialog the way its answer buttons do: hide, then cDialogManager::Answer with no answer (ends the input lock, nothing chosen). - Autotest can select a save game row (-Save). - Names for the UI manager, tooltip and dialog functions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
89 lines
4.8 KiB
Markdown
89 lines
4.8 KiB
Markdown
# HUD layout in widened views (`src/mods/hud.cpp`)
|
|
|
|
The UI is laid out for 1024x768. In a widened render target (e.g. 1835x768)
|
|
the windows are moved as a whole, without touching their own coordinates:
|
|
the bottom bar and all dialogs are centred, the party panel (portrait, health)
|
|
sits at the right edge. Main menu and loading screen keep the classic 1024x768
|
|
frame (see `widescreen.md`).
|
|
|
|
## How a window is moved
|
|
|
|
A window's render and event methods are hooked (`HUD_RENDER`, `HUD_RENDER3`,
|
|
`HUD_EVENT`). While one of them runs on a thread, everything the window sees or
|
|
draws is translated by its offset `dx` (outermost window wins, children and
|
|
shared base methods are inside the scope):
|
|
|
|
| What | Where | Translation |
|
|
|---|---|---|
|
|
| screen-space vertices (`XYZRHW`) | `IDirect3DDevice7::DrawPrimitive` / `DrawIndexedPrimitive` (vtable 25 / 26) | x + dx |
|
|
| 3D items (belt, equipment) | ortho projection centred on the screen | none needed |
|
|
| mouse events | `cEvent_mouseMove` x at +4 (previous) and +0xc; button down / up x at +8 | x - dx, restored afterwards |
|
|
| mouse cursor object (`*0xcdbadc`, accessor `0x6550f0`) | x at +4, previous x at +0xc (polled for hover, item pick-up `0x6559a0`) | the accessor returns a per-thread copy with x - dx; dwords the window changed are written back afterwards |
|
|
| `GetMouseClientPos` (`0x66e500`) | polled position | x - dx |
|
|
|
|
## Calls outside a window's scope
|
|
|
|
- Tooltips (`cUI_Tooltip::vf14` `0x6e7300`) are separate windows that the
|
|
hovered window positions in its own layout: they draw with the offset of the
|
|
moved window under the cursor (noted while windows render, expires after
|
|
150 ms).
|
|
- Hit tests (`vf1c`: `0x731d40`, taskbar `0x6dfb10`) are also called by the UI
|
|
manager with plain render coordinates, e.g. "is UI under the cursor" for
|
|
world picking (`0x75a370`). They use the offset the top-level window (parent
|
|
chain at +0x50) was last drawn with.
|
|
- The item carried by the cursor (`0x6555e0`, drawn by the render loop) and the
|
|
manager's status line / message box (`0x75bbf0`, `0x75c200`, laid out around
|
|
x = 512) draw with the centre offset.
|
|
|
|
## UI events
|
|
|
|
`vf04` of an event is its category: 1 mouse move, 2 button down, 3 button up,
|
|
4 key (code at +8), 5 UI command (`cEventUI2`: id at +4, vf08 subtype). Only
|
|
mouse events carry positions; category 5 events with subtype 0x10 test a point
|
|
at +0x48 in `cUI_Window2::vf10`, but commands such as "open ESC menu" (0x11)
|
|
keep unrelated data there.
|
|
|
|
## Why the cursor is copied, not shifted
|
|
|
|
UI windows render on the render thread while world input (picking,
|
|
hold-to-move, `0x611f10`) reads the same cursor object on another thread.
|
|
Shifting the shared object for the duration of a window's scope let the world
|
|
sample a position ~400 px off now and then (jerky hold-to-move). Every user of
|
|
the cursor goes through the accessor `0x6550f0`, so inside a moved window it
|
|
returns a thread-local copy instead (with its own CRITICAL_SECTION at +0x24;
|
|
copying and writing back hold the real object's lock).
|
|
|
|
## Full-screen layers
|
|
|
|
A primitive drawn inside a moved window that covers the whole 1024x768 layout
|
|
(e.g. the dimming behind the exit confirmation) is stretched across the render
|
|
width instead of shifted.
|
|
|
|
## Standalone controls (quest dialog)
|
|
|
|
Some dialogs (e.g. accepting a quest) are not one of the hooked window
|
|
classes: their buttons and texts sit directly in the UI manager as top-level
|
|
controls (measured: the clicked button's root window was the cUI_Button2
|
|
itself). They were drawn unshifted or with whichever window's scope happened
|
|
to be active, so the dialog jumped between the 1024 layout and the centre and
|
|
clicks only hit at the unshifted position. `cUI_Button2`, `cUI_Static2`,
|
|
`cUI_StaticText64`, `cUI_StaticText64FX` (render and event) and
|
|
`cUI_TextLines` (render) are hooked as centred windows too; inside other
|
|
windows they are in that window's scope already (outermost wins).
|
|
|
|
## Tooltips and quest dialogs (2026-10-16)
|
|
|
|
Quest dialogs are tooltips: `cUI_Manager::tooltipCreate` (`0x75a870`) hands
|
|
out a panel from a pool at cUI_Manager +0x124 (`UI_WND_TOOLTIP`, created
|
|
1024x768, flags 0x804 while hidden); the quest dialog used one at 136,306,
|
|
752x157 (flags 0x805). Tooltips used to be drawn with the offset of the moved
|
|
window under the cursor in every frame, and without recording an offset for
|
|
hit tests: dialogs jumped to the 1024 frame when the cursor left the UI, and
|
|
their clicks only hit at the unshifted position. Now a tooltip keeps the
|
|
offset it gets when it becomes visible (the hovered window's, or the centre
|
|
when no moved window is under the cursor: a dialog) and records it for hit
|
|
tests. Visible dialog tooltips: `mods::hud::VisibleDialogTooltips`.
|
|
|
|
The standalone control hooks (buttons, texts) above turned out not to be what
|
|
the quest dialog is made of; they stay, for top-level controls in general.
|