fix(D.2b): Slice 2 — buttons inside a whole-window-Draggable frame get their Click

Visual gate 2 (user): the "Slots" toggle caption was visible but unclickable.

Root cause (UiRoot.OnMouseDown/OnMouseUp): a left-press on a non-drag-source
widget inside a whole-window-Draggable frame (the inventory window's IA-12
drag) set _windowDragTarget; OnMouseUp then early-returned before emitting the
Click. So the paperdoll Slots button (the first plain button inside the
draggable inventory frame) never received its click. Chat/toolbar buttons
escape this — their frames aren't whole-window-draggable.

Fix (toolkit, root cause not band-aid): add UiElement.HandlesClick (a virtual
opt-out parallel to IsDragSource); UiButton overrides it true; OnMouseDown
routes a HandlesClick press to the widget (like CapturesPointerDrag) instead of
the window-drag, so OnMouseUp emits the Click. 2 regression tests lock it
(HandlesClick widget in a Draggable frame emits Click; a plain one doesn't).

Build + full App suite green (596, +2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-23 10:08:59 +02:00
parent fe319bd2aa
commit 8ee3d89feb
4 changed files with 67 additions and 5 deletions

View file

@ -121,6 +121,15 @@ public abstract class UiElement
/// false; overridden by drag sources (e.g. an occupied <see cref="UiItemSlot"/>).</summary>
public virtual bool IsDragSource => false;
/// <summary>If true, a left-press on this element is handled BY the element (it receives the Click
/// on release) instead of being captured as a whole-window move on a Draggable ancestor. Set by
/// interactive leaf widgets (e.g. <see cref="UiButton"/>) so they stay clickable inside a
/// whole-window-Draggable frame like the inventory window — where, without this, the IA-12
/// whole-window-drag swallows the press and the Click is never emitted. Distinct from
/// <see cref="IsDragSource"/> (starts a drag-drop) and <see cref="CapturesPointerDrag"/> (a
/// self-driven interior drag such as text selection). Default false.</summary>
public virtual bool HandlesClick => false;
/// <summary>Minimum size enforced while resizing.</summary>
public float MinWidth { get; set; } = 40f;
public float MinHeight { get; set; } = 40f;