diff --git a/src/Controls/samples/Controls.Sample/Pages/Core/BorderGalleries/BorderClipPlayground.xaml b/src/Controls/samples/Controls.Sample/Pages/Core/BorderGalleries/BorderClipPlayground.xaml index f5ea28b6079b..1fa74b102257 100644 --- a/src/Controls/samples/Controls.Sample/Pages/Core/BorderGalleries/BorderClipPlayground.xaml +++ b/src/Controls/samples/Controls.Sample/Pages/Core/BorderGalleries/BorderClipPlayground.xaml @@ -17,13 +17,14 @@ - + - + diff --git a/src/Controls/src/Core/Border/Border.cs b/src/Controls/src/Core/Border/Border.cs index 1411fe4c4636..9086bd938089 100644 --- a/src/Controls/src/Core/Border/Border.cs +++ b/src/Controls/src/Core/Border/Border.cs @@ -314,7 +314,7 @@ void OnStrokeDashArrayChanged(object? sender, NotifyCollectionChangedEventArgs e void UpdateStrokeShape() { - if (StrokeShape is Shape strokeShape) + if (StrokeShape is Shape strokeShape && StrokeThickness == 0) { strokeShape.StrokeThickness = StrokeThickness; } diff --git a/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.Windows.cs new file mode 100644 index 000000000000..f1abf3a396f9 --- /dev/null +++ b/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.Windows.cs @@ -0,0 +1,58 @@ +using Xunit; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Graphics; +using Microsoft.Maui.Controls.Shapes; +using Microsoft.Maui.Handlers; +using Microsoft.UI.Xaml.Hosting; +using Microsoft.Maui.Platform; +using System.Threading.Tasks; +using Microsoft.UI.Composition; + +namespace Microsoft.Maui.DeviceTests +{ + public partial class BorderTests : ControlsHandlerTestBase + { + [Theory(DisplayName = "Inner CornerRadius Initializes Correctly")] + [InlineData(0)] + [InlineData(12)] + [InlineData(24)] + public async Task InnerCornerRadiusInitializesCorrectly(int cornerRadius) + { + SetupBuilder(); + + var expected = Colors.Red; + + var border = new Border() + { + Content = new Label { Text = "Background", TextColor = Colors.White }, + StrokeShape = new RoundRectangle { CornerRadius = cornerRadius }, + Background = new SolidPaint(expected), + StrokeThickness = 0, + HeightRequest = 100, + WidthRequest = 300 + }; + + await AttachAndRun(border, (handler) => + { + var contentPanel = GetNativeBorder(handler as BorderHandler); + var content = contentPanel.Content; + var visual = ElementCompositionPreview.GetElementVisual(content); + + var clip = visual.Clip as CompositionGeometricClip; + Assert.NotNull(clip); + + var geometry = clip.Geometry as CompositionPathGeometry; + var path = geometry.Path; + Assert.NotNull(path); + + Assert.True(contentPanel.IsInnerPath); + }); + + await AssertColorAtPoint(border, expected, typeof(BorderHandler), cornerRadius, cornerRadius); + } + + ContentPanel GetNativeBorder(BorderHandler borderHandler) => + borderHandler.PlatformView; + + } +} \ No newline at end of file diff --git a/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.cs b/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.cs index cc4d64ef9dd9..30c67a736c8c 100644 --- a/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/Border/BorderTests.cs @@ -18,8 +18,9 @@ void SetupBuilder() { builder.ConfigureMauiHandlers(handlers => { - handlers.AddHandler(); handlers.AddHandler(); + handlers.AddHandler(); + handlers.AddHandler(); }); }); } diff --git a/src/Core/src/Platform/Windows/ContentPanel.cs b/src/Core/src/Platform/Windows/ContentPanel.cs index 2a80de485958..3692d4c0d063 100644 --- a/src/Core/src/Platform/Windows/ContentPanel.cs +++ b/src/Core/src/Platform/Windows/ContentPanel.cs @@ -15,7 +15,7 @@ namespace Microsoft.Maui.Platform public class ContentPanel : MauiPanel { readonly Path? _borderPath; - IShape? _borderShape; + IBorderStroke? _borderStroke; FrameworkElement? _content; internal Path? BorderPath => _borderPath; @@ -30,6 +30,8 @@ internal FrameworkElement? Content } } + internal bool IsInnerPath { get; private set; } + protected override global::Windows.Foundation.Size ArrangeOverride(global::Windows.Foundation.Size finalSize) { var actual = base.ArrangeOverride(finalSize); @@ -52,9 +54,15 @@ void ContentPanelSizeChanged(object sender, UI.Xaml.SizeChangedEventArgs e) if (_borderPath == null) return; - _borderPath.UpdatePath(_borderShape, ActualWidth, ActualHeight); + var width = e.NewSize.Width; + var height = e.NewSize.Height; + + if (width <= 0 || height <= 0) + return; + + _borderPath.UpdatePath(_borderStroke?.Shape, width, height); UpdateContent(); - UpdateClip(_borderShape); + UpdateClip(_borderStroke?.Shape, width, height); } internal void EnsureBorderPath() @@ -73,16 +81,40 @@ public void UpdateBackground(Paint? background) _borderPath.UpdateBackground(background); } + [Obsolete("Use Microsoft.Maui.Platform.UpdateBorderStroke instead")] public void UpdateBorderShape(IShape borderShape) { - _borderShape = borderShape; + UpdateBorder(borderShape); + } - if (_borderPath == null) + internal void UpdateBorderStroke(IBorderStroke borderStroke) + { + if (borderStroke is null) + return; + + _borderStroke = borderStroke; + + if (_borderStroke is null) + return; + + UpdateBorder(_borderStroke.Shape); + } + + void UpdateBorder(IShape? strokeShape) + { + if (strokeShape is null || _borderPath is null) return; - _borderPath.UpdateBorderShape(_borderShape, ActualWidth, ActualHeight); + _borderPath.UpdateBorderShape(strokeShape, ActualWidth, ActualHeight); UpdateContent(); - UpdateClip(_borderShape); + + var width = ActualWidth; + var height = ActualHeight; + + if (width <= 0 || height <= 0) + return; + + UpdateClip(strokeShape, width, height); } void AddContent(FrameworkElement? content) @@ -104,27 +136,38 @@ void UpdateContent() Content.RenderTransform = new TranslateTransform() { X = -strokeThickness, Y = -strokeThickness }; } - void UpdateClip(IShape? borderShape) + void UpdateClip(IShape? borderShape, double width, double height) { - if (Content == null) + if (Content is null) return; - var clipGeometry = borderShape; - - if (clipGeometry == null) + if (height <= 0 && width <= 0) return; - double width = ActualWidth; - double height = ActualHeight; + var clipGeometry = borderShape; - if (height <= 0 && width <= 0) + if (clipGeometry is null) return; var visual = ElementCompositionPreview.GetElementVisual(Content); var compositor = visual.Compositor; + var pathSize = new Graphics.Rect(0, 0, width, height); - var clipPath = clipGeometry.PathForBounds(pathSize); + PathF? clipPath; + + if (clipGeometry is IRoundRectangle roundedRectangle) + { + float strokeThickness = (float)(_borderPath?.StrokeThickness ?? 0); + clipPath = roundedRectangle.InnerPathForBounds(pathSize, strokeThickness / 2); + IsInnerPath = true; + } + else + { + clipPath = clipGeometry.PathForBounds(pathSize); + IsInnerPath = false; + } + var device = CanvasDevice.GetSharedDevice(); var geometry = clipPath.AsPath(device); diff --git a/src/Core/src/Platform/Windows/StrokeExtensions.cs b/src/Core/src/Platform/Windows/StrokeExtensions.cs index b0798cbd699a..687a06160f33 100644 --- a/src/Core/src/Platform/Windows/StrokeExtensions.cs +++ b/src/Core/src/Platform/Windows/StrokeExtensions.cs @@ -4,12 +4,10 @@ public static class StrokeExtensions { public static void UpdateStrokeShape(this ContentPanel platformView, IBorderStroke border) { - var shape = border.Shape; - - if (shape == null) + if (border is null) return; - platformView.UpdateBorderShape(shape); + platformView.UpdateBorderStroke(border); } public static void UpdateStroke(this ContentPanel platformView, IBorderStroke border) diff --git a/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Windows.cs b/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Windows.cs index b49f66f8d391..9d1711496fe3 100644 --- a/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Windows.cs +++ b/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Windows.cs @@ -1,7 +1,4 @@ -using System; -using System.Threading.Tasks; - -namespace Microsoft.Maui.DeviceTests +namespace Microsoft.Maui.DeviceTests { public partial class BorderHandlerTests {