From e7e2516c07f46cd17e4d2299b4f7e78cd92d9267 Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Tue, 26 Jul 2022 13:43:39 +0200 Subject: [PATCH 1/6] Scene hittest now uses exclusive `Rect.Contains`. Fixes https://github.com/AvaloniaUI/Avalonia/issues/3079 --- src/Avalonia.Base/Rendering/SceneGraph/Scene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Base/Rendering/SceneGraph/Scene.cs index 20c23d7bee..c436299018 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/Scene.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/Scene.cs @@ -300,7 +300,7 @@ namespace Avalonia.Rendering.SceneGraph if (node.ClipToBounds) { clip = clip == null ? node.ClipBounds : clip.Value.Intersect(node.ClipBounds); - clipped = !clip.Value.Contains(_point); + clipped = !clip.Value.ContainsExclusive(_point); } if (node.GeometryClip != null) From 01d040bf6ca6043fb42fcc6cecb04ea176df3819 Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Wed, 27 Jul 2022 23:25:43 +0200 Subject: [PATCH 2/6] + hit test attempt --- .../DeferredRendererTests_HitTesting.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs index 33c7b3e0f8..e1b36c9d72 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs @@ -537,6 +537,37 @@ namespace Avalonia.Base.UnitTests.Rendering } } + [Fact] + public void HitTest_Should_Not_Hit_Controls_Next_Pixel() + { + using (TestApplication()) + { + Button targetButton; + + var root = new TestRoot + { + Width = 300, + Height = 200, + Child = new StackPanel + { + Orientation = Orientation.Vertical, + Children = + { + new Button { Width = 10, Height = 10 }, + { targetButton = new Button { Width = 10, Height = 10 } } + } + } + }; + + root.Renderer = new DeferredRenderer(root, null); + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + var result = root.Renderer.HitTest(new Point(5, 10), root, null); + Assert.Equal(new[] { targetButton }, result); + } + } + private IDisposable TestApplication() { return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); From d560fd28473df11cfe0824419cbf32bfb6f3ee88 Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Wed, 27 Jul 2022 23:28:34 +0200 Subject: [PATCH 3/6] + CompositorHitTesting test --- .../Rendering/CompositorHitTestingTests.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs index 705c8fad9c..6543649eb2 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs @@ -408,10 +408,33 @@ public class CompositorHitTestingTests : CompositorTestsBase s.AssertHitTest(175, 100, null); } } + + [Fact] + public void HitTest_Should_Not_Hit_Controls_Next_Pixel() + { + using (var s = new CompositorServices(new Size(200, 200))) + { + Button targetButton; + + var stackPanel = new StackPanel + { + Orientation = Orientation.Vertical, + Children = + { + new Button { Width = 10, Height = 10 }, + { targetButton = new Button { Width = 10, Height = 10 } } + } + }; + + s.TopLevel.Content = stackPanel; + + s.AssertHitTest(new Point(5, 10), null, targetButton); + } + } private IDisposable TestApplication() { return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); } -} \ No newline at end of file +} From c226432e9b6e3392e93c38d1708ed6dc61cc291b Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Thu, 28 Jul 2022 14:40:38 +0200 Subject: [PATCH 4/6] Tests fixing --- .../Rendering/CompositorHitTestingTests.cs | 9 +++++---- .../Rendering/DeferredRendererTests_HitTesting.cs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs index 6543649eb2..a584a859ec 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs @@ -414,21 +414,22 @@ public class CompositorHitTestingTests : CompositorTestsBase { using (var s = new CompositorServices(new Size(200, 200))) { - Button targetButton; + Rectangle targetRectangle; var stackPanel = new StackPanel { Orientation = Orientation.Vertical, + HorizontalAlignment = HorizontalAlignment.Left, Children = { - new Button { Width = 10, Height = 10 }, - { targetButton = new Button { Width = 10, Height = 10 } } + new Rectangle { Width = 10, Height = 10, Fill= Brushes.Red}, + { targetRectangle = new Rectangle { Width = 10, Height = 10, Fill = Brushes.Green} } } }; s.TopLevel.Content = stackPanel; - s.AssertHitTest(new Point(5, 10), null, targetButton); + s.AssertHitTest(new Point(5, 10), null, targetRectangle); } } diff --git a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs index e1b36c9d72..7800ddfc8a 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs @@ -542,7 +542,7 @@ namespace Avalonia.Base.UnitTests.Rendering { using (TestApplication()) { - Button targetButton; + Rectangle targetRectangle; var root = new TestRoot { @@ -551,10 +551,11 @@ namespace Avalonia.Base.UnitTests.Rendering Child = new StackPanel { Orientation = Orientation.Vertical, + HorizontalAlignment = HorizontalAlignment.Left, Children = { - new Button { Width = 10, Height = 10 }, - { targetButton = new Button { Width = 10, Height = 10 } } + new Rectangle { Width = 10, Height = 10, Fill = Brushes.Red}, + { targetRectangle = new Rectangle { Width = 10, Height = 10, Fill = Brushes.Green} } } } }; @@ -564,7 +565,7 @@ namespace Avalonia.Base.UnitTests.Rendering root.Arrange(new Rect(root.DesiredSize)); var result = root.Renderer.HitTest(new Point(5, 10), root, null); - Assert.Equal(new[] { targetButton }, result); + Assert.Equal(new[] { targetRectangle }, result); } } From 1c1499c39961786f5b5046fc2fbd4c641f570566 Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Thu, 28 Jul 2022 19:00:19 +0200 Subject: [PATCH 5/6] More `ContainsExclusive` --- .../Rendering/SceneGraph/RectangleNode.cs | 4 ++-- .../Rendering/CompositorHitTestingTests.cs | 6 +++--- .../Rendering/DeferredRendererTests_HitTesting.cs | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs index 7b79c446f9..a9d1bf96e5 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs @@ -103,13 +103,13 @@ namespace Avalonia.Rendering.SceneGraph if (Brush != null) { var rect = Rect.Rect.Inflate((Pen?.Thickness / 2) ?? 0); - return rect.Contains(p); + return rect.ContainsExclusive(p); } else { var borderRect = Rect.Rect.Inflate((Pen?.Thickness / 2) ?? 0); var emptyRect = Rect.Rect.Deflate((Pen?.Thickness / 2) ?? 0); - return borderRect.Contains(p) && !emptyRect.Contains(p); + return borderRect.ContainsExclusive(p) && !emptyRect.ContainsExclusive(p); } } diff --git a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs index a584a859ec..02012bf62b 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CompositorHitTestingTests.cs @@ -414,7 +414,7 @@ public class CompositorHitTestingTests : CompositorTestsBase { using (var s = new CompositorServices(new Size(200, 200))) { - Rectangle targetRectangle; + Border targetRectangle; var stackPanel = new StackPanel { @@ -422,8 +422,8 @@ public class CompositorHitTestingTests : CompositorTestsBase HorizontalAlignment = HorizontalAlignment.Left, Children = { - new Rectangle { Width = 10, Height = 10, Fill= Brushes.Red}, - { targetRectangle = new Rectangle { Width = 10, Height = 10, Fill = Brushes.Green} } + new Border { Width = 10, Height = 10, Background= Brushes.Red}, + { targetRectangle = new Border { Width = 10, Height = 10, Background = Brushes.Green} } } }; diff --git a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs index 7800ddfc8a..d3fdbb63db 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs @@ -542,11 +542,11 @@ namespace Avalonia.Base.UnitTests.Rendering { using (TestApplication()) { - Rectangle targetRectangle; + Border targetRectangle; var root = new TestRoot { - Width = 300, + Width = 50, Height = 200, Child = new StackPanel { @@ -554,8 +554,8 @@ namespace Avalonia.Base.UnitTests.Rendering HorizontalAlignment = HorizontalAlignment.Left, Children = { - new Rectangle { Width = 10, Height = 10, Fill = Brushes.Red}, - { targetRectangle = new Rectangle { Width = 10, Height = 10, Fill = Brushes.Green} } + new Border { Width = 50, Height = 50, Background = Brushes.Red}, + { targetRectangle = new Border { Width = 50, Height = 50, Background = Brushes.Green} } } } }; @@ -564,7 +564,7 @@ namespace Avalonia.Base.UnitTests.Rendering root.Measure(Size.Infinity); root.Arrange(new Rect(root.DesiredSize)); - var result = root.Renderer.HitTest(new Point(5, 10), root, null); + var result = root.Renderer.HitTest(new Point(25, 50), root, null); Assert.Equal(new[] { targetRectangle }, result); } } From 2a5760921761d819264d190a381a2314b09a0516 Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Sat, 30 Jul 2022 17:16:16 +0200 Subject: [PATCH 6/6] + more `ContainsExclusive` for nodes --- .../Rendering/SceneGraph/ExperimentalAcrylicNode.cs | 2 +- src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs | 2 +- src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs index 59ebcf5109..12b67105e9 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs @@ -83,7 +83,7 @@ namespace Avalonia.Rendering.SceneGraph if (Material != null) { var rect = Rect.Rect; - return rect.Contains(p); + return rect.ContainsExclusive(p); } } diff --git a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs index 9199611ed6..1f58111ecf 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs @@ -73,6 +73,6 @@ namespace Avalonia.Rendering.SceneGraph } /// - public override bool HitTest(Point p) => Bounds.Contains(p); + public override bool HitTest(Point p) => Bounds.ContainsExclusive(p); } } diff --git a/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs index 23267166a5..339881e675 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs @@ -109,7 +109,7 @@ namespace Avalonia.Rendering.SceneGraph } /// - public override bool HitTest(Point p) => Bounds.Contains(p); + public override bool HitTest(Point p) => Bounds.ContainsExclusive(p); public override void Dispose() {