Skip to content

Commit 3c7c469

Browse files
authored
Raise pointer events on captured element (#18420)
1 parent e189ef9 commit 3c7c469

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

src/Avalonia.Base/Input/PointerOverPreProcessor.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ public void OnNext(RawInputEventArgs value)
7979
else if (pointerDevice.TryGetPointer(args) is { } pointer &&
8080
pointer.Type != PointerType.Touch)
8181
{
82-
var element = pointer.Captured ?? args.InputHitTestResult.firstEnabledAncestor;
82+
var element = GetEffectivePointerOverElement(
83+
args.InputHitTestResult.firstEnabledAncestor,
84+
pointer.Captured);
8385

8486
SetPointerOver(pointer, args.Root, element, args.Timestamp, args.Position,
8587
new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()),
@@ -96,7 +98,10 @@ public void SceneInvalidated(Rect dirtyRect)
9698

9799
if (dirtyRect.Contains(clientPoint))
98100
{
99-
var element = pointer.Captured ?? _inputRoot.InputHitTest(clientPoint);
101+
var element = GetEffectivePointerOverElement(
102+
_inputRoot.InputHitTest(clientPoint),
103+
pointer.Captured);
104+
100105
SetPointerOver(pointer, _inputRoot, element, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
101106
}
102107
else if (!((Visual)_inputRoot).Bounds.Contains(clientPoint))
@@ -106,6 +111,11 @@ public void SceneInvalidated(Rect dirtyRect)
106111
}
107112
}
108113

114+
private static IInputElement? GetEffectivePointerOverElement(IInputElement? hitTestElement, IInputElement? captured)
115+
=> captured is not null && hitTestElement != captured ?
116+
null :
117+
hitTestElement;
118+
109119
private void ClearPointerOver()
110120
{
111121
if (_currentPointer is (var pointer, var position))

tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public void TouchMove_Should_Not_Set_IsPointerOver()
120120
}
121121

122122
[Fact]
123-
public void HitTest_Should_Be_Ignored_If_Element_Captured()
123+
public void HitTest_Should_Ignore_Non_Captured_Elements()
124124
{
125125
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
126126

@@ -145,8 +145,19 @@ public void HitTest_Should_Be_Ignored_If_Element_Captured()
145145
}
146146
}, renderer.Object);
147147

148-
SetHit(renderer, canvas);
149148
pointer.SetupGet(p => p.Captured).Returns(decorator);
149+
150+
// Move the pointer over the canvas: the captured decorator should lose the pointer over state.
151+
SetHit(renderer, canvas);
152+
impl.Object.Input!(CreateRawPointerMovedArgs(device, root));
153+
154+
Assert.False(decorator.IsPointerOver);
155+
Assert.False(border.IsPointerOver);
156+
Assert.False(canvas.IsPointerOver);
157+
Assert.False(root.IsPointerOver);
158+
159+
// Move back the pointer over the decorator: raise events normally for it since it's captured.
160+
SetHit(renderer, decorator);
150161
impl.Object.Input!(CreateRawPointerMovedArgs(device, root));
151162

152163
Assert.True(decorator.IsPointerOver);

0 commit comments

Comments
 (0)