fix(ui): D.5.3/B.1 — dragging a toolbar item moved the window instead of the item
Found at visual verification: an occupied UiItemSlot sits inside the Draggable toolbar frame (UiNineSlicePanel.Draggable=true), so UiRoot.OnMouseDown's FindWindow returned the frame and the window-move branch won — press+drag on a slot moved the whole bar instead of picking up the item. The slot wasn't CapturesPointerDrag (that path is for self-driven text-selection and suppresses the BeginDrag promotion), and UiRoot had no path for "a drag-source inside a draggable window." Fix: add UiElement.IsDragSource (virtual, default false); UiItemSlot overrides it to `ItemId != 0` (occupancy-gated). UiRoot.OnMouseDown now prioritizes IsDragSource over window-move — an OCCUPIED slot starts the item drag (promotes to BeginDrag on >3px), an EMPTY slot falls through to the IA-12 whole-window-drag so the bar stays movable by its empty cells / chrome. UiRoot stays item-agnostic (reads only the bool). This REDUCES divergence (occupied cells now drag like retail) within IA-12's umbrella — no new register row. Regression tests reproduce the LIVE topology (slot inside a Draggable frame); the earlier RootWithBoundSlot tests put the slot directly under the root, so they could not catch it. Full suite 493 pass / 0 fail / 2 skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1e53820dd7
commit
acdefc2e21
4 changed files with 69 additions and 0 deletions
|
|
@ -203,4 +203,46 @@ public class DragDropSpineTests
|
|||
list.Cell.OnEvent(new UiEvent(0u, list.Cell, UiEventType.DragEnter, Payload: SomePayload()));
|
||||
Assert.Equal(UiItemSlot.DragAcceptState.Reject, list.Cell.DragAcceptVisual);
|
||||
}
|
||||
|
||||
// ── item drag inside a Draggable window (the LIVE toolbar topology) ──────
|
||||
// Regression (visual gate 2026-06-20): the slot sits inside the Draggable toolbar
|
||||
// frame, so FindWindow returns the frame. An OCCUPIED slot must start an ITEM drag
|
||||
// (IsDragSource), NOT move the window; an EMPTY slot falls through to whole-window
|
||||
// drag (IA-12) so the bar stays movable by its empty cells / chrome. The earlier
|
||||
// RootWithBoundSlot tests put the slot directly under the root (no draggable
|
||||
// ancestor), so they could not catch this.
|
||||
private static (UiRoot root, UiPanel frame, UiItemList list) DraggableFrameWithSlot(uint itemId)
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var frame = new UiPanel { Left = 10, Top = 300, Width = 200, Height = 60, Draggable = true };
|
||||
var list = new UiItemList(_ => (1u, 1, 1)) { Left = 5, Top = 5, Width = 32, Height = 32 };
|
||||
list.Cell.Width = 32; list.Cell.Height = 32;
|
||||
if (itemId != 0) list.Cell.SetItem(itemId, 0x99u);
|
||||
frame.AddChild(list);
|
||||
root.AddChild(frame);
|
||||
return (root, frame, list);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OccupiedSlotInsideDraggableWindow_armsItemDrag_doesNotMoveWindow()
|
||||
{
|
||||
var (root, frame, list) = DraggableFrameWithSlot(0x5001u);
|
||||
// Slot screen rect = frame(10,300)+list(5,5) → (15,305)..(47,337). Press inside, drag >3px.
|
||||
root.OnMouseDown(UiMouseButton.Left, 20, 310);
|
||||
root.OnMouseMove(40, 310);
|
||||
Assert.Same(list.Cell, root.DragSource); // item drag armed
|
||||
Assert.Equal(10f, frame.Left); // window did NOT move
|
||||
Assert.Equal(300f, frame.Top);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptySlotInsideDraggableWindow_movesWindow_notItemDrag()
|
||||
{
|
||||
var (root, frame, _) = DraggableFrameWithSlot(0u); // empty slot → not a drag source
|
||||
root.OnMouseDown(UiMouseButton.Left, 20, 310);
|
||||
root.OnMouseMove(40, 310);
|
||||
Assert.Null(root.DragSource); // no item drag
|
||||
Assert.Equal(30f, frame.Left); // window moved (offX=20-10=10; new Left=40-10=30)
|
||||
Assert.Equal(300f, frame.Top); // y unchanged (310-10=300)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue