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:
parent
db03b4bda8
commit
e74efc5c06
3 changed files with 106 additions and 19 deletions
|
|
@ -20,11 +20,18 @@ public class UiRootInputTests
|
|||
public bool Clicked;
|
||||
public int Clicks;
|
||||
public int DoubleClicks;
|
||||
public Action? ClickAction;
|
||||
public ClickRecorder(bool handlesClick) => _handlesClick = handlesClick;
|
||||
public override bool HandlesClick => _handlesClick;
|
||||
public override bool OnEvent(in UiEvent e)
|
||||
{
|
||||
if (e.Type == UiEventType.Click) { Clicked = true; Clicks++; return true; }
|
||||
if (e.Type == UiEventType.Click)
|
||||
{
|
||||
Clicked = true;
|
||||
Clicks++;
|
||||
ClickAction?.Invoke();
|
||||
return true;
|
||||
}
|
||||
if (e.Type == UiEventType.DoubleClick) { DoubleClicks++; return true; }
|
||||
return false;
|
||||
}
|
||||
|
|
@ -66,6 +73,47 @@ public class UiRootInputTests
|
|||
Assert.Equal(1, btn.DoubleClicks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoubleClick_WhenClickHidesCapturedWindow_KeepsOriginalEventTarget()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var window = new UiPanel { Width = 200, Height = 100 };
|
||||
var btn = new ClickRecorder(handlesClick: true) { Width = 120, Height = 20 };
|
||||
window.AddChild(btn);
|
||||
root.AddChild(window);
|
||||
root.RegisterWindow("test", window);
|
||||
|
||||
root.Tick(0, nowMs: 1_000);
|
||||
root.OnMouseDown(UiMouseButton.Left, 10, 10);
|
||||
root.OnMouseUp(UiMouseButton.Left, 10, 10);
|
||||
|
||||
btn.ClickAction = () => root.HideWindow("test");
|
||||
root.Tick(0, nowMs: 1_300);
|
||||
root.OnMouseDown(UiMouseButton.Left, 10, 10);
|
||||
root.OnMouseUp(UiMouseButton.Left, 10, 10);
|
||||
|
||||
Assert.False(window.Visible);
|
||||
Assert.Null(root.Captured);
|
||||
Assert.Equal(2, btn.Clicks);
|
||||
Assert.Equal(1, btn.DoubleClicks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MouseUp_WhenClickTransfersCapture_PreservesNewCaptureOwner()
|
||||
{
|
||||
var root = new UiRoot { Width = 800, Height = 600 };
|
||||
var btn = new ClickRecorder(handlesClick: true) { Width = 120, Height = 20 };
|
||||
var modal = new UiPanel { Left = 200, Width = 100, Height = 100 };
|
||||
root.AddChild(btn);
|
||||
root.AddChild(modal);
|
||||
btn.ClickAction = () => root.SetCapture(modal);
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 10, 10);
|
||||
root.OnMouseUp(UiMouseButton.Left, 10, 10);
|
||||
|
||||
Assert.Same(modal, root.Captured);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItemDragReleasedOutsideUi_raisesOutsideReleaseEvent()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue