fix #210: stabilize mouse-up capture ownership

Pin each mouse-up to its original captured target before callbacks can hide a window or transfer capture. Classify double-clicks before dispatch, preserve replacement capture owners, and cover both re-entrant paths so jump/combat visibility changes cannot crash the UI root.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 10:27:27 +02:00
parent db03b4bda8
commit e74efc5c06
3 changed files with 106 additions and 19 deletions

View file

@ -46,6 +46,38 @@ Copy this block when adding a new issue:
---
## #210 — Mouse-up crashes when a UI callback changes pointer capture
**Status:** IN-PROGRESS — implementation complete 2026-07-13, pending live gate
**Severity:** HIGH
**Component:** retained UI / input lifecycle
**Description:** Jumping while changing combat mode could terminate the client
with a `NullReferenceException` in `UiRoot.OnMouseUp`. The crash was exposed by
the new jump/combat visibility transitions, but affected any click callback that
hid its captured window or transferred capture.
**Root cause:** `OnMouseUp` repeatedly dereferenced the mutable global
`Captured` property while dispatching MouseUp, Click, and DoubleClick. Those
callbacks may synchronously clear or replace capture, so one physical release
could change target halfway through its own event transaction. The unconditional
final `ReleaseCapture` could also discard a replacement capture.
**Resolution:** Mouse-up now snapshots the original mouse-down target and
double-click classification before dispatching callbacks, uses that target for
the complete event transaction, and releases capture only if the original target
still owns it. Regression tests cover hiding the captured window during the
second click and transferring capture to a newly opened element.
**Files:** `src/AcDream.App/UI/UiRoot.cs`,
`tests/AcDream.App.Tests/UI/UiRootInputTests.cs`
**Acceptance:** Hold/release Jump while repeatedly toggling combat and peace,
including rapid double-clicks. The client remains running, the combat bar follows
combat mode, and the jump bar still fills and hides normally.
---
## #209 — Retail jump power bar missing
**Status:** IN-PROGRESS — implementation complete 2026-07-13, pending live visual gate