fix(ui): spell-bar drag-reorder works — the per-frame rebuild was destroying the dragged cell (#354)
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Everything already existed — the drag payloads, the favorite wire pair (0x1E3 add-at-position / 0x1E4 remove, byte-confirmed against retail's Event_AddSpellFavorite @0x006A0F70 and ACE), the insert-shift state ops. The bug: lifting a favorite fires SpellbookChanged, the next per-frame Tick rebuilt the bar, the rebuild flushed and recreated every cell, and UiRoot's subtree-removal safety net canceled the in-flight drag whose source had just been destroyed — one frame after every lift, before any drop could land. The rebuild now defers for the duration of the drag gesture, and the drop ports retail's own -1-if-lifted-before-target index adjustment (SpellCastSubMenu::AddFavorite @0x004C7060) so final positions are byte-identical: insert-shift, not swap; drag-out still deletes (the lift's removal stands on a missed drop, retail's shape). The real-pointer-pipeline test fails against the pre-fix code with the exact cancellation and passes after; a discriminator pins that physical-item drop handlers reject the spell payload. AP-172 files the one presentation divergence (mid-drag reflow happens on release, not continuously) — renumbered from the agent's AP-171 draft, which collided with the same-day double-click row. #354 filed and closed. Clean-room complete solution: 11,541 passed / 4 skipped / 0 failed. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0a996a1a91
commit
81a9d85a1d
5 changed files with 260 additions and 5 deletions
|
|
@ -24,6 +24,42 @@ What does NOT go here:
|
|||
- Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending.
|
||||
- Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed.
|
||||
|
||||
## #354 — Spell-bar drag reorder did not work: lifting a favorite canceled the drag before the drop could land
|
||||
|
||||
**Status:** CLOSED 2026-08-08 — user gate finding ("I should be able to
|
||||
rearrange spells on the spell bar. Like dragging one out and dropping it
|
||||
in another position. That does not work today"), diagnosed and fixed same
|
||||
session. Root cause: `SpellcastingUiController.BeginFavoriteDrag` performs
|
||||
retail's press-time removal (`gmSpellcastingUI::RecvNotice_ItemListBeginDrag`
|
||||
@0x004C7360 → `RemoveSpellFromMenu`, matching `PlayerModule::RemoveSpellFavorite`
|
||||
@0x005D4910) — correct and pre-existing — but that removal fires
|
||||
`SpellbookChanged`, and the very next per-frame `Tick()` (production drives
|
||||
this unconditionally via `RetailUiRuntime.Tick`) called `Rebuild()`, which
|
||||
flushes and recreates every favorite-bar cell (`UiItemList.Flush` →
|
||||
`RemoveChild`). `UiRoot`'s subtree-removal safety net
|
||||
(`ClearSubtreeOwnership`) cancels any drag whose source widget is
|
||||
destroyed — so the drag was silently canceled one frame after every lift,
|
||||
before the user could complete a drop. Empirically confirmed: a real-pointer-
|
||||
path test (`DragFavoriteOntoAnotherSlot_ThroughTheRealPointerPipeline_ReordersAndSyncsWire`)
|
||||
driving `UiRoot.OnMouseDown`/`OnMouseMove`/a mid-drag `Tick`/`OnMouseUp` fails
|
||||
with `screen.DragSource == null` against the pre-fix code, and passes after
|
||||
it. Fix (`SpellcastingUiController.cs`): defer the favorite-list rebuild for
|
||||
the whole drag gesture (`_favoriteDragActive`), and compensate the drop-time
|
||||
target index for the resulting stale sibling numbering by porting retail's
|
||||
own `SpellCastSubMenu::AddFavorite` @0x004C7060 index adjustment (decrement
|
||||
the target by one when the lifted item's original index was before it) —
|
||||
same insert-shift semantics `PlayerModule::AddSpellFavorite` @0x005D43E0's
|
||||
`InsertPos` already implements, now reachable through a live drag. Recorded
|
||||
as AP-172 in the divergence register (the mid-drag visual reflow now happens
|
||||
on release rather than continuously, final positions/wire are retail-exact).
|
||||
Tests: `SpellcastingUiControllerTests.cs` (the real-pointer-path reorder
|
||||
test + a payload-discriminator sabotage check confirming a spell-favorite
|
||||
payload is rejected by a physical `IItemListDragHandler`), `SpellbookTests.cs`
|
||||
(`SetFavorite`/`RemoveFavorite` insert-shift unit coverage). Wire golden
|
||||
bytes for `AddSpellFavorite`/`RemoveSpellFavorite` (opcodes 0x1E3/0x1E4) and
|
||||
`RuntimeCharacterState.TryAddFavorite`/`TryRemoveFavorite` were already
|
||||
covered and needed no change.
|
||||
|
||||
## #353 — Toolbar selected-object text: count field ignores authored HJustify; name field does not wrap to its authored two lines
|
||||
|
||||
**Status:** CLOSED 2026-08-08 — user-passed ("Ok slider bar looks ok!" + the wrap confirmed); the OneLine routing fix (4cfcc8b3) completed it. (RightAligned on the authored HJustify=2 entry; two stacked centered one-line labels wrapping at the authored 140 px via WrapNameTwoLines).
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue