From ea291b864b332213fb36d77154ca565b4600b6e8 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 26 Apr 2024 13:00:37 +0200 Subject: [PATCH] feat: FauxGradientBorderPresenter --- .../Controls/FauxGradientBorderPresenter.cs | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/Uno.UI/Controls/FauxGradientBorderPresenter.cs diff --git a/src/Uno.UI/Controls/FauxGradientBorderPresenter.cs b/src/Uno.UI/Controls/FauxGradientBorderPresenter.cs new file mode 100644 index 000000000000..f804e147b0a4 --- /dev/null +++ b/src/Uno.UI/Controls/FauxGradientBorderPresenter.cs @@ -0,0 +1,152 @@ +#nullable enable + +using System.Collections.Generic; +using System.Linq; +using Uno.Foundation.Logging; +using Uno.UI.Xaml.Media; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; + +namespace Uno.UI.Controls; + +/// +/// This presenter provides a way to display "fake" LinearGradientBrush on element border for +/// cases which are unsupported by WebAssembly, iOS and macOS. +/// +/// +/// WASM - the presenter will be used in case the element has CornerRadius applied. +/// iOS and macOS - the presenter will be used in case the element has LinearGradientBrush with a transform applied. +/// All other cases - the presenter is not visible. +/// +public partial class FauxGradientBorderPresenter : ContentPresenter +{ +#if __WASM__ || __IOS__ || __MACOS__ + private readonly Border? _displayBorder; +#endif + + public FauxGradientBorderPresenter() + { +#if __WASM__ || __IOS__ || __MACOS__ + HorizontalContentAlignment = HorizontalAlignment.Stretch; + VerticalContentAlignment = VerticalAlignment.Stretch; + Content = _displayBorder = new Border(); +#else + Visibility = Visibility.Collapsed; +#endif + } + + /// + /// Gets or sets the border brush that is supposed to be displayed. + /// + public Brush RequestedBorderBrush + { + get => (Brush)GetValue(RequestedBorderBrushProperty); + set => SetValue(RequestedBorderBrushProperty, value); + } + + /// + /// Identifies the RequestedBorderBrush dependency property. + /// + public static DependencyProperty RequestedBorderBrushProperty { get; } = + DependencyProperty.Register( + nameof(RequestedBorderBrush), + typeof(Brush), + typeof(FauxGradientBorderPresenter), + new FrameworkPropertyMetadata(null, propertyChangedCallback: (s, args) => ((FauxGradientBorderPresenter)s)?.OnBorderChanged())); + + /// + /// Gets or sets the thickness of the border that is supposed to be displayed. + /// + public Thickness RequestedBorderThickness + { + get => (Thickness)GetValue(RequestedBorderThicknessProperty); + set => SetValue(RequestedBorderThicknessProperty, value); + } + + /// + /// Identifies the RequestedBorderThickness dependency property. + /// + public static DependencyProperty RequestedBorderThicknessProperty { get; } = + DependencyProperty.Register( + nameof(RequestedBorderThickness), + typeof(Thickness), + typeof(FauxGradientBorderPresenter), + new FrameworkPropertyMetadata(default(Thickness), propertyChangedCallback: (s, args) => ((FauxGradientBorderPresenter)s)?.OnBorderChanged())); + + public CornerRadius RequestedCornerRadius + { + get => (CornerRadius)GetValue(RequestedCornerRadiusProperty); + set => SetValue(RequestedCornerRadiusProperty, value); + } + + public static DependencyProperty RequestedCornerRadiusProperty { get; } = + DependencyProperty.Register( + nameof(RequestedCornerRadius), + typeof(CornerRadius), + typeof(FauxGradientBorderPresenter), + new FrameworkPropertyMetadata(CornerRadius.None, propertyChangedCallback: (s, args) => ((FauxGradientBorderPresenter)s)?.OnBorderChanged())); + + private void OnBorderChanged() + { +#if __WASM__ || __IOS__ || __MACOS__ + if (_displayBorder == null) + { + return; + } + + var requestedThickness = RequestedBorderThickness; + var requestedBorderBrush = RequestedBorderBrush; + var requestedCornerRadius = RequestedCornerRadius; + + if (requestedBorderBrush is not LinearGradientBrush gradientBrush || + !gradientBrush.CanApplySolidColorRendering()) + { + _displayBorder.Visibility = Visibility.Collapsed; + return; + } + +#if __WASM__ + if (requestedCornerRadius == CornerRadius.None) + { + // WASM can render linear gradient border unless corner radius is set. + _displayBorder.Visibility = Visibility.Collapsed; + return; + } +#endif + +#if __IOS__ || __MACOS__ + if (gradientBrush.RelativeTransform == null) + { + // iOS can render linear gradient border unless relative transform is used. + _displayBorder.Visibility = Visibility.Collapsed; + return; + } +#endif + + requestedThickness.Left = 0; + requestedThickness.Right = 0; + var minorStopAlignment = gradientBrush.GetMinorStopAlignment(); + if (minorStopAlignment == VerticalAlignment.Top) + { + requestedThickness.Bottom = 0; + } + else + { + requestedThickness.Top = 0; + } + + if (requestedThickness == Thickness.Empty) + { + _displayBorder.Visibility = Visibility.Collapsed; + return; + } + + _displayBorder.Visibility = Visibility.Visible; + + _displayBorder.CornerRadius = requestedCornerRadius; + _displayBorder.BorderThickness = requestedThickness; + _displayBorder.BorderBrush = gradientBrush.FauxOverlayBrush; +#endif + } +}