diff --git a/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/CommonStyles/CommonStylesTests.cs b/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/CommonStyles/CommonStylesTests.cs new file mode 100644 index 000000000000..1dbda2b5f79b --- /dev/null +++ b/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/CommonStyles/CommonStylesTests.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using Common; +using MUXControlsTestApp.Utilities; +using System.Linq; +using System.Threading; +using Windows.Foundation.Metadata; +using Windows.System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using PlatformConfiguration = Common.PlatformConfiguration; +using OSVersion = Common.OSVersion; +using System.Collections.Generic; +using XamlControlsResources = Microsoft.UI.Xaml.Controls.XamlControlsResources; +using Windows.UI.Xaml.Markup; +using System; +using Microsoft.UI.Xaml.Controls; +using System.Text; + +#if USING_TAEF +using WEX.TestExecution; +using WEX.TestExecution.Markup; +using WEX.Logging.Interop; +#else +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; +#endif + +namespace Windows.UI.Xaml.Tests.MUXControls.ApiTests +{ + [TestClass] + public class CommonStylesApiTests : MUXApiTestBase + { + // TODO: Many tests from MUX CommonStylesApiTests.cpp are missing here and should be added in the future to Uno Platform. + + [TestMethod] +#if __MACOS__ + [Ignore("Currently fails on macOS, part of #9282 epic")] +#endif + public void CornerRadiusFilterConverterTest() + { + if (!PlatformConfiguration.IsOsVersionGreaterThan(OSVersion.Redstone4)) + { + Log.Comment("Corner radius is only available on RS5+"); + return; + } + + RunOnUIThread.Execute(() => + { + var root = (StackPanel)XamlReader.Load( + @" + + + + + + + + + + + + + + + + "); + + var topRadiusGrid = (Grid)root.FindName("TopRadiusGrid"); + var rightRadiusGrid = (Grid)root.FindName("RightRadiusGrid"); + var bottomRadiusGrid = (Grid)root.FindName("BottomRadiusGrid"); + var leftRadiusGrid = (Grid)root.FindName("LeftRadiusGrid"); + + Verify.AreEqual(new CornerRadius(12, 12, 0, 0), topRadiusGrid.CornerRadius); + Verify.AreEqual(new CornerRadius(0, 6, 6, 0), rightRadiusGrid.CornerRadius); + Verify.AreEqual(new CornerRadius(0, 0, 6, 6), bottomRadiusGrid.CornerRadius); + Verify.AreEqual(new CornerRadius(6, 0, 0, 6), leftRadiusGrid.CornerRadius); + }); + } + } +} diff --git a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterConverter.cs b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterConverter.cs index 96a18502c1e4..5c145b09eb5a 100644 --- a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterConverter.cs +++ b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterConverter.cs @@ -1,75 +1,133 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. +// MUX Reference CornerRadiusFilterConverter.cpp, commit 22e5052 + +#nullable enable + +using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; -namespace Microsoft.UI.Xaml.Controls.Primitives +namespace Microsoft.UI.Xaml.Controls.Primitives; + +/// +/// Converts an existing CornerRadius struct to a new CornerRadius struct, with filters applied +/// to extract only the specified fields, leaving the others set to 0. +/// +public partial class CornerRadiusFilterConverter : DependencyObject, IValueConverter { - public partial class CornerRadiusFilterConverter : DependencyObject, IValueConverter + /// + /// Gets or sets the type of the filter applied to the CornerRadiusFilterConverter. + /// + public CornerRadiusFilterKind Filter { - public static DependencyProperty FilterProperty { get; } = DependencyProperty.Register( - nameof(Filter), typeof(CornerRadiusFilterKind), typeof(CornerRadiusFilterConverter), new FrameworkPropertyMetadata(CornerRadiusFilterKind.None)); + get => (CornerRadiusFilterKind)GetValue(FilterProperty); + set => SetValue(FilterProperty, value); + } - public CornerRadiusFilterKind Filter - { - get => (CornerRadiusFilterKind)GetValue(FilterProperty); - set => SetValue(FilterProperty, value); - } + /// + /// Identifies the Filter dependency property. + /// + public static DependencyProperty FilterProperty { get; } = + DependencyProperty.Register( + nameof(Filter), + typeof(CornerRadiusFilterKind), + typeof(CornerRadiusFilterConverter), + new FrameworkPropertyMetadata(CornerRadiusFilterKind.None)); - private static CornerRadius Convert(CornerRadius radius, CornerRadiusFilterKind filterKind) - { - var result = radius; + /// + /// Gets or sets the scale multiplier applied to the CornerRadiusFilterConverter. + /// + public double Scale + { + get => (double)GetValue(ScaleProperty); + set => SetValue(ScaleProperty, value); + } - switch (filterKind) - { - case CornerRadiusFilterKind.Top: - result.BottomLeft = 0; - result.BottomRight = 0; - break; - case CornerRadiusFilterKind.Right: - result.TopLeft = 0; - result.BottomLeft = 0; - break; - case CornerRadiusFilterKind.Bottom: - result.TopLeft = 0; - result.TopRight = 0; - break; - case CornerRadiusFilterKind.Left: - result.TopRight = 0; - result.BottomRight = 0; - break; - } + /// + /// Identifies the Scale dependency property. + /// + public static DependencyProperty ScaleProperty { get; } = + DependencyProperty.Register( + nameof(Scale), + typeof(double), + typeof(CornerRadiusFilterConverter), + new FrameworkPropertyMetadata(1.0)); - return result; + private static CornerRadius Convert(CornerRadius radius, CornerRadiusFilterKind filterKind) + { + var result = radius; + + switch (filterKind) + { + case CornerRadiusFilterKind.Top: + result.BottomLeft = 0; + result.BottomRight = 0; + break; + case CornerRadiusFilterKind.Right: + result.TopLeft = 0; + result.BottomLeft = 0; + break; + case CornerRadiusFilterKind.Bottom: + result.TopLeft = 0; + result.TopRight = 0; + break; + case CornerRadiusFilterKind.Left: + result.TopRight = 0; + result.BottomRight = 0; + break; } - private static double GetDoubleValue(CornerRadius radius, CornerRadiusFilterKind filterKind) - => - filterKind switch - { - CornerRadiusFilterKind.TopLeftValue => radius.TopLeft, - CornerRadiusFilterKind.BottomRightValue => radius.BottomRight, - _ => 0d - }; + return result; + } + + private static double GetDoubleValue(CornerRadius radius, CornerRadiusFilterKind filterKind) => + filterKind switch + { + CornerRadiusFilterKind.TopLeftValue => radius.TopLeft, + CornerRadiusFilterKind.BottomRightValue => radius.BottomRight, + _ => 0d + }; - public object Convert(object value, Type targetType, object parameter, string language) + /// + /// Converts the source CornerRadius by extracting only the fields specified + /// by the Filter and leaving others set to 0. + /// + /// The source CornerRadius being passed to the target. + /// The type of the target property. Part of the IValueConverter.Convert interface method, but not used. + /// An optional parameter to be used in the converter logic. Part of the IValueConverter.Convert interface method, but not used. + /// The language of the conversion. Part of the IValueConverter.Convert interface method, but not used. + /// The converted CornerRadius/double value to be passed to the target dependency property. + public object? Convert(object? value, Type targetType, object? parameter, string language) + { + if (value is CornerRadius cornerRadius) { - var filter = Filter; + var scale = Scale; + if (!double.IsNaN(scale)) + { + cornerRadius.TopLeft *= scale; + cornerRadius.TopRight *= scale; + cornerRadius.BottomLeft *= scale; + cornerRadius.BottomRight *= scale; + } - if (value is CornerRadius cornerRadius) + var filterType = Filter; + if (filterType == CornerRadiusFilterKind.TopLeftValue || + filterType == CornerRadiusFilterKind.BottomRightValue) { - if (filter == CornerRadiusFilterKind.TopLeftValue || filter == CornerRadiusFilterKind.BottomRightValue) - { - return GetDoubleValue(cornerRadius, filter); - } - return Convert(cornerRadius, filter); + return GetDoubleValue(cornerRadius, filterType); } - return null; + return Convert(cornerRadius, filterType); } - public object ConvertBack(object value, Type targetType, object parameter, string language) => - throw new NotSupportedException(); + return null; } + + /// + /// Not implemented. + /// + /// Always thrown when called. + public object? ConvertBack(object? value, Type targetType, object? parameter, string language) => + throw new NotImplementedException(); } diff --git a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterKind.cs b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterKind.cs index 9ac7673ebf3e..7ba405591e23 100644 --- a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterKind.cs +++ b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusFilterKind.cs @@ -1,13 +1,48 @@ -namespace Microsoft.UI.Xaml.Controls.Primitives +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. +// MUX Reference CornerRadiusFilterConverters.idl, commit c6174f1 + +#nullable enable + +namespace Microsoft.UI.Xaml.Controls.Primitives; + +/// +/// Defines constants that specify the filter type for a CornerRadiusFilterConverter instance. +/// +public enum CornerRadiusFilterKind { - public enum CornerRadiusFilterKind - { - None, - Top, - Right, - Bottom, - Left, - TopLeftValue, - BottomRightValue - } + /// + /// No filter applied. + /// + None, + + /// + /// Filters TopLeft and TopRight values, sets BottomLeft and BottomRight to 0. + /// + Top, + + /// + /// Filters TopRight and BottomRight values, sets TopLeft and BottomLeft to 0. + /// + Right, + + /// + /// Filters BottomLeft and BottomRight values, sets TopLeft and TopRight to 0. + /// + Bottom, + + /// + /// Filters TopLeft and BottomLeft values, sets TopRight and BottomRight to 0. + /// + Left, + + /// + /// Gets the double value of TopLeft corner. + /// + TopLeftValue, + + /// + /// Gets the double value of BottomRight corner. + /// + BottomRightValue } diff --git a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverter.cs b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverter.cs index 31b9f8a274ce..b75063776955 100644 --- a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverter.cs +++ b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverter.cs @@ -1,123 +1,163 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. +// MUX Reference CornerRadiusToThicknessConverter.cpp, commit 8aaf7f8 + +#nullable enable + +using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; -namespace Microsoft.UI.Xaml.Controls.Primitives +namespace Microsoft.UI.Xaml.Controls.Primitives; + +/// +/// Converts a CornerRadius to Thickness and also applies filters to extract only the specified fields, leaving the others set to 0. +/// +public sealed partial class CornerRadiusToThicknessConverter : DependencyObject, IValueConverter { - public partial class CornerRadiusToThicknessConverter : DependencyObject, IValueConverter + /// + /// Gets or sets the conversion kind that will be applied to the CornerRadiusToThicknessConverter. + /// + public CornerRadiusToThicknessConverterKind ConversionKind { - public double Multiplier - { - get => (double)GetValue(MultiplierProperty); - set => SetValue(MultiplierProperty, value); - } + get => (CornerRadiusToThicknessConverterKind)GetValue(ConversionKindProperty); + set => SetValue(ConversionKindProperty, value); + } - public static DependencyProperty MultiplierProperty { get; } = - DependencyProperty.Register(nameof(Multiplier), typeof(double), typeof(CornerRadiusToThicknessConverter), new FrameworkPropertyMetadata(1.0)); + /// + /// Identifies the ConversionKind dependency property. + /// + public static DependencyProperty ConversionKindProperty { get; } = + DependencyProperty.Register( + nameof(ConversionKind), + typeof(CornerRadiusToThicknessConverterKind), + typeof(CornerRadiusToThicknessConverter), + new FrameworkPropertyMetadata(CornerRadiusToThicknessConverterKind.FilterLeftAndRightFromTop)); - public CornerRadiusToThicknessConverterKind ConversionKind - { - get => (CornerRadiusToThicknessConverterKind)GetValue(ConversionKindProperty); - set => SetValue(ConversionKindProperty, value); - } - - public static DependencyProperty ConversionKindProperty { get; } = DependencyProperty.Register( - nameof(ConversionKind), typeof(CornerRadiusToThicknessConverterKind), typeof(CornerRadiusToThicknessConverter), new FrameworkPropertyMetadata(CornerRadiusToThicknessConverterKind.FilterLeftAndRightFromTop)); + /// + /// Gets or sets the value of the multiplier for the radius. + /// + public double Multiplier + { + get => (double)GetValue(MultiplierProperty); + set => SetValue(MultiplierProperty, value); + } - private static Thickness Convert(CornerRadius radius, CornerRadiusToThicknessConverterKind filterKind, double multiplier) - { - var result = new Thickness { }; + /// + /// Identifies the Multiplier dependency property. + /// + public static DependencyProperty MultiplierProperty { get; } = + DependencyProperty.Register( + nameof(Multiplier), + typeof(double), + typeof(CornerRadiusToThicknessConverter), + new FrameworkPropertyMetadata(1.0)); - switch (filterKind) - { - case CornerRadiusToThicknessConverterKind.FilterLeftAndRightFromTop: - result.Left = radius.TopLeft * multiplier; - result.Right = radius.TopRight * multiplier; - result.Top = 0; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterLeftAndRightFromBottom: - result.Left = radius.BottomLeft * multiplier; - result.Right = radius.BottomRight * multiplier; - result.Top = 0; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterTopAndBottomFromLeft: - result.Left = 0; - result.Right = 0; - result.Top = radius.TopLeft * multiplier; - result.Bottom = radius.BottomLeft * multiplier; - break; - case CornerRadiusToThicknessConverterKind.FilterTopAndBottomFromRight: - result.Left = 0; - result.Right = 0; - result.Top = radius.TopRight * multiplier; - result.Bottom = radius.BottomRight * multiplier; - break; - case CornerRadiusToThicknessConverterKind.FilterTopFromTopLeft: - result.Left = 0; - result.Right = 0; - result.Top = radius.TopLeft * multiplier; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterTopFromTopRight: - result.Left = 0; - result.Right = 0; - result.Top = radius.TopRight * multiplier; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterRightFromTopRight: - result.Left = 0; - result.Right = radius.TopRight * multiplier; - result.Top = 0; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterRightFromBottomRight: - result.Left = 0; - result.Right = radius.BottomRight * multiplier; - result.Top = 0; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterBottomFromBottomRight: - result.Left = 0; - result.Right = 0; - result.Top = 0; - result.Bottom = radius.BottomRight * multiplier; - break; - case CornerRadiusToThicknessConverterKind.FilterBottomFromBottomLeft: - result.Left = 0; - result.Right = 0; - result.Top = 0; - result.Bottom = radius.BottomLeft * multiplier; - break; - case CornerRadiusToThicknessConverterKind.FilterLeftFromBottomLeft: - result.Left = radius.BottomLeft * multiplier; - result.Right = 0; - result.Top = 0; - result.Bottom = 0; - break; - case CornerRadiusToThicknessConverterKind.FilterLeftFromTopLeft: - result.Left = radius.TopLeft * multiplier; - result.Right = 0; - result.Top = 0; - result.Bottom = 0; - break; - } + private static Thickness Convert(CornerRadius radius, CornerRadiusToThicknessConverterKind filterKind, double multiplier) + { + var result = new Thickness { }; - return result; + switch (filterKind) + { + case CornerRadiusToThicknessConverterKind.FilterLeftAndRightFromTop: + result.Left = radius.TopLeft * multiplier; + result.Right = radius.TopRight * multiplier; + result.Top = 0; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterLeftAndRightFromBottom: + result.Left = radius.BottomLeft * multiplier; + result.Right = radius.BottomRight * multiplier; + result.Top = 0; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterTopAndBottomFromLeft: + result.Left = 0; + result.Right = 0; + result.Top = radius.TopLeft * multiplier; + result.Bottom = radius.BottomLeft * multiplier; + break; + case CornerRadiusToThicknessConverterKind.FilterTopAndBottomFromRight: + result.Left = 0; + result.Right = 0; + result.Top = radius.TopRight * multiplier; + result.Bottom = radius.BottomRight * multiplier; + break; + case CornerRadiusToThicknessConverterKind.FilterTopFromTopLeft: + result.Left = 0; + result.Right = 0; + result.Top = radius.TopLeft * multiplier; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterTopFromTopRight: + result.Left = 0; + result.Right = 0; + result.Top = radius.TopRight * multiplier; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterRightFromTopRight: + result.Left = 0; + result.Right = radius.TopRight * multiplier; + result.Top = 0; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterRightFromBottomRight: + result.Left = 0; + result.Right = radius.BottomRight * multiplier; + result.Top = 0; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterBottomFromBottomRight: + result.Left = 0; + result.Right = 0; + result.Top = 0; + result.Bottom = radius.BottomRight * multiplier; + break; + case CornerRadiusToThicknessConverterKind.FilterBottomFromBottomLeft: + result.Left = 0; + result.Right = 0; + result.Top = 0; + result.Bottom = radius.BottomLeft * multiplier; + break; + case CornerRadiusToThicknessConverterKind.FilterLeftFromBottomLeft: + result.Left = radius.BottomLeft * multiplier; + result.Right = 0; + result.Top = 0; + result.Bottom = 0; + break; + case CornerRadiusToThicknessConverterKind.FilterLeftFromTopLeft: + result.Left = radius.TopLeft * multiplier; + result.Right = 0; + result.Top = 0; + result.Bottom = 0; + break; } - public object Convert(object value, Type targetType, object parameter, string language) + return result; + } + + /// + /// Converts a CornerRadius value to a Thickness value, while also extracting the fields specified by ConversionKind (leaving others set to 0). + /// + /// The source CornerRadius being passed to the target. + /// The type of the target property. Part of the IValueConverter.Convert interface method, but not used. + /// An optional parameter to be used in the converter logic. Part of the IValueConverter.Convert interface method, but not used. + /// The language of the conversion. Part of the IValueConverter.Convert interface method, but not used. + /// The converted Thickness value to be passed to the target dependency property. + public object? Convert(object? value, Type targetType, object? parameter, string language) + { + if (value is CornerRadius radius) { - if (value is CornerRadius radius) - { - var multiplier = Multiplier; - return Convert(radius, ConversionKind, multiplier); - } - return null; + var multiplier = Multiplier; + return Convert(radius, ConversionKind, multiplier); } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - => throw new NotSupportedException(); + return null; } + + /// + /// Not implemented. + /// + /// Always thrown when called. + public object? ConvertBack(object? value, Type targetType, object? parameter, string language) => + throw new NotImplementedException(); } diff --git a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverterKind.cs b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverterKind.cs index 87e4d04d198c..36722fe8c67f 100644 --- a/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverterKind.cs +++ b/src/Uno.UI/Microsoft/UI/Xaml/Controls/Primitives/CornerRadiusToThicknessConverterKind.cs @@ -1,18 +1,73 @@ -namespace Microsoft.UI.Xaml.Controls.Primitives +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. +// MUX Reference CornerRadiusFilterConverters.idl, commit c6174f1 + +#nullable enable + +namespace Microsoft.UI.Xaml.Controls.Primitives; + +/// +/// Defines constants that specify the filter type for a CornerRadiusToThicknessConverter instance. +/// +public enum CornerRadiusToThicknessConverterKind { - public enum CornerRadiusToThicknessConverterKind - { - FilterTopAndBottomFromLeft, - FilterTopAndBottomFromRight, - FilterLeftAndRightFromTop, - FilterLeftAndRightFromBottom, - FilterTopFromTopLeft, - FilterTopFromTopRight, - FilterRightFromTopRight, - FilterRightFromBottomRight, - FilterBottomFromBottomRight, - FilterBottomFromBottomLeft, - FilterLeftFromBottomLeft, - FilterLeftFromTopLeft, - }; + /// + /// Filters Top and Bottom values from TopLeft and BottomLeft values. + /// + FilterTopAndBottomFromLeft, + + /// + /// Filters Top and Bottom values from TopRight and BottomRight values. + /// + FilterTopAndBottomFromRight, + + /// + /// Filters Left and Right values from TopLeft and TopRight values. + /// + FilterLeftAndRightFromTop, + + /// + /// Filters Left and Right values from BottomLeft and BottomRight values. + /// + FilterLeftAndRightFromBottom, + + /// + /// Filters Top value from TopLeft value. + /// + FilterTopFromTopLeft, + + /// + /// Filters Top value from TopRight value. + /// + FilterTopFromTopRight, + + /// + /// Filters Right value from TopRight value. + /// + FilterRightFromTopRight, + + /// + /// Filters Right value from BottomRight value. + /// + FilterRightFromBottomRight, + + /// + /// Filters Bottom value from BottomRight value. + /// + FilterBottomFromBottomRight, + + /// + /// Filters Bottom value from BottomLeft value. + /// + FilterBottomFromBottomLeft, + + /// + /// Filters Left value from BottomLeft value. + /// + FilterLeftFromBottomLeft, + + /// + /// Filters Left value from TopLeft value. + /// + FilterLeftFromTopLeft, }