Przeglądaj źródła

Fix bounds clipping.

Steven Kirk 9 lat temu
rodzic
commit
6b6474bc8d

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

+ 49 - 0
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()
         {

+ 8 - 8
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();