Pārlūkot izejas kodu

Use scene geometry for hit-testing.

This means we now correctly hit test circles etc.
Steven Kirk 9 gadi atpakaļ
vecāks
revīzija
86216d9420

+ 20 - 2
samples/RenderTest/MainWindow.xaml.cs

@@ -2,16 +2,19 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Linq;
 using System.Reactive.Linq;
 using Avalonia;
 using Avalonia.Animation;
 using Avalonia.Controls;
 using Avalonia.Controls.Shapes;
 using Avalonia.Data;
+using Avalonia.Input;
 using Avalonia.Layout;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.Rendering;
+using Avalonia.VisualTree;
 
 namespace RenderTest
 {
@@ -37,16 +40,18 @@ namespace RenderTest
 
             for (var i = 0; i < Count; ++i)
             {
+                Ellipse ellipse;
                 var element = new Panel
                 {
                     Children =
                     {
-                        new Ellipse
+                        (ellipse = new Ellipse
                         {
+                            Name = $"ellipse{i}",
                             Width = 100,
                             Height = 100,
                             Fill = Brushes.Blue,
-                        },
+                        }),
                         new Path
                         {
                             Data = StreamGeometry.Parse(
@@ -73,10 +78,23 @@ namespace RenderTest
                     degrees,
                     BindingPriority.Animation);
 
+                ellipse.PointerEnter += Ellipse_PointerEnter;
+                ellipse.PointerLeave += Ellipse_PointerLeave;
+
                 panel.Children.Add(element);
             }
 
             Content = panel;
         }
+
+        private void Ellipse_PointerEnter(object sender, PointerEventArgs e)
+        {
+            ((Ellipse)sender).Fill = Brushes.Red;
+        }
+
+        private void Ellipse_PointerLeave(object sender, PointerEventArgs e)
+        {
+            ((Ellipse)sender).Fill = Brushes.Blue;
+        }
     }
 }

+ 1 - 0
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@@ -115,6 +115,7 @@
     <Compile Include="Rendering\DefaultRenderLoop.cs" />
     <Compile Include="Rendering\SceneGraph\DeferredDrawingContextImpl.cs" />
     <Compile Include="Rendering\SceneGraph\GeometryNode.cs" />
+    <Compile Include="Rendering\SceneGraph\IDrawNode.cs" />
     <Compile Include="Rendering\SceneGraph\ImageNode.cs" />
     <Compile Include="Rendering\SceneGraph\ISceneNode.cs" />
     <Compile Include="Rendering\SceneGraph\IVisualNode.cs" />

+ 43 - 6
src/Avalonia.Visuals/Media/DrawingContext.cs

@@ -75,7 +75,11 @@ namespace Avalonia.Media
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
         public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
-            => PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect);
+        {
+            Contract.Requires<ArgumentNullException>(source != null);
+
+            PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect);
+        }
 
         /// <summary>
         /// Draws a line.
@@ -83,7 +87,13 @@ namespace Avalonia.Media
         /// <param name="pen">The stroke pen.</param>
         /// <param name="p1">The first point of the line.</param>
         /// <param name="p2">The second point of the line.</param>
-        public void DrawLine(Pen pen, Point p1, Point p2) => PlatformImpl.DrawLine(pen, p1, p2);
+        public void DrawLine(Pen pen, Point p1, Point p2)
+        {
+            if (PenIsVisible(pen))
+            {
+                PlatformImpl.DrawLine(pen, p1, p2);
+            }
+        }
 
         /// <summary>
         /// Draws a geometry.
@@ -92,7 +102,12 @@ namespace Avalonia.Media
         /// <param name="pen">The stroke pen.</param>
         /// <param name="geometry">The geometry.</param>
         public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
-            => PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl);
+        {
+            if (brush != null || PenIsVisible(pen))
+            {
+                PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl);
+            }
+        }
 
         /// <summary>
         /// Draws the outline of a rectangle.
@@ -101,7 +116,12 @@ namespace Avalonia.Media
         /// <param name="rect">The rectangle bounds.</param>
         /// <param name="cornerRadius">The corner radius.</param>
         public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0.0f)
-            => PlatformImpl.DrawRectangle(pen, rect, cornerRadius);
+        {
+            if (PenIsVisible(pen))
+            {
+                PlatformImpl.DrawRectangle(pen, rect, cornerRadius);
+            }
+        }
 
         /// <summary>
         /// Draws text.
@@ -110,7 +130,14 @@ namespace Avalonia.Media
         /// <param name="origin">The upper-left corner of the text.</param>
         /// <param name="text">The text.</param>
         public void DrawText(IBrush foreground, Point origin, FormattedText text)
-            => PlatformImpl.DrawText(foreground, origin, text.PlatformImpl);
+        {
+            Contract.Requires<ArgumentNullException>(text != null);
+
+            if (foreground != null)
+            {
+                PlatformImpl.DrawText(foreground, origin, text.PlatformImpl);
+            }
+        }
 
         /// <summary>
         /// Draws a filled rectangle.
@@ -119,7 +146,12 @@ namespace Avalonia.Media
         /// <param name="rect">The rectangle bounds.</param>
         /// <param name="cornerRadius">The corner radius.</param>
         public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f)
-            => PlatformImpl.FillRectangle(brush, rect, cornerRadius);
+        {
+            if (brush != null && rect != Rect.Empty)
+            {
+                PlatformImpl.FillRectangle(brush, rect, cornerRadius);
+            }
+        }
 
         public struct PushedState : IDisposable
         {
@@ -277,5 +309,10 @@ namespace Avalonia.Media
             _transformContainers = null;
             PlatformImpl.Dispose();
         }
+
+        private static bool PenIsVisible(Pen pen)
+        {
+            return pen?.Brush != null && pen.Thickness > 0;
+        }
     }
 }

+ 12 - 1
src/Avalonia.Visuals/Media/Geometry.cs

@@ -68,7 +68,7 @@ namespace Avalonia.Media
         }
 
         /// <summary>
-        /// Indicates whether the geometry contains the specified point.
+        /// Indicates whether the geometry's fill contains the specified point.
         /// </summary>
         /// <param name="point">The point.</param>
         /// <returns><c>true</c> if the geometry contains the point; otherwise, <c>false</c>.</returns>
@@ -76,5 +76,16 @@ namespace Avalonia.Media
         {
             return PlatformImpl.FillContains(point);
         }
+
+        /// <summary>
+        /// Indicates whether the geometry's stroke contains the specified point.
+        /// </summary>
+        /// <param name="pen">The pen to use.</param>
+        /// <param name="point">The point.</param>
+        /// <returns><c>true</c> if the geometry contains the point; otherwise, <c>false</c>.</returns>
+        public bool StrokeContains(Pen pen, Point point)
+        {
+            return PlatformImpl.StrokeContains(pen, point);
+        }
     }
 }

+ 11 - 1
src/Avalonia.Visuals/Platform/IGeometryImpl.cs

@@ -1,6 +1,8 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using Avalonia.Media;
+
 namespace Avalonia.Platform
 {
     /// <summary>
@@ -26,10 +28,18 @@ namespace Avalonia.Platform
         Rect GetRenderBounds(double strokeThickness);
 
         /// <summary>
-        /// Indicates whether the geometry contains the specified point.
+        /// Indicates whether the geometry's fill contains the specified point.
         /// </summary>
         /// <param name="point">The point.</param>
         /// <returns><c>true</c> if the geometry contains the point; otherwise, <c>false</c>.</returns>
         bool FillContains(Point point);
+
+        /// <summary>
+        /// Indicates whether the geometry's stroke contains the specified point.
+        /// </summary>
+        /// <param name="pen">The stroke to use.</param>
+        /// <param name="point">The point.</param>
+        /// <returns><c>true</c> if the geometry contains the point; otherwise, <c>false</c>.</returns>
+        bool StrokeContains(Pen pen, Point point);
     }
 }

+ 3 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@@ -7,7 +7,7 @@ using Avalonia.Platform;
 
 namespace Avalonia.Rendering.SceneGraph
 {
-    public class GeometryNode : ISceneNode
+    public class GeometryNode : IDrawNode
     {
         public GeometryNode(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry)
         {
@@ -38,7 +38,8 @@ namespace Avalonia.Rendering.SceneGraph
 
         public bool HitTest(Point p)
         {
-            throw new NotImplementedException();
+            p *= Transform.Invert();
+            return Geometry.FillContains(p) || Geometry.StrokeContains(Pen, p);
         }
     }
 }

+ 12 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/IDrawNode.cs

@@ -0,0 +1,12 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Avalonia.Rendering.SceneGraph
+{
+    public interface IDrawNode : ISceneNode
+    {
+        bool HitTest(Point p);
+    }
+}

+ 0 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/ISceneNode.cs

@@ -8,8 +8,6 @@ namespace Avalonia.Rendering.SceneGraph
 {
     public interface ISceneNode
     {
-        bool HitTest(Point p);
-
         void Render(IDrawingContextImpl context);
     }
 }

+ 2 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs

@@ -13,5 +13,7 @@ namespace Avalonia.Rendering.SceneGraph
         Rect ClipBounds { get; set; }
         bool ClipToBounds { get; set; }
         IReadOnlyList<ISceneNode> Children { get; }
+
+        bool HitTest(Point p);
     }
 }

+ 2 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@@ -7,7 +7,7 @@ using Avalonia.Platform;
 
 namespace Avalonia.Rendering.SceneGraph
 {
-    public class ImageNode : ISceneNode
+    public class ImageNode : IDrawNode
     {
         public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
         {
@@ -41,7 +41,7 @@ namespace Avalonia.Rendering.SceneGraph
 
         public bool HitTest(Point p)
         {
-            throw new NotImplementedException();
+            return (DestRect * Transform).Contains(p);
         }
     }
 }

+ 3 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs

@@ -6,7 +6,7 @@ using Avalonia.Media;
 
 namespace Avalonia.Rendering.SceneGraph
 {
-    public class LineNode : ISceneNode
+    public class LineNode : IDrawNode
     {
         public LineNode(Matrix transform, Pen pen, Point p1, Point p2)
         {
@@ -34,7 +34,8 @@ namespace Avalonia.Rendering.SceneGraph
 
         public bool HitTest(Point p)
         {
-            throw new NotImplementedException();
+            // TODO: Implement line hit testing.
+            return false;
         }
     }
 }

+ 2 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@@ -6,7 +6,7 @@ using Avalonia.Media;
 
 namespace Avalonia.Rendering.SceneGraph
 {
-    public class RectangleNode : ISceneNode
+    public class RectangleNode : IDrawNode
     {
         public RectangleNode(Matrix transform, IBrush brush, Pen pen, Rect rect, float cornerRadius)
         {
@@ -49,7 +49,7 @@ namespace Avalonia.Rendering.SceneGraph
 
         public bool HitTest(Point p)
         {
-            throw new NotImplementedException();
+            return (Rect * Transform).Contains(p);
         }
     }
 }

+ 2 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@@ -99,6 +99,8 @@ namespace Avalonia.Rendering.SceneGraph
                         }
                     }
 
+                    dynamic d = node.Visual;
+
                     if (node.HitTest(p))
                     {
                         yield return node.Visual;

+ 2 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs

@@ -7,7 +7,7 @@ using Avalonia.Platform;
 
 namespace Avalonia.Rendering.SceneGraph
 {
-    public class TextNode : ISceneNode
+    public class TextNode : IDrawNode
     {
         public TextNode(Matrix transform, IBrush foreground, Point origin, IFormattedTextImpl text)
         {
@@ -38,7 +38,7 @@ namespace Avalonia.Rendering.SceneGraph
 
         public bool HitTest(Point p)
         {
-            throw new NotImplementedException();
+            return (new Rect(Origin, Text.Measure()) * Transform).Contains(p);
         }
     }
 }

+ 12 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@@ -34,7 +34,17 @@ namespace Avalonia.Rendering.SceneGraph
 
         public bool HitTest(Point p)
         {
-            return ClipBounds.Contains(p);
+            foreach (var child in Children)
+            {
+                var drawNode = child as IDrawNode;
+
+                if (drawNode?.HitTest(p) == true)
+                {
+                    return true;
+                }
+            }
+
+            return false;
         }
 
         public void Render(IDrawingContextImpl context)
@@ -48,7 +58,7 @@ namespace Avalonia.Rendering.SceneGraph
 
             if (ClipToBounds)
             {
-                context.PushClip(ClipBounds);
+                context.PushClip(ClipBounds * Transform.Invert());
             }
 
             foreach (var child in Children)

+ 9 - 0
src/Gtk/Avalonia.Cairo/Media/StreamGeometryContextImpl.cs

@@ -71,6 +71,15 @@ namespace Avalonia.Cairo.Media
             }
         }
 
+        internal bool StrokeContains(Pen pen, Point point)
+        {
+            using (var context = new Cairo.Context(new Cairo.ImageSurface(Cairo.Format.Argb32, 0, 0)))
+            {
+                context.AppendPath(Path);
+                return context.InStroke(point.X, point.Y);
+            }
+        }
+
         public void LineTo(Point point)
         {
             if (this.Path == null)

+ 5 - 0
src/Gtk/Avalonia.Cairo/Media/StreamGeometryImpl.cs

@@ -72,5 +72,10 @@ namespace Avalonia.Cairo.Media
         {
             return _impl.FillContains(point);
         }
+
+        public bool StrokeContains(Pen pen, Point point)
+        {
+            return _impl.StrokeContains(pen, point);
+        }
     }
 }

+ 7 - 0
src/Skia/Avalonia.Skia/StreamGeometryImpl.cs

@@ -84,6 +84,13 @@ namespace Avalonia.Skia
             return GetRenderBounds(0).Contains(point);
         }
 
+        public bool StrokeContains(Pen pen, Point point)
+        {
+            // TODO: Not supported by SkiaSharp yet, so use expanded Rect
+            // return EffectivePath.Contains(point.X, point.Y);
+            return GetRenderBounds(0).Contains(point);
+        }
+
         class StreamContext : IStreamGeometryContextImpl
         {
             private readonly StreamGeometryImpl _geometryImpl;

+ 4 - 1
src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs

@@ -85,11 +85,14 @@ namespace Avalonia.Direct2D1.Media
             }
         }
 
-
         public bool FillContains(Point point)
         {
             return Geometry.FillContainsPoint(point.ToSharpDX());
         }
 
+        public bool StrokeContains(Avalonia.Media.Pen pen, Point point)
+        {
+            return Geometry.StrokeContainsPoint(point.ToSharpDX(), (float)pen.Thickness);
+        }
     }
 }

+ 16 - 0
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@@ -22,6 +22,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
                 {
                     Child = border = new Border
                     {
+                        Width = 100,
+                        Height = 100,
                         Background = Brushes.Red,
                         Child = textBlock = new TextBlock
                         {
@@ -30,6 +32,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
                     }
                 };
 
+                tree.Measure(Size.Infinity);
+                tree.Arrange(new Rect(tree.DesiredSize));
+
                 var initial = new Scene(tree);
                 var result = SceneBuilder.Update(initial);
 
@@ -129,14 +134,20 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
                 {
                     Child = border = new Border
                     {
+                        Width = 100,
+                        Height = 100,
                         Background = Brushes.Red,
                         Child = textBlock = new TextBlock
                         {
+                            Foreground = Brushes.Green,
                             Text = "Hello World",
                         }
                     }
                 };
 
+                tree.Measure(Size.Infinity);
+                tree.Arrange(new Rect(tree.DesiredSize));
+
                 var initial = SceneBuilder.Update(new Scene(tree));
                 var initialBackgroundNode = initial.FindNode(border).Children[0];
                 var initialTextNode = initial.FindNode(textBlock).Children[0];
@@ -172,6 +183,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
                 TextBlock textBlock;
                 var tree = new TestRoot
                 {
+                    Width = 100,
+                    Height = 100,
                     Child = border = new Border
                     {
                         Background = Brushes.Red,
@@ -182,6 +195,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
                     }
                 };
 
+                tree.Measure(Size.Infinity);
+                tree.Arrange(new Rect(tree.DesiredSize));
+
                 var initial = SceneBuilder.Update(new Scene(tree));
 
                 border.Child = null;

+ 25 - 7
tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

@@ -48,13 +48,8 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
         class MockStreamGeometry : IStreamGeometryImpl
         {
             private MockStreamGeometryContext _impl = new MockStreamGeometryContext();
-            public Rect Bounds
-            {
-                get
-                {
-                    throw new NotImplementedException();
-                }
-            }
+
+            public Rect Bounds => _impl.CalculateBounds();
 
             public Matrix Transform
             {
@@ -79,6 +74,11 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                 return _impl.FillContains(point);
             }
 
+            public bool StrokeContains(Pen pen, Point point)
+            {
+                return false;
+            }
+
             public Rect GetRenderBounds(double strokeThickness)
             {
                 throw new NotImplementedException();
@@ -102,6 +102,24 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     points.Add(startPoint);
                 }
 
+                public Rect CalculateBounds()
+                {
+                    var left = double.MaxValue;
+                    var right = double.MinValue;
+                    var top = double.MaxValue;
+                    var bottom = double.MinValue;
+                    
+                    foreach (var p in points)
+                    {
+                        left = Math.Min(p.X, left);
+                        right = Math.Max(p.X, right);
+                        top = Math.Min(p.Y, top);
+                        bottom = Math.Max(p.Y, bottom);
+                    }
+
+                    return new Rect(new Point(left, top), new Point(right, bottom));
+                }
+
                 public void CubicBezierTo(Point point1, Point point2, Point point3)
                 {
                     throw new NotImplementedException();

+ 93 - 7
tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs

@@ -12,6 +12,7 @@ using Avalonia.VisualTree;
 using Moq;
 using Xunit;
 using System;
+using Avalonia.Controls.Shapes;
 
 namespace Avalonia.Visuals.UnitTests.VisualTree
 {
@@ -30,6 +31,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     {
                         Width = 100,
                         Height = 100,
+                        Background = Brushes.Red,
                         HorizontalAlignment = HorizontalAlignment.Center,
                         VerticalAlignment = VerticalAlignment.Center
                     }
@@ -43,7 +45,37 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
 
                 var result = container.GetVisualsAt(new Point(100, 100));
 
-                Assert.Equal(new[] { container.Child, container }, result);
+                Assert.Equal(new[] { container.Child }, result);
+            }
+        }
+
+        [Fact]
+        public void GetVisualsAt_Should_Not_Find_Empty_Controls_At_Point()
+        {
+            using (TestApplication())
+            {
+                var container = new TestRoot
+                {
+                    Width = 200,
+                    Height = 200,                    
+                    Child = new Border
+                    {
+                        Width = 100,
+                        Height = 100,
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        VerticalAlignment = VerticalAlignment.Center
+                    }
+                };
+
+                container.Measure(Size.Infinity);
+                container.Arrange(new Rect(container.DesiredSize));
+
+                var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
+                context.Render(container);
+
+                var result = container.GetVisualsAt(new Point(100, 100));
+
+                Assert.Empty(result);
             }
         }
 
@@ -52,6 +84,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
         {
             using (TestApplication())
             {
+                Border visible;
                 var container = new TestRoot
                 {
                     Width = 200,
@@ -60,11 +93,13 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     {
                         Width = 100,
                         Height = 100,
+                        Background = Brushes.Red,
                         HorizontalAlignment = HorizontalAlignment.Center,
                         VerticalAlignment = VerticalAlignment.Center,
                         IsVisible = false,
-                        Child = new Border
+                        Child = visible = new Border
                         {
+                            Background = Brushes.Red,
                             HorizontalAlignment = HorizontalAlignment.Stretch,
                             VerticalAlignment = VerticalAlignment.Stretch,
                         }
@@ -79,7 +114,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
 
                 var result = container.GetVisualsAt(new Point(100, 100));
 
-                Assert.Equal(new[] { container }, result);
+                Assert.Empty(result);
             }
         }
 
@@ -96,6 +131,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     {
                         Width = 100,
                         Height = 100,
+                        Background = Brushes.Red,
                         HorizontalAlignment = HorizontalAlignment.Center,
                         VerticalAlignment = VerticalAlignment.Center
                     }
@@ -109,7 +145,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
 
                 var result = container.GetVisualsAt(new Point(10, 10));
 
-                Assert.Equal(new[] { container }, result);
+                Assert.Empty(result);
             }
         }
 
@@ -131,6 +167,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                             {
                                 Width = 100,
                                 Height = 100,
+                                Background = Brushes.Red,
                                 HorizontalAlignment = HorizontalAlignment.Center,
                                 VerticalAlignment = VerticalAlignment.Center
                             },
@@ -138,6 +175,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                             {
                                 Width = 50,
                                 Height = 50,
+                                Background = Brushes.Red,
                                 HorizontalAlignment = HorizontalAlignment.Center,
                                 VerticalAlignment = VerticalAlignment.Center
                             }
@@ -153,7 +191,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
 
                 var result = container.GetVisualsAt(new Point(100, 100));
 
-                Assert.Equal(new[] { container.Children[1], container.Children[0], container }, result);
+                Assert.Equal(new[] { container.Children[1], container.Children[0] }, result);
             }
         }
 
@@ -176,6 +214,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                                 Width = 100,
                                 Height = 100,
                                 ZIndex = 1,
+                                Background = Brushes.Red,
                                 HorizontalAlignment = HorizontalAlignment.Center,
                                 VerticalAlignment = VerticalAlignment.Center
                             },
@@ -183,6 +222,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                             {
                                 Width = 50,
                                 Height = 50,
+                                Background = Brushes.Red,
                                 HorizontalAlignment = HorizontalAlignment.Center,
                                 VerticalAlignment = VerticalAlignment.Center
                             },
@@ -191,6 +231,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                                 Width = 75,
                                 Height = 75,
                                 ZIndex = 2,
+                                Background = Brushes.Red,
                                 HorizontalAlignment = HorizontalAlignment.Center,
                                 VerticalAlignment = VerticalAlignment.Center
                             }
@@ -206,7 +247,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
 
                 var result = container.GetVisualsAt(new Point(100, 100));
 
-                Assert.Equal(new[] { container.Children[2], container.Children[0], container.Children[1], container }, result);
+                Assert.Equal(new[] { container.Children[2], container.Children[0], container.Children[1] }, result);
             }
         }
 
@@ -223,6 +264,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     {
                         Width = 200,
                         Height = 200,
+                        Background = Brushes.Red,
                         ClipToBounds = false,
                         Children = new Controls.Controls
                         {
@@ -231,12 +273,14 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                                 Width = 100,
                                 Height = 100,
                                 ZIndex = 1,
+                                Background = Brushes.Red,
                                 HorizontalAlignment = HorizontalAlignment.Left,
                                 VerticalAlignment = VerticalAlignment.Top,
                                 Child = target = new Border
                                 {
                                     Width = 50,
                                     Height = 50,
+                                    Background = Brushes.Red,
                                     HorizontalAlignment = HorizontalAlignment.Left,
                                     VerticalAlignment = VerticalAlignment.Top,
                                     RenderTransform = new TranslateTransform(110, 110),
@@ -271,12 +315,14 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     {
                         Width = 100,
                         Height = 200,
+                        Background = Brushes.Red,
                         Children = new Controls.Controls
                         {
                             new Panel()
                             {
                                 Width = 100,
                                 Height = 100,
+                                Background = Brushes.Red,
                                 Margin = new Thickness(0, 100, 0, 0),
                                 ClipToBounds = true,
                                 Children = new Controls.Controls
@@ -285,6 +331,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                                     {
                                         Width = 100,
                                         Height = 100,
+                                        Background = Brushes.Red,
                                         Margin = new Thickness(0, -100, 0, 0)
                                     })
                                 }
@@ -321,17 +368,20 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                     {
                         Width = 100,
                         Height = 200,
+                        Background = Brushes.Red,
                         Children = new Controls.Controls
                         {
                             (target = new Border()
                             {
                                 Width = 100,
-                                Height = 100
+                                Height = 100,
+                                Background = Brushes.Red,
                             }),
                             new Border()
                             {
                                 Width = 100,
                                 Height = 100,
+                                Background = Brushes.Red,
                                 Margin = new Thickness(0, 100, 0, 0),
                                 Child = scroll = new ScrollContentPresenter()
                                 {
@@ -343,11 +393,13 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
                                             {
                                                 Width = 100,
                                                 Height = 100,
+                                                Background = Brushes.Red,
                                             }),
                                             (item2 = new Border()
                                             {
                                                 Width = 100,
                                                 Height = 100,
+                                                Background = Brushes.Red,
                                             }),
                                         }
                                     }
@@ -393,6 +445,40 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
             }
         }
 
+
+        [Fact]
+        public void GetVisualsAt_Should_Not_Find_Path_When_Outside_Fill()
+        {
+            using (TestApplication())
+            {
+                Path path;
+                var container = new TestRoot
+                {
+                    Width = 200,
+                    Height = 200,
+                    Child = path = new Path
+                    {
+                        Width = 200,
+                        Height = 200,
+                        Fill = Brushes.Red,
+                        Data = StreamGeometry.Parse("M100,0 L0,100 100,100")
+                    }
+                };
+
+                container.Measure(Size.Infinity);
+                container.Arrange(new Rect(container.DesiredSize));
+
+                var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
+                context.Render(container);
+
+                var result = container.GetVisualsAt(new Point(100, 100));
+                Assert.Equal(new[] { path }, result);
+
+                result = container.GetVisualsAt(new Point(10, 10));
+                Assert.Empty(result);
+            }
+        }
+
         private IDisposable TestApplication()
         {
             return UnitTestApplication.Start(