fix(ui): share primary panel placement
Port gmPanelUI's persistent parent placement semantics across Inventory, Character, and Magic while preserving each imported child's content and resize policy. Synchronize typed retained-window handles so drag, switching, close/reopen, and layout persistence all observe one canonical location; keep effect panels independent. User-verified in normal Release. Release build and all 5,770 tests passed with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
124e046976
commit
3f3cfdac30
7 changed files with 460 additions and 9 deletions
113
docs/research/2026-07-17-retail-shared-main-panel-pseudocode.md
Normal file
113
docs/research/2026-07-17-retail-shared-main-panel-pseudocode.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Retail shared main-panel placement — pseudocode
|
||||
|
||||
**Date:** 2026-07-17
|
||||
**Symptom:** moving Inventory and then opening Character/Skills or Magic opens
|
||||
the new panel at its own stale saved position instead of the position just used.
|
||||
|
||||
## Retail oracle
|
||||
|
||||
Named Sept-2013 retail symbols:
|
||||
|
||||
- `gmPanelUI::SetupChildren @ 0x004BC9E0`
|
||||
- `gmPanelUI::RecvNotice_SetPanelVisibility @ 0x004BC6F0`
|
||||
- `gmPanelUI::PostInit @ 0x004BD010`
|
||||
- `gmPanelUI::ResizeTo @ 0x004BC6E0`
|
||||
|
||||
Verbatim `acclient.h` ownership:
|
||||
|
||||
```text
|
||||
gmPanelUI : UIElement_Field
|
||||
SmartArray<PanelChildInfo> m_childrenInfoArray
|
||||
UIElement* m_currentlyShownPanel
|
||||
UIElement* m_previouslyShownPanel
|
||||
UIElement* m_pPanelPagesFrame
|
||||
bool m_bStretchMode
|
||||
|
||||
PanelChildInfo
|
||||
UIElement* child
|
||||
uint panelID
|
||||
```
|
||||
|
||||
`SetupChildren` resolves each known panel element recursively, stores the
|
||||
child pointer together with its property-`0x10000029` panel ID, and initially
|
||||
hides every child. It does not create independent top-level windows.
|
||||
|
||||
## Retail behavior
|
||||
|
||||
```text
|
||||
SetPanelVisibility(panelId, show):
|
||||
requested = find m_childrenInfoArray entry by panelId
|
||||
if requested is missing:
|
||||
return
|
||||
|
||||
if show:
|
||||
if requested.child == m_currentlyShownPanel:
|
||||
requested.child.SetVisible(true)
|
||||
parent.SetVisible(true)
|
||||
return
|
||||
|
||||
previous = m_currentlyShownPanel
|
||||
m_currentlyShownPanel = requested.child
|
||||
|
||||
if previous is visible:
|
||||
if requested restores previous and previous does not:
|
||||
m_previouslyShownPanel = previous
|
||||
else:
|
||||
m_previouslyShownPanel = null
|
||||
send SetPanelVisibility(previous.panelID, false)
|
||||
|
||||
requested.child.SetVisible(true)
|
||||
parent.SetVisible(true)
|
||||
return
|
||||
|
||||
if requested.child == m_currentlyShownPanel:
|
||||
requested.child.SetVisible(false)
|
||||
if m_previouslyShownPanel exists:
|
||||
send SetPanelVisibility(previous.panelID, true)
|
||||
m_previouslyShownPanel = null
|
||||
else:
|
||||
m_currentlyShownPanel = null
|
||||
parent.SetVisible(false)
|
||||
return
|
||||
|
||||
requested.child.SetVisible(false)
|
||||
```
|
||||
|
||||
The decisive placement fact is structural: Inventory, Character/Skills, and
|
||||
Magic are children of one `gmPanelUI`. The switch changes child visibility;
|
||||
there is no child move and no per-panel top-level position. Dragging the parent
|
||||
therefore moves the one position subsequently used by every child.
|
||||
|
||||
## acdream port shape
|
||||
|
||||
acdream imports these child LayoutDesc roots independently, so each currently
|
||||
has its own retained wrapper. `RetailPanelUiController` already ports retail's
|
||||
one-active-child and restore-previous lifecycle. Complete that logical-parent
|
||||
ownership by adding one canonical placement for the primary toolbar children
|
||||
(Inventory `7`, Character `11`, Magic `13`):
|
||||
|
||||
```text
|
||||
on primary child frame moved:
|
||||
canonicalPosition = moved frame position
|
||||
move every other primary child frame to canonicalPosition
|
||||
|
||||
before showing a primary child:
|
||||
if canonicalPosition exists:
|
||||
move requested child frame to canonicalPosition
|
||||
else:
|
||||
canonicalPosition = requested child frame position
|
||||
|
||||
before hiding the active primary child:
|
||||
capture its current position as canonicalPosition
|
||||
```
|
||||
|
||||
The controller owns this synchronization and subscribes to typed
|
||||
`RetailWindowHandle.Moved` events. Toolbar commands, keyboard commands,
|
||||
restore-previous effects, persistence restore, and direct window dragging all
|
||||
therefore pass through the same owner. Panel-authored size/resize policy stays
|
||||
with the active child; only the retail parent placement is shared.
|
||||
|
||||
ACE contains no client UI implementation, and the in-tree WorldBuilder
|
||||
reference contains no `gmPanelUI` port, so neither supplies a competing
|
||||
behavioral interpretation. The named retail client and verbatim header are the
|
||||
oracle for this UI ownership rule.
|
||||
Loading…
Add table
Add a link
Reference in a new issue