123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- // 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.Controls;
- using Avalonia.Controls.Presenters;
- using Avalonia.Layout;
- using Avalonia.Media;
- using Avalonia.Platform;
- using Avalonia.Rendering;
- using Avalonia.UnitTests;
- using Moq;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using Xunit;
- namespace Avalonia.Input.UnitTests
- {
- public class InputElement_HitTesting
- {
- [Fact]
- public void InputHitTest_Should_Find_Control_At_Point()
- {
- using (var application = UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- var container = new Decorator
- {
- 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.InputHitTest(new Point(100, 100));
- Assert.Equal(container.Child, result);
- }
- }
- [Fact]
- public void InputHitTest_Should_Not_Find_Control_Outside_Point()
- {
- using (UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- var container = new Decorator
- {
- 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.InputHitTest(new Point(10, 10));
- Assert.Equal(container, result);
- }
- }
- [Fact]
- public void InputHitTest_Should_Find_Top_Control_At_Point()
- {
- using (UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- var container = new Panel
- {
- Width = 200,
- Height = 200,
- Children = new Controls.Controls
- {
- new Border
- {
- Width = 100,
- Height = 100,
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center
- },
- new Border
- {
- Width = 50,
- Height = 50,
- 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.InputHitTest(new Point(100, 100));
- Assert.Equal(container.Children[1], result);
- }
- }
- [Fact]
- public void InputHitTest_Should_Find_Top_Control_At_Point_With_ZOrder()
- {
- using (UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- var container = new Panel
- {
- Width = 200,
- Height = 200,
- Children = new Controls.Controls
- {
- new Border
- {
- Width = 100,
- Height = 100,
- ZIndex = 1,
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center
- },
- new Border
- {
- Width = 50,
- Height = 50,
- 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.InputHitTest(new Point(100, 100));
- Assert.Equal(container.Children[0], result);
- }
- }
- [Fact]
- public void InputHitTest_Should_Find_Control_Translated_Outside_Parent_Bounds()
- {
- using (UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- Border target;
- var container = new Panel
- {
- Width = 200,
- Height = 200,
- ClipToBounds = false,
- Children = new Controls.Controls
- {
- new Border
- {
- Width = 100,
- Height = 100,
- ZIndex = 1,
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Top,
- Child = target = new Border
- {
- Width = 50,
- Height = 50,
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Top,
- RenderTransform = new TranslateTransform(110, 110),
- }
- },
- }
- };
- container.Measure(Size.Infinity);
- container.Arrange(new Rect(container.DesiredSize));
- var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
- context.Render(container);
- var result = container.InputHitTest(new Point(120, 120));
- Assert.Equal(target, result);
- }
- }
- [Fact]
- public void InputHitTest_Should_Not_Find_Control_Outside_Parent_Bounds_When_Clipped()
- {
- using (UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- Border target;
- var container = new Panel
- {
- Width = 100,
- Height = 200,
- Children = new Controls.Controls
- {
- new Panel()
- {
- Width = 100,
- Height = 100,
- Margin = new Thickness(0, 100, 0, 0),
- ClipToBounds = true,
- Children = new Controls.Controls
- {
- (target = new Border()
- {
- Width = 100,
- Height = 100,
- Margin = new Thickness(0, -100, 0, 0)
- })
- }
- }
- }
- };
- container.Measure(Size.Infinity);
- container.Arrange(new Rect(container.DesiredSize));
- var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
- context.Render(container);
- var result = container.InputHitTest(new Point(50, 50));
- Assert.NotEqual(target, result);
- Assert.Equal(container, result);
- }
- }
- [Fact]
- public void InputHitTest_Should_Not_Find_Control_Outside_Scroll_ViewPort()
- {
- using (UnitTestApplication.Start(new TestServices(renderInterface: new MockRenderInterface())))
- {
- Border target;
- Border item1;
- Border item2;
- ScrollContentPresenter scroll;
- var container = new Panel
- {
- Width = 100,
- Height = 200,
- Children = new Controls.Controls
- {
- (target = new Border()
- {
- Width = 100,
- Height = 100
- }),
- new Border()
- {
- Width = 100,
- Height = 100,
- Margin = new Thickness(0, 100, 0, 0),
- Child = scroll = new ScrollContentPresenter()
- {
- Content = new StackPanel()
- {
- Children = new Controls.Controls
- {
- (item1 = new Border()
- {
- Width = 100,
- Height = 100,
- }),
- (item2 = new Border()
- {
- Width = 100,
- Height = 100,
- }),
- }
- }
- }
- }
- }
- };
- scroll.UpdateChild();
- container.Measure(Size.Infinity);
- container.Arrange(new Rect(container.DesiredSize));
- var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
- context.Render(container);
- var result = container.InputHitTest(new Point(50, 150));
- Assert.Equal(item1, result);
- result = container.InputHitTest(new Point(50, 50));
- Assert.Equal(target, result);
- scroll.Offset = new Vector(0, 100);
- //we don't have setup LayoutManager so we will make it manually
- scroll.Parent.InvalidateArrange();
- container.InvalidateArrange();
- container.Arrange(new Rect(container.DesiredSize));
- context.Render(container);
- result = container.InputHitTest(new Point(50, 150));
- Assert.Equal(item2, result);
- result = container.InputHitTest(new Point(50, 50));
- Assert.NotEqual(item1, result);
- Assert.Equal(target, result);
- }
- }
- class MockRenderInterface : IPlatformRenderInterface
- {
- public IFormattedTextImpl CreateFormattedText(string text, Typeface typeface, TextAlignment textAlignment, TextWrapping wrapping, Size constraint, IReadOnlyList<FormattedTextStyleSpan> spans)
- {
- throw new NotImplementedException();
- }
- public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
- {
- throw new NotImplementedException();
- }
- public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)
- {
- throw new NotImplementedException();
- }
- public IStreamGeometryImpl CreateStreamGeometry()
- {
- return new MockStreamGeometry();
- }
- public IBitmapImpl LoadBitmap(Stream stream)
- {
- throw new NotImplementedException();
- }
- public IBitmapImpl LoadBitmap(string fileName)
- {
- throw new NotImplementedException();
- }
- public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride)
- {
- throw new NotImplementedException();
- }
- public IWritableBitmapImpl CreateWritableBitmap(int width, int height, PixelFormat? fmt)
- {
- throw new NotImplementedException();
- }
- class MockStreamGeometry : Avalonia.Platform.IStreamGeometryImpl
- {
- private MockStreamGeometryContext _impl = new MockStreamGeometryContext();
- public Rect Bounds
- {
- get
- {
- throw new NotImplementedException();
- }
- }
- public Matrix Transform
- {
- get
- {
- throw new NotImplementedException();
- }
- set
- {
- throw new NotImplementedException();
- }
- }
- public IStreamGeometryImpl Clone()
- {
- return this;
- }
- public bool FillContains(Point point)
- {
- return _impl.FillContains(point);
- }
- public Rect GetRenderBounds(double strokeThickness)
- {
- throw new NotImplementedException();
- }
- public IGeometryImpl Intersect(IGeometryImpl geometry)
- {
- throw new NotImplementedException();
- }
- public IStreamGeometryContextImpl Open()
- {
- return _impl;
- }
- public bool StrokeContains(Pen pen, Point point)
- {
- throw new NotImplementedException();
- }
- public IGeometryImpl WithTransform(Matrix transform)
- {
- throw new NotImplementedException();
- }
- class MockStreamGeometryContext : IStreamGeometryContextImpl
- {
- private List<Point> points = new List<Point>();
- public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
- {
- throw new NotImplementedException();
- }
- public void BeginFigure(Point startPoint, bool isFilled)
- {
- points.Add(startPoint);
- }
- public void CubicBezierTo(Point point1, Point point2, Point point3)
- {
- throw new NotImplementedException();
- }
- public void Dispose()
- {
- }
- public void EndFigure(bool isClosed)
- {
- }
- public void LineTo(Point point)
- {
- points.Add(point);
- }
- public void QuadraticBezierTo(Point control, Point endPoint)
- {
- throw new NotImplementedException();
- }
- public void SetFillRule(FillRule fillRule)
- {
- }
- public bool FillContains(Point point)
- {
- // Use the algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html
- // to determine if the point is in the geometry (since it will always be convex in this situation)
- for (int i = 0; i < points.Count; i++)
- {
- var a = points[i];
- var b = points[(i + 1) % points.Count];
- var c = points[(i + 2) % points.Count];
- Vector v0 = c - a;
- Vector v1 = b - a;
- Vector v2 = point - a;
- var dot00 = v0 * v0;
- var dot01 = v0 * v1;
- var dot02 = v0 * v2;
- var dot11 = v1 * v1;
- var dot12 = v1 * v2;
- var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
- var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
- var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
- if ((u >= 0) && (v >= 0) && (u + v < 1)) return true;
- }
- return false;
- }
- }
- }
- }
- }
- }
|