浏览代码

Fix - Search for visual parents when hittesting for pointer events (#18416)

* search for visual parents when hittesting

* Add unit test for hit testing on disabled visual

---------

Co-authored-by: Julien Lebosquain <[email protected]>
Emmanuel Hansen 7 月之前
父节点
当前提交
e189ef9c53
共有 2 个文件被更改,包括 56 次插入1 次删除
  1. 1 1
      src/Avalonia.Controls/TopLevel.cs
  2. 55 0
      tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

+ 1 - 1
src/Avalonia.Controls/TopLevel.cs

@@ -866,7 +866,7 @@ namespace Avalonia.Controls
             var candidate = hitTestElement;
             while (candidate?.IsEffectivelyEnabled == false)
             {
-                candidate = (candidate as Visual)?.Parent as IInputElement;
+                candidate = (candidate as Visual)?.VisualParent as IInputElement;
             }
 
             return candidate;

+ 55 - 0
tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

@@ -6,6 +6,7 @@ using Avalonia.Controls;
 using Avalonia.Headless;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
+using Avalonia.Media;
 using Avalonia.Rendering;
 using Avalonia.UnitTests;
 
@@ -494,6 +495,60 @@ namespace Avalonia.Base.UnitTests.Input
                 result);
         }
 
+        [Fact]
+        public void Disabled_Element_Should_Set_PointerOver_On_Visual_Parent()
+        {
+            using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
+
+            var renderer = new Mock<IHitTester>();
+            var deviceMock = CreatePointerDeviceMock();
+            var impl = CreateTopLevelImplMock();
+
+            var disabledChild = new Border
+            {
+                Background = Brushes.Red,
+                Width = 100,
+                Height = 100,
+                IsEnabled = false
+            };
+
+            var visualParent = new Border
+            {
+                Background = Brushes.Black,
+                Width = 100,
+                Height = 100,
+                Child = disabledChild
+            };
+
+            var logicalParent = new Border
+            {
+                Background = Brushes.Blue,
+                Width = 100,
+                Height = 100
+            };
+
+            // Change the logical parent and check that we're correctly hit testing on the visual tree.
+            // This scenario is made up because it's easy to test.
+            // In the real world, this happens with nested Popups from MenuItems (but that's very cumbersome to test).
+            ((ISetLogicalParent) disabledChild).SetParent(null);
+            ((ISetLogicalParent) disabledChild).SetParent(logicalParent);
+
+            var root = CreateInputRoot(
+                impl.Object,
+                new Panel
+                {
+                    Children = { visualParent }
+                },
+                renderer.Object);
+
+            Assert.False(visualParent.IsPointerOver);
+            SetHit(renderer, disabledChild);
+
+            impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, new Point(50, 50)));
+            Assert.True(visualParent.IsPointerOver);
+            Assert.False(logicalParent.IsPointerOver);
+        }
+
         private static void AddEnteredExitedHandlers(
             EventHandler<PointerEventArgs> handler,
             params IInputElement[] controls)