diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 35079f5cb3..c1879e44be 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -20,7 +20,7 @@ namespace Avalonia.Rendering.SceneGraph using (var impl = new DeferredDrawingContextImpl()) using (var context = new DrawingContext(impl)) { - Update(context, scene, (VisualNode)scene.Root, true); + Update(context, scene, (VisualNode)scene.Root, scene.Root.Visual.Bounds, true); } } @@ -58,12 +58,15 @@ namespace Avalonia.Rendering.SceneGraph using (var impl = new DeferredDrawingContextImpl(dirty)) using (var context = new DrawingContext(impl)) { + var clip = scene.Root.Visual.Bounds; + if (node.Parent != null) { context.PushPostTransform(node.Parent.Transform); + clip = node.Parent.ClipBounds; } - Update(context, scene, (VisualNode)node, recurse); + Update(context, scene, (VisualNode)node, clip, recurse); } return true; @@ -120,7 +123,7 @@ namespace Avalonia.Rendering.SceneGraph return (VisualNode)node; } - private static void Update(DrawingContext context, Scene scene, VisualNode node, bool forceRecurse) + private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse) { var visual = node.Visual; var opacity = visual.Opacity; @@ -153,12 +156,17 @@ namespace Avalonia.Rendering.SceneGraph node.Transform != contextImpl.Transform; node.Transform = contextImpl.Transform; - node.ClipBounds = bounds * node.Transform; + node.ClipBounds = (bounds * node.Transform).Intersect(clip); node.ClipToBounds = clipToBounds; node.GeometryClip = visual.Clip; node.Opacity = opacity; node.OpacityMask = visual.OpacityMask; + if (node.ClipToBounds) + { + clip = clip.Intersect(node.ClipBounds); + } + visual.Render(context); if (forceRecurse) @@ -166,7 +174,7 @@ namespace Avalonia.Rendering.SceneGraph foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) { var childNode = scene.FindNode(child) ?? CreateNode(scene, child, node); - Update(context, scene, (VisualNode)childNode, forceRecurse); + Update(context, scene, (VisualNode)childNode, clip, forceRecurse); } node.SubTreeUpdated = true; diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index e8c1e5e302..80997ecb5a 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -98,6 +98,55 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph } } + [Fact] + public void ClipBounds_Should_Be_Intersection_With_Parent_ClipBounds() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + Border border; + var tree = new TestRoot + { + Width = 200, + Height = 300, + Child = new Canvas + { + ClipToBounds = true, + Width = 100, + Height = 100, + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Top, + Children = + { + (border = new Border + { + Background = Brushes.AliceBlue, + Width = 100, + Height = 100, + [Canvas.LeftProperty] = 50, + [Canvas.TopProperty] = 50, + }) + } + } + }; + + tree.Measure(Size.Infinity); + tree.Arrange(new Rect(tree.DesiredSize)); + + var scene = new Scene(tree); + SceneBuilder.UpdateAll(scene); + + var borderNode = scene.FindNode(border); + Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds); + + // Initial ClipBounds are correct, make sure they're still correct after updating border. + scene = scene.Clone(); + Assert.True(SceneBuilder.Update(scene, border)); + + borderNode = scene.FindNode(border); + Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds); + } + } + [Fact] public void Should_Respect_ZIndex() { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs index abe8d108c8..704cbf72d1 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs @@ -171,8 +171,8 @@ namespace Avalonia.Visuals.UnitTests.VisualTree } }; - container.Measure(Size.Infinity); - container.Arrange(new Rect(container.DesiredSize)); + root.Measure(Size.Infinity); + root.Arrange(new Rect(container.DesiredSize)); var result = container.GetVisualsAt(new Point(100, 100)); @@ -224,8 +224,8 @@ namespace Avalonia.Visuals.UnitTests.VisualTree } }; - container.Measure(Size.Infinity); - container.Arrange(new Rect(container.DesiredSize)); + root.Measure(Size.Infinity); + root.Arrange(new Rect(container.DesiredSize)); var result = container.GetVisualsAt(new Point(100, 100)); @@ -319,8 +319,8 @@ namespace Avalonia.Visuals.UnitTests.VisualTree } }; - container.Measure(Size.Infinity); - container.Arrange(new Rect(container.DesiredSize)); + root.Measure(Size.Infinity); + root.Arrange(new Rect(container.DesiredSize)); var result = container.GetVisualsAt(new Point(50, 50)); @@ -391,8 +391,8 @@ namespace Avalonia.Visuals.UnitTests.VisualTree scroll.UpdateChild(); - container.Measure(Size.Infinity); - container.Arrange(new Rect(container.DesiredSize)); + root.Measure(Size.Infinity); + root.Arrange(new Rect(container.DesiredSize)); var result = container.GetVisualsAt(new Point(50, 150)).First();