소스 검색

Raise pointer events on captured element (#18420)

Julien Lebosquain 7 달 전
부모
커밋
3c7c469018
2개의 변경된 파일25개의 추가작업 그리고 4개의 파일을 삭제
  1. 12 2
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  2. 13 2
      tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

+ 12 - 2
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@@ -79,7 +79,9 @@ namespace Avalonia.Input
                 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()),
@@ -96,7 +98,10 @@ namespace Avalonia.Input
 
                 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))
@@ -106,6 +111,11 @@ namespace Avalonia.Input
             }
         }
 
+        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))

+ 13 - 2
tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

@@ -120,7 +120,7 @@ namespace Avalonia.Base.UnitTests.Input
         }
 
         [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()));
 
@@ -145,8 +145,19 @@ namespace Avalonia.Base.UnitTests.Input
                 }
             }, 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);