Browse Source

Use scene geometry for hit-testing.

This means we now correctly hit test circles etc.
scenegraph-after-breakage
Steven Kirk 10 years ago
parent
commit
86216d9420
  1. 22
      samples/RenderTest/MainWindow.xaml.cs
  2. 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  3. 49
      src/Avalonia.Visuals/Media/DrawingContext.cs
  4. 13
      src/Avalonia.Visuals/Media/Geometry.cs
  5. 12
      src/Avalonia.Visuals/Platform/IGeometryImpl.cs
  6. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  7. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/IDrawNode.cs
  8. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/ISceneNode.cs
  9. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
  10. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  11. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
  12. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  13. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  14. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
  15. 14
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  16. 9
      src/Gtk/Avalonia.Cairo/Media/StreamGeometryContextImpl.cs
  17. 5
      src/Gtk/Avalonia.Cairo/Media/StreamGeometryImpl.cs
  18. 7
      src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
  19. 5
      src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs
  20. 16
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  21. 32
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs
  22. 100
      tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs

22
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
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" />

49
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;
}
}
}

13
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);
}
}
}

12
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);
}
}

5
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
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);
}
}

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
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);
}
}

4
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);
}
}
}

5
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;
}
}
}

4
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
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;

4
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);
}
}
}

14
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
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
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
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;

5
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
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;

32
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();

100
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(

Loading…
Cancel
Save