From 5a3d9f15743d6e5caf75e5e5fe96898fe0d30e1c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 1 Jan 2023 19:18:32 +0600 Subject: [PATCH] Use parent clip rect of the adorned visual --- .../Server/ServerCompositionVisual.cs | 19 ++++- .../Controls/AdornerTests.cs | 73 ++++++++++++++++++ tests/Avalonia.RenderTests/TestBase.cs | 9 ++- ...s_Adorner_Is_Properly_Clipped.expected.png | Bin 0 -> 673 bytes ...s_Adorner_Is_Properly_Clipped.expected.png | Bin 0 -> 673 bytes 5 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 tests/Avalonia.RenderTests/Controls/AdornerTests.cs create mode 100644 tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png create mode 100644 tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 0e15cbd54b..0b2df6d2b3 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -42,11 +42,18 @@ namespace Avalonia.Rendering.Composition.Server Root!.RenderedVisuals++; + if (Opacity != 1) + canvas.PushOpacity(Opacity); + if (AdornedVisual != null) + { + canvas.PostTransform = Matrix.Identity; + canvas.Transform = Matrix.Identity; + canvas.PushClip(AdornedVisual._combinedTransformedClipBounds); + } var transform = GlobalTransformMatrix; canvas.PostTransform = MatrixUtils.ToMatrix(transform); canvas.Transform = Matrix.Identity; - if (Opacity != 1) - canvas.PushOpacity(Opacity); + var boundsRect = new Rect(new Size(Size.X, Size.Y)); if (ClipToBounds && !HandlesClipToBounds) canvas.PushClip(Root!.SnapToDevicePixels(boundsRect)); @@ -67,6 +74,8 @@ namespace Avalonia.Rendering.Composition.Server canvas.PopGeometryClip(); if (ClipToBounds && !HandlesClipToBounds) canvas.PopClip(); + if (AdornedVisual != null) + canvas.PopClip(); if(Opacity != 1) canvas.PopOpacity(); } @@ -155,8 +164,12 @@ namespace Avalonia.Rendering.Composition.Server _clipSizeDirty = false; } + + _combinedTransformedClipBounds = + AdornedVisual?._combinedTransformedClipBounds + ?? Parent?._combinedTransformedClipBounds + ?? new Rect(Root!.Size); - _combinedTransformedClipBounds = Parent?._combinedTransformedClipBounds ?? new Rect(Root!.Size); if (_transformedClipBounds != null) _combinedTransformedClipBounds = _combinedTransformedClipBounds.Intersect(_transformedClipBounds.Value); diff --git a/tests/Avalonia.RenderTests/Controls/AdornerTests.cs b/tests/Avalonia.RenderTests/Controls/AdornerTests.cs new file mode 100644 index 0000000000..c833017212 --- /dev/null +++ b/tests/Avalonia.RenderTests/Controls/AdornerTests.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; +using Avalonia.Media; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests; +#else +namespace Avalonia.Direct2D1.RenderTests.Controls; +#endif + +public class AdornerTests : TestBase +{ + public AdornerTests() + : base(@"Controls\Adorner") + { + } + + [Fact] + public async Task Focus_Adorner_Is_Properly_Clipped() + { + Border adorned; + var tree = new Decorator + { + Child = new VisualLayerManager + { + Child = new Border + { + Background = Brushes.Red, + Padding = new Thickness(10, 50, 10,10), + Child = new Border() + { + Background = Brushes.White, + ClipToBounds = true, + Padding = new Thickness(0, -30, 0, 0), + Child = adorned = new Border + { + Background = Brushes.Green, + VerticalAlignment = VerticalAlignment.Top, + Height = 100, + Width = 50 + } + } + } + }, + Width = 200, + Height = 200 + }; + var adorner = new Border + { + BorderThickness = new Thickness(2), + BorderBrush = Brushes.Black + }; + + var size = new Size(tree.Width, tree.Height); + tree.Measure(size); + tree.Arrange(new Rect(size)); + + + adorned.AttachedToVisualTree += delegate + { + AdornerLayer.SetAdornedElement(adorner, adorned); + AdornerLayer.GetAdornerLayer(adorned)!.Children.Add(adorner); + }; + tree.Measure(size); + tree.Arrange(new Rect(size)); + + await RenderToFile(tree); + CompareImages(skipImmediate: true, skipDeferred: true); + } +} \ No newline at end of file diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 8a127897d7..313281d6c6 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -156,7 +156,8 @@ namespace Avalonia.Direct2D1.RenderTests public ILockedFramebuffer Lock() => _bitmap.Lock(); } - protected void CompareImages([CallerMemberName] string testName = "") + protected void CompareImages([CallerMemberName] string testName = "", + bool skipImmediate = false, bool skipDeferred = false, bool skipCompositor = false) { var expectedPath = Path.Combine(OutputPath, testName + ".expected.png"); var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png"); @@ -172,17 +173,17 @@ namespace Avalonia.Direct2D1.RenderTests var deferredError = CompareImages(deferred, expected); var compositedError = CompareImages(composited, expected); - if (immediateError > 0.022) + if (immediateError > 0.022 && !skipImmediate) { Assert.True(false, immediatePath + ": Error = " + immediateError); } - if (deferredError > 0.022) + if (deferredError > 0.022 && !skipDeferred) { Assert.True(false, deferredPath + ": Error = " + deferredError); } - if (compositedError > 0.022) + if (compositedError > 0.022 && !skipCompositor) { Assert.True(false, compositedPath + ": Error = " + compositedError); } diff --git a/tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png b/tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..6a67087d41828128492aa2cb44f7d53f067db4f1 GIT binary patch literal 673 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`0|Qfpr;B4q z#hkZSH+nfc3OHPhJ=y;J{BfllC%-D5QmgWtEE3Jb^Zo3+t!a$&tE_DqY>XQn1UOje zPqf^AI^B4`#j)%g)%O4H^!I(N{eJxXji*uayZ)-!D`Iz4IT|2zRSq{DZ!m$3x^zZ)*9ThlOnjD4(@g_3ckDu}UDb_{>0S+c~;sf9P X|Dv4+8exvWG{@lS>gTe~DWM4f2)3Ck literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png b/tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..6a67087d41828128492aa2cb44f7d53f067db4f1 GIT binary patch literal 673 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`0|Qfpr;B4q z#hkZSH+nfc3OHPhJ=y;J{BfllC%-D5QmgWtEE3Jb^Zo3+t!a$&tE_DqY>XQn1UOje zPqf^AI^B4`#j)%g)%O4H^!I(N{eJxXji*uayZ)-!D`Iz4IT|2zRSq{DZ!m$3x^zZ)*9ThlOnjD4(@g_3ckDu}UDb_{>0S+c~;sf9P X|Dv4+8exvWG{@lS>gTe~DWM4f2)3Ck literal 0 HcmV?d00001