Skip to content

Raise pointer events on captured element #18420

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/Avalonia.Base/Input/PointerOverPreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ public void OnNext(RawInputEventArgs value)
else if (pointerDevice.TryGetPointer(args) is { } pointer &&
pointer.Type != PointerType.Touch)
{
var element = pointer.Captured ?? args.InputHitTestResult.firstEnabledAncestor;
var element = GetEffectivePointerOverElement(
args.InputHitTestResult.firstEnabledAncestor,
pointer.Captured);

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

if (dirtyRect.Contains(clientPoint))
{
var element = pointer.Captured ?? _inputRoot.InputHitTest(clientPoint);
var element = GetEffectivePointerOverElement(
_inputRoot.InputHitTest(clientPoint),
pointer.Captured);

SetPointerOver(pointer, _inputRoot, element, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
else if (!((Visual)_inputRoot).Bounds.Contains(clientPoint))
Expand All @@ -106,6 +111,11 @@ public void SceneInvalidated(Rect dirtyRect)
}
}

private static IInputElement? GetEffectivePointerOverElement(IInputElement? hitTestElement, IInputElement? captured)
=> captured is not null && hitTestElement != captured ?
null :
hitTestElement;

private void ClearPointerOver()
{
if (_currentPointer is (var pointer, var position))
Expand Down
15 changes: 13 additions & 2 deletions tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public void TouchMove_Should_Not_Set_IsPointerOver()
}

[Fact]
public void HitTest_Should_Be_Ignored_If_Element_Captured()
public void HitTest_Should_Ignore_Non_Captured_Elements()
{
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));

Expand All @@ -144,8 +144,19 @@ public void HitTest_Should_Be_Ignored_If_Element_Captured()
}
}, renderer.Object);

SetHit(renderer, canvas);
pointer.SetupGet(p => p.Captured).Returns(decorator);

// Move the pointer over the canvas: the captured decorator should lose the pointer over state.
SetHit(renderer, canvas);
impl.Object.Input!(CreateRawPointerMovedArgs(device, root));

Assert.False(decorator.IsPointerOver);
Assert.False(border.IsPointerOver);
Assert.False(canvas.IsPointerOver);
Assert.False(root.IsPointerOver);

// Move back the pointer over the decorator: raise events normally for it since it's captured.
SetHit(renderer, decorator);
impl.Object.Input!(CreateRawPointerMovedArgs(device, root));

Assert.True(decorator.IsPointerOver);
Expand Down
Loading