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
|
|
@ -113,6 +113,14 @@ public abstract class UiElement
|
|||
/// drag-drop candidacy is suppressed in favour of the element's own handling.</summary>
|
||||
public bool CapturesPointerDrag { get; set; }
|
||||
|
||||
/// <summary>If true, a left-press-and-move on this element starts a DRAG-DROP
|
||||
/// (<see cref="UiRoot"/> promotes to BeginDrag) rather than moving a Draggable
|
||||
/// ancestor window — so an item cell inside the toolbar frame drags the item, not
|
||||
/// the window. Distinct from <see cref="CapturesPointerDrag"/> (a self-driven
|
||||
/// interior drag like text selection, which does NOT promote to BeginDrag). Default
|
||||
/// false; overridden by drag sources (e.g. an occupied <see cref="UiItemSlot"/>).</summary>
|
||||
public virtual bool IsDragSource => false;
|
||||
|
||||
/// <summary>Minimum size enforced while resizing.</summary>
|
||||
public float MinWidth { get; set; } = 40f;
|
||||
public float MinHeight { get; set; } = 40f;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,14 @@ public sealed class UiItemSlot : UiElement
|
|||
public override (uint tex, int w, int h)? GetDragGhost()
|
||||
=> ItemId != 0 && IconTexture != 0 ? (IconTexture, (int)Width, (int)Height) : null;
|
||||
|
||||
/// <summary>An OCCUPIED slot is a drag source — a press-and-move picks up the item
|
||||
/// rather than moving the toolbar window. An EMPTY slot is NOT a drag source, so a
|
||||
/// press-and-move there falls through to the IA-12 whole-window-drag, keeping the bar
|
||||
/// movable by its empty cells / chrome. Drives <see cref="UiRoot"/>'s mousedown
|
||||
/// window-vs-item disambiguation (retail moves the window via a dragbar, never cells;
|
||||
/// our whole-window-drag approximation reconciles by gating on occupancy).</summary>
|
||||
public override bool IsDragSource => ItemId != 0;
|
||||
|
||||
/// <summary>Walk up to the containing <see cref="UiItemList"/> (the drop handler owner).</summary>
|
||||
private UiItemList? FindList()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -260,6 +260,17 @@ public sealed class UiRoot : UiElement
|
|||
_resizeMouseX = x; _resizeMouseY = y;
|
||||
_dragCandidate = false;
|
||||
}
|
||||
else if (target.IsDragSource)
|
||||
{
|
||||
// A drag SOURCE (e.g. an occupied item cell) inside a Draggable window
|
||||
// starts an item drag-drop, NOT a window move. UiRoot stays item-agnostic:
|
||||
// it only reads the IsDragSource flag (the cell decides occupancy). The
|
||||
// BeginDrag promotion happens on the >3px move (and cancels if the source's
|
||||
// GetDragPayload() returns null). Empty cells are NOT drag sources, so they
|
||||
// fall through to window.Draggable below (IA-12 whole-window-drag), keeping
|
||||
// the bar movable by its empty cells / chrome.
|
||||
_dragCandidate = true;
|
||||
}
|
||||
else if (target.CapturesPointerDrag)
|
||||
{
|
||||
// The pressed widget owns interior drags (e.g. text selection):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue