acdream/docs/research/2026-06-20-window-manager-inventory-handoff.md
Erik a391b86a2e docs(handoff): window manager → inventory window → paperdoll (next D.2b arc)
Frames the next core-panels work for a fresh session, after B.1 (drag spine) +
B.2 (toolbar reorder/remove + wire) + toolbar collapse merged this session.
Three sub-phases in order: (A) minimal window manager (open/close + I-key toggle;
UiHost has no open/close API today, windows are always-on); (B) inventory window
(Stream C, synthesis §4 — UiItemList grid + sub-window mount + wire gaps +
InventoryController; the drag SOURCE that closes B.2's drag-from-inventory via a
SourceKind==Inventory branch in ToolbarController.HandleDropRelease); (C) paperdoll
UiViewport (Type 0xD) + Core→App IUiViewportRenderer seam, heaviest, last. Spell
bar deferred. Reuse the shipped spine/ShortcutStore/wire. Paste-able new-session
prompt + MEMORY index line inside.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:38:41 +02:00

200 lines
14 KiB
Markdown

# Handoff — window manager → inventory window → paperdoll (the next D.2b core-panels arc)
**Date:** 2026-06-20
**From:** the D.5.3 session that shipped the drag-drop spine (B.1), toolbar shortcut reorder/remove +
wire (B.2), and toolbar collapse-to-one-row — all **merged to `main`** (merge commit `abbd97b`;
branch `claude/hopeful-maxwell-214a12`, tip `14443e5`). Full suite green (2752) at merge.
**Line numbers below WILL drift — grep the symbol, don't trust the line.**
---
## 0. What just shipped (the foundation the next work builds on)
This is now on `main` and visually confirmed:
- **Drag-drop spine (B.1)** — the shared widget-level drag machine. `UiRoot` holds the device-level
drag (BeginDrag/UpdateDragHover/FinishDrag, ghost snapshotted at begin, drop delivered only on a real
hit). `UiElement` has `GetDragPayload()`/`GetDragGhost()`/`IsDragSource` virtuals (UiRoot stays
item-agnostic). `UiItemSlot` is drag source + drop target + accept/reject overlay; `UiItemList` owns
a registered `IItemListDragHandler { OnDragLift; OnDragOver; HandleDropRelease }`. Payload =
`ItemDragPayload(ObjId, SourceKind, SourceSlot, SourceCell)`. Spec/plan:
`docs/superpowers/{specs,plans}/2026-06-20-d2b-drag-drop-spine*.md`.
- **Toolbar shortcut drag (B.2)** — retail **remove-on-lift / place-on-drop / no-restore**.
`ToolbarController : IItemListDragHandler` drives a `ShortcutStore` (`AcDream.Core.Items`, 18 slots)
+ the wire: `InventoryActions.BuildAddShortcut(seq,index,objectGuid,spellId,layer)` /
`BuildRemoveShortcut`, sent via `WorldSession.SendAddShortcut`/`SendRemoveShortcut`. Green-cross
accept sprite `0x060011FA`. Spec/plan: `docs/superpowers/{specs,plans}/2026-06-20-d2b-toolbar-shortcut-drag*.md`.
- **Toolbar collapse** — `UiCollapsibleFrame` bottom-edge snap resize (1-row ↔ 2-row). Spec:
`docs/superpowers/specs/2026-06-20-d2b-toolbar-collapse-design.md`.
**The one B-stream piece still owed:** *drag FROM inventory onto the bar.* B.2 did the within-bar
reorder/remove (`SourceKind == ShortcutBar`). The fresh-from-inventory branch (retail `flags & 0xE == 0`
`CreateShortcutToItem`) needs the inventory window as a drag SOURCE — so it lands WITH Stream C
below, not before it.
---
## 1. Read first
- This doc.
- `docs/research/2026-06-16-ui-panels-synthesis.md` §4 — **the build plan** for the core panels
(build order, widget list, cross-panel wire table). Stream C follows it.
- `docs/research/2026-06-16-inventory-deep-dive.md``gmInventoryUI 0x21000023` nests paperdoll
(`0x21000024`) + backpack (`0x21000022`) + 3D-items (`0x21000021`); backpack burden Meter
(`SetLoadLevel`→fill `0x69`); full inventory wire catalog + acdream parse-status.
- `docs/research/2026-06-16-equipment-paperdoll-deep-dive.md` — the doll = `UIElement_Viewport`
(Type `0xD`) hosting a re-dressed player clone; ~25 equip slots; wield = `GetAndWieldItem 0x001A`
(builder MISSING); needs a Core→App `IUiViewportRenderer` seam.
- `docs/research/2026-06-18-d53-bar-finish-and-inventory-handoff.md` §C — the predecessor's
current-code readiness for the inventory window (most still accurate; deltas noted below).
- `claude-memory/project_d2b_retail_ui.md` (the toolkit, now incl. B.1/B.2/collapse) +
`claude-memory/project_object_item_model.md` (the D.5.4 `ClientObjectTable`).
**Mandatory workflow** (CLAUDE.md): grep `docs/research/named-retail/acclient_2013_pseudo_c.txt` by
`class::method` → cross-ref ACE/holtburger → pseudocode → port; conformance tests throughout. Each
sub-phase gets its own brainstorm → spec → plan → subagent-driven (the flow this arc has been using).
---
## 2. Scope — three sub-phases, in order
| # | Sub-phase | One-liner |
|---|---|---|
| **A** | **Window manager (open/close + `I`-key)** | The small shared infra to open/close a window + a keybind to toggle the inventory window. Prerequisite for everything below. |
| **B** | **Inventory window (Stream C / D.5.5)** | `gmInventoryUI` nesting paperdoll + backpack + 3D-items. The drag SOURCE that completes "drag-from-inventory." |
| **C** | **Paperdoll** (`UiViewport`) | The 3D dressed-doll viewport — the heaviest piece, its own Core→App seam. Last. |
**Out of scope:** the spell bar (a separate spell-casting feature, NOT the action-bar spell shortcuts);
the faithful dragbar/resizebar window resize (IA-12 whole-window-drag + the new bottom-edge collapse
are the accepted approximations).
---
## 3. Sub-phase A — window manager (open/close + `I`-key) — DO THIS FIRST
**Current state:** every retail-UI window is **always-on at a hardcoded position** — the vitals,
chat, and toolbar are added to `_uiHost.Root` unconditionally at startup under `ACDREAM_RETAIL_UI=1`
(grep `_uiHost.Root.AddChild` in `GameWindow.cs`; the mounts are in the `_options.RetailUi` block).
`UiHost` has **no open/close API** (`src/AcDream.App/UI/UiHost.cs` — it exposes `Root`, `Tick`,
`Draw`, `WireMouse/Keyboard` only). Input flows through `InputDispatcher` (Phase K) —
`keybinds.json` + `KeyBindings`; the `I` key needs an action + binding.
**Minimum deliverable:** open/close a top-level window + toggle the inventory window with `I`.
Concretely:
- A small open/close concept on `UiHost`/`UiRoot` (e.g. register a named window + Show/Hide/Toggle that
flips `Visible` and brings it to front via `ZOrder`). Today windows are always `Visible=true`; the
manager makes a window default-hidden and toggleable. (z-order/persist can be minimal — `Visible` +
a top-most `ZOrder` bump on open.)
- An `InputAction` (e.g. `ToggleInventory`) + the retail default `I` keybind (cross-check
`docs/research/named-retail/retail-default.keymap.txt`), wired through the existing
`InputDispatcher` (see `claude-memory/project_input_pipeline.md`), gated by `WantsKeyboard` so it
doesn't fire while a chat input is focused.
**Brainstorm questions (A):** Is the window manager a property on `UiRoot` (a registry of named
top-level windows + Show/Hide/Toggle), or a thin `UiHost` API? How is z-order handled on open (bring
to front)? Does `I` toggle (open↔close) or only open (Esc/close-button to close)? Persist open-state
across the session (in-memory) only — no disk persistence yet (that's the deferred Plan-2)?
Faithful retail note: retail windows are opened by the radar/menu buttons + hotkeys and managed by
keystone.dll (no decomp) — so this is a toolkit-defined manager (IA-12/IA-15 umbrella), kept minimal.
---
## 4. Sub-phase B — inventory window (Stream C / D.5.5) — follow the synthesis §4
**The design is already written — follow `2026-06-16-ui-panels-synthesis.md` §4.** This section is the
**current-code readiness** + what's missing. Don't re-derive the design.
**READY (and stronger now, post-B.1/B.2):**
- `UiItemSlot` + `UiItemList` + `IconComposer` (`src/AcDream.App/UI/`) — the shared item-cell spine,
now with the full drag-drop machine (B.1). An inventory grid is `UiItemList` cells + an
`IItemListDragHandler` on the controller.
- `DatWidgetFactory` registers `0x10000031 → UiItemList` (grep `0x10000031` in
`src/AcDream.App/UI/Layout/DatWidgetFactory.cs`).
- The data path: `ClientObjectTable.GetContents(containerGuid)` → ordered guids → `Get(guid)` → full
icon fields (`src/AcDream.Core/Items/ClientObjectTable.cs`). The object/container model shipped in
D.5.4 (`project_object_item_model.md`).
- The shortcut wire + `ShortcutStore` (B.2) — so **drag-from-inventory-onto-the-bar** is now a small
addition: the inventory cell is a drag SOURCE (its handler's `OnDragLift` does NOT remove — an
inventory→bar drag creates a shortcut REFERENCE; the item stays in the pack), and `ToolbarController.
HandleDropRelease` gains the `SourceKind == Inventory` branch (place a new shortcut at the target via
`SendAddShortcut`, no source-bump) — retail's `flags & 0xE == 0` `CreateShortcutToItem` path.
**MISSING (the build, in synthesis order — see the 2026-06-18 handoff §C.1-6 for the detailed list):**
1. **Window manager** — §3 above (do first).
2. **`UiItemList` N-cell grid mode** — currently single-cell (`UiItemList.cs`; `Flush`/`AddItem`
skeleton exists, no column-count/pitch/wrap). LIKELY ~6 cols; confirm from
`UIElement_ItemList::ItemList_AddItem`.
3. **Sub-window mount in `LayoutImporter`**`gmInventoryUI 0x21000023` nests `gm*UI` children with
their own `BaseLayoutId`; the importer only does TEMPLATE inheritance today
(`src/AcDream.App/UI/Layout/LayoutImporter.cs`) — instantiating a nested `gm*UI` window is new.
4. **Wire gaps** (inventory deep-dive §4.3): builders `DropItem 0x001B`, `GetAndWieldItem 0x001A`,
`NoLongerViewingContents 0x0195`; parsers `ViewContents 0x0196`, `SetStackSize 0x0197`,
`InventoryRemoveObject`; fix `ParsePutObjInContainer` (drops the 4th `containerType`) +
`ParseInventoryServerSaveFailed` (drops `weenieError`); register `ViewContents`/`0x019A`/`0x0052`/
`0x00A0` in `GameEventWiring`. (Grep these symbols — none landed this session.)
5. **`InventoryController`** (`gm*UI::PostInit` find-by-id pattern): backpack burden Meter
(`SetLoadLevel`→fill `0x69`), own-pack list + side-pack list, `ObjDescEvent 0xF625` → re-dress.
**Brainstorm questions (B):** Sub-window mount — recursive `Import()` in `LayoutImporter`, or an
external stitch by the controller? Grid column count (confirm 6 from decomp)? Open by `I`-key only, or
also a toolbar inventory button? The inventory cell's drag semantics (lift does NOT remove from the
pack — it's a copy-to-shortcut for the bar, but a real MOVE between packs DOES relocate via
`PutItemInContainer 0x0019`) — pin the SourceKind→action matrix (deep-dive §5.7 opcode table).
---
## 5. Sub-phase C — paperdoll (`UiViewport`, Type 0xD) — heaviest, last
**The single biggest new piece.** No widget, no factory registration, no renderer today. Needs an
`IUiViewportRenderer` **Core→App seam** (structure Rule 2) for a scissored single-entity GL pass — the
doll is the local player's ObjDesc-dressed entity in a fixed viewport. ~25 equip slots; the
element-id→`EquipMask` map; wield = `GetAndWieldItem 0x001A` (builder still MISSING). Brainstorm
separately (it's substantial). Follow `2026-06-16-equipment-paperdoll-deep-dive.md`.
**Brainstorm questions (C):** Does the doll clone the player `WorldEntity` or build a fresh
ObjDesc-dressed `AnimatedEntityState` (the player is the camera, so there's no player-as-renderable
today)? `IUiViewportRenderer` timing (post-world pass vs pre-pass)? Scissor infra (the toolkit has no
GL scissor yet — see the collapse spec's clipping discussion).
---
## 6. Build order + dependency graph
```
A. window manager (open/close + I-key) ← do first; unblocks B
B. inventory window (grid + sub-window mount + wire gaps + InventoryController)
│ └─ also completes B.2's drag-from-inventory (inventory cell = drag source;
│ ToolbarController.HandleDropRelease gains the SourceKind==Inventory branch)
C. paperdoll (UiViewport + IUiViewportRenderer seam + PaperDollController) ← heaviest, last
```
Critical-path note: A is small and unblocks B; the inventory window (B) is the big one and the drag
*source* that closes the B-stream; the paperdoll (C) is the heaviest and independent enough to come
last.
---
## 7. ⚠ State notes for the fresh session
- **Start from `main`** (merge `abbd97b`) — it has all of D.5.2 + D.5.4 + this session's D.5.3 B.1/B.2
+ the collapse, plus the indoor-lighting handoff (`f7f3e08`, issues `#142`/`#143` are LIGHTING, not
UI). Create a new worktree off `main`.
- **ISSUES:** `#144` is the (LOW, latent) empty-item-slot click note from B.1; `#141` is the toolbar
selected-object display (D.5.3a done; mana/stack deferred). `#142`/`#143` are the lighting issues.
- The drag spine's `IItemListDragHandler` + `ItemDragPayload` + `ShortcutStore` + the shortcut wire all
exist now — reuse them; don't rebuild.
- Don't re-port what WorldBuilder/the toolkit already gives (read `worldbuilder-inventory.md` first for
any dat/render work).
---
## 8. New-session prompt (paste into a fresh session)
> Continue acdream's D.2b retail-UI track. **Read `docs/research/2026-06-20-window-manager-inventory-handoff.md` first**, then the 2026-06-16 UI deep-dives + synthesis §4 it references. The drag-drop spine (B.1), toolbar shortcut reorder/remove + wire (B.2), and toolbar collapse all shipped + merged to `main` (`abbd97b`). Next, in order: **(A)** a minimal **window manager** — open/close + an `I`-key toggle for the inventory window (today every retail-UI window is always-on at a hardcoded position; `UiHost` has no open/close API); **(B)** the **inventory window** (`gmInventoryUI` nesting paperdoll/backpack/3D-items — `UiItemList` N-cell grid + sub-window mount in `LayoutImporter` + the inventory wire gaps + `InventoryController`; it's the drag SOURCE that completes B.2's drag-from-inventory via a `SourceKind==Inventory` branch in `ToolbarController.HandleDropRelease`); **(C)** the **paperdoll** `UiViewport` (Type 0xD) 3D doll, with a Core→App `IUiViewportRenderer` seam — heaviest, last. Spell bar DEFERRED. Use the full brainstorm → spec → plan → subagent-driven flow per sub-phase; mandatory grep-named→cross-ref→pseudocode→port for any wire format; conformance tests throughout. Reuse the shipped spine (`IItemListDragHandler`/`ItemDragPayload`/`ShortcutStore`/the AddShortcut/RemoveShortcut wire) — don't rebuild it. Resolve objects via `ClientObjectTable.Get(guid)` / `GetContents(containerGuid)`. Start a new worktree off `main`.
**MEMORY.md index line:**
- [Handoff: window manager → inventory → paperdoll (2026-06-20)](research/2026-06-20-window-manager-inventory-handoff.md) — next D.2b-UI arc after B.1/B.2/collapse (all merged to main `abbd97b`). 3 sub-phases: (A) window manager (open/close + I-key — UiHost has no open/close API today; windows are always-on); (B) inventory window (Stream C, synthesis §4 — UiItemList grid + sub-window mount + wire gaps DropItem 0x1B/GetAndWieldItem 0x1A/ViewContents 0x196 + InventoryController; the drag SOURCE that closes B.2's drag-from-inventory via a SourceKind==Inventory branch); (C) paperdoll UiViewport Type 0xD + Core→App IUiViewportRenderer seam (heaviest). Reuse the shipped drag spine + ShortcutStore + shortcut wire. Spell bar DEFERRED.