diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
index c4f0ebcd3b..e33dc999dc 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/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml
index 9abcf5d32b..28f8649e2d 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml
@@ -50,7 +50,8 @@
Opacity="0"
Fill="{DynamicResource CheckBoxCheckGlyphForegroundUnchecked}"
Stretch="Uniform"
- VerticalAlignment="Center" />
+ VerticalAlignment="Center"
+ FlowDirection="LeftToRight" />
@@ -146,7 +147,6 @@
-
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 0000000000..6a67087d41
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png differ
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 0000000000..6a67087d41
Binary files /dev/null and b/tests/TestFiles/Skia/Controls/Adorner/Focus_Adorner_Is_Properly_Clipped.expected.png differ