acdream/src/AcDream.App/UI/UiEvent.cs
Erik 67b0815c79 fix(ui): OP5 re-check residuals R1/R2 (coordinator pass) — OP5 CLOSED
R1: UiRoot now delivers WM_CAPTURECHANGED (0x215 — retail's own Win32
event-id space) to the element losing pointer capture on BOTH release
and re-target; UiScrollbar terminates a mid-drag gesture there,
completing it (one DragCompleted flush persisting the user's last-seen
value) and unlatching IsDragging — a panel-close keybind mid-drag or a
second-button re-target can no longer latch the drag flag forever and
silently suppress every later settings flush. Normal MouseUp paths
no-op (the latch is already clear when capture releases).

R2: the scalar latch arms BEFORE the track-click jump applies, so the
jump's own ScalarChanged tick defers its flush to the MouseUp's single
DragCompleted — one flush per press gesture, never the
inline-then-completed double; the DragCompleted doc now states the real
contract (fires once per value-capable gesture incl. capture loss)
instead of the refuted never-on-jump claim.

Tests: capture-loss mid-drag (ends + completes once + stray-MouseUp
no-double), no-drag capture-change no-op, bare-track-click
single-completion with the latch observed armed during the jump tick.
Also reconciles the research doc's U4 row to its closure (the six
caption pairs, the BN zero-fold post-mortem) per the OP6 rework's flag.

Full Release suite: 13,128 passed / 4 skipped / 0 failed (one
documented #250-class allocation flake on first run, green in
isolation and on full-suite rerun).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 08:13:24 +02:00

97 lines
3.9 KiB
C#

using System.Numerics;
namespace AcDream.App.UI;
/// <summary>
/// Per-event payload delivered to <see cref="UiElement.OnEvent"/>.
/// Mirrors the retail AC client's 24-byte event struct that is passed to
/// every widget's vtable slot +0x128 (<c>OnEvent(int* event)</c>).
///
/// Layout from decompiled <c>chunk_004A0000.c</c> paperdoll handler
/// <c>FUN_004A5FA0</c>:
/// <code>
/// int source_id; // param_2[0] — e.g. 0x100001d6 (drag source)
/// void* target_widget; // param_2[1]
/// int event_type; // param_2[2] — see UiEventType
/// int data0; // param_2[3]
/// int data1; // param_2[4] — typically x in local coords
/// int data2; // param_2[5] — typically y
/// int data3; // param_2[6]
/// </code>
/// </summary>
public readonly record struct UiEvent(
uint SourceId,
UiElement? Target,
int Type, // see <see cref="UiEventType"/>
int Data0 = 0,
int Data1 = 0,
int Data2 = 0,
int Data3 = 0,
object? Payload = null);
/// <summary>
/// Retail AC UI event-type constants. Each value matches the decompiled
/// switch-case in widgets' OnEvent handlers (e.g. 0x01 click, 0x15 drag
/// begin, 0x3E drop released). Win32 WM_* numbers are reused for raw
/// button/key/mouse events (0x200 = WM_MOUSEMOVE etc.) — this matches
/// retail where internal event codes collide deliberately with WM_*.
///
/// Evidence from decompile:
/// - 0x01 click — chunk_00470000.c ~11140, chunk_004C0000.c ~9270
/// - 0x05/0x06 hover — chunk_00460000.c ~6253
/// - 0x07 tooltip — chunk_00460000.c ~6253 (the UI manager polls the
/// hovered element's tooltip deadline before global time)
/// - 0x0A scroll — chunk_00470000.c ~11210
/// - 0x0E right-click— chunk_004A0000.c ~2674
/// - 0x15 drag begin — chunk_004A0000.c ~2707
/// - 0x1C drag-over — chunk_004A0000.c ~2723
/// - 0x21 drag-enter — chunk_004A0000.c ~2714
/// - 0x3E drop-released — chunk_004A0000.c ~2754
/// </summary>
public static class UiEventType
{
public const int Click = 0x01;
public const int HoverEnter = 0x05;
public const int HoverLeave = 0x06;
public const int Tooltip = 0x07;
public const int DoubleClick = 0x08;
public const int Scroll = 0x0A;
public const int RightClick = 0x0E;
public const int DragBegin = 0x15;
public const int DragOver = 0x1C;
public const int DragEnter = 0x21;
public const int FocusLost = 0x28;
public const int FocusGained = 0x29;
public const int DropReleased = 0x3E;
// Raw Win32-style event numbers (retail uses WM_* verbatim for internal dispatch).
public const int MouseMove = 0x200;
public const int MouseDown = 0x201; // left button down
public const int MouseUp = 0x202; // left button up
/// <summary>WM_CAPTURECHANGED (0x215) — delivered by <see cref="UiRoot"/>
/// to the element LOSING pointer capture when capture is released or
/// re-targeted WITHOUT a MouseUp reaching that element (panel hidden by a
/// keybind mid-drag; a second button press re-targeting capture). A widget
/// holding gesture state keyed to capture (the scrollbar's drag latch)
/// must terminate the gesture here (OP5 re-check R1, 2026-08-11).</summary>
public const int CaptureChanged = 0x215;
public const int DoubleClickLeft = 0x203;
public const int RightDown = 0x204;
public const int RightUp = 0x205;
public const int MiddleDown = 0x207;
public const int MiddleUp = 0x208;
public const int KeyDown = 0x100;
public const int KeyUp = 0x101;
public const int Char = 0x102;
}
/// <summary>
/// Mouse button enum matching retail's 1/2/3 encoding.
/// </summary>
public enum UiMouseButton
{
Left = 1,
Right = 2,
Middle = 3,
}