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>
113 lines
3.8 KiB
Markdown
113 lines
3.8 KiB
Markdown
# 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.
|