From 3887af4454da613c7253d3c418ac7e0abcc9ea1c Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 2 Feb 2024 16:35:33 +0100 Subject: [PATCH 01/12] refactor: Move border-related properties into a IBorderInfoProvider --- .../Border/Border.IBorderInfoProvider.cs | 17 ++++++ .../Controls/Border/IBorderInfoProvider.cs | 38 ++++++++++++ ...rViewBaseItemChrome.IBorderInfoProvider.cs | 60 +++++++++++++++++++ .../ContentPresenter.IBorderInfoProvider.cs | 17 ++++++ .../Controls/Page/Page.IBorderInfoProvider.cs | 19 ++++++ .../Panel/Panel.IBorderInfoProvider.cs | 19 ++++++ 6 files changed, 170 insertions(+) create mode 100644 src/Uno.UI/UI/Xaml/Controls/Border/Border.IBorderInfoProvider.cs create mode 100644 src/Uno.UI/UI/Xaml/Controls/Border/IBorderInfoProvider.cs create mode 100644 src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs create mode 100644 src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.IBorderInfoProvider.cs create mode 100644 src/Uno.UI/UI/Xaml/Controls/Page/Page.IBorderInfoProvider.cs create mode 100644 src/Uno.UI/UI/Xaml/Controls/Panel/Panel.IBorderInfoProvider.cs diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/Border.IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/Border/Border.IBorderInfoProvider.cs new file mode 100644 index 000000000000..7a3ef283c94a --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/Border/Border.IBorderInfoProvider.cs @@ -0,0 +1,17 @@ +using Uno.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; + +namespace Microsoft.UI.Xaml.Controls; + +partial class Border : IBorderInfoProvider +{ + Brush IBorderInfoProvider.Background => Background; + + BackgroundSizing IBorderInfoProvider.BackgroundSizing => BackgroundSizing; + + Brush IBorderInfoProvider.BorderBrush => BorderBrush; + + Thickness IBorderInfoProvider.BorderThickness => BorderThickness; + + CornerRadius IBorderInfoProvider.CornerRadius => CornerRadius; +} diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/Border/IBorderInfoProvider.cs new file mode 100644 index 000000000000..26d779728f6d --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/Border/IBorderInfoProvider.cs @@ -0,0 +1,38 @@ +#nullable enable + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; + +namespace Uno.UI.Xaml.Controls; + +/// +/// Provides properties required for border rendering. +/// +internal partial interface IBorderInfoProvider +{ + /// + /// Gets the background brush. + /// + Brush? Background { get; } + + /// + /// Gets the background sizing. + /// + BackgroundSizing BackgroundSizing { get; } + + /// + /// Gets the border brush. + /// + Brush? BorderBrush { get; } + + /// + /// Gets the border thickness. + /// + Thickness BorderThickness { get; } + + /// + /// Gets the corner radius. + /// + CornerRadius CornerRadius { get; } +} diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs new file mode 100644 index 000000000000..2852a9a08031 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.UI.Xaml.Media; +using Uno.UI.Xaml.Controls; + +namespace Microsoft.UI.Xaml.Controls; + +partial class CalendarViewBaseItem : IBorderInfoProvider +{ + Brush IBorderInfoProvider.Background + { + get + { + var background = Background; + + if (IsClear(background)) + { + if (FindTodaySelectedBackgroundBrush() is { } todaySelectedBackground + && !IsClear(todaySelectedBackground)) + { + background = todaySelectedBackground; + } + else if (FindSelectedBackgroundBrush() is { } selectedBackground + && !IsClear(selectedBackground)) + { + background = selectedBackground; + } + else + { + background = GetItemBackgroundBrush(); + } + } + return background; + } + } + + BackgroundSizing IBorderInfoProvider.BackgroundSizing => BackgroundSizing.InnerBorderEdge; + + Brush IBorderInfoProvider.BorderBrush + { + get + { + var borderBrush = GetItemBorderBrush(forFocus: false); + if (m_isToday && m_isSelected && GetItemInnerBorderBrush() is { } selectedBrush) + { + // We don't support inner border yet, so even if not optimal we just use it as border. + borderBrush = selectedBrush; + } + return borderBrush; + } + } + + + Thickness IBorderInfoProvider.BorderThickness => GetItemBorderThickness(); + + CornerRadius IBorderInfoProvider.CornerRadius => GetItemCornerRadius(); +} diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.IBorderInfoProvider.cs new file mode 100644 index 000000000000..e0998b37c9e2 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.IBorderInfoProvider.cs @@ -0,0 +1,17 @@ +using Uno.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; + +namespace Microsoft.UI.Xaml.Controls; + +partial class ContentPresenter : IBorderInfoProvider +{ + Brush IBorderInfoProvider.Background => Background; + + BackgroundSizing IBorderInfoProvider.BackgroundSizing => BackgroundSizing; + + Brush IBorderInfoProvider.BorderBrush => BorderBrush; + + Thickness IBorderInfoProvider.BorderThickness => BorderThickness; + + CornerRadius IBorderInfoProvider.CornerRadius => CornerRadius; +} diff --git a/src/Uno.UI/UI/Xaml/Controls/Page/Page.IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/Page/Page.IBorderInfoProvider.cs new file mode 100644 index 000000000000..d1bec74b7ff9 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/Page/Page.IBorderInfoProvider.cs @@ -0,0 +1,19 @@ +#nullable enable + +using Microsoft.UI.Xaml.Media; +using Uno.UI.Xaml.Controls; + +namespace Microsoft.UI.Xaml.Controls; + +public partial class Page : IBorderInfoProvider +{ + Brush? IBorderInfoProvider.Background => Background; + + BackgroundSizing IBorderInfoProvider.BackgroundSizing => InternalBackgroundSizing; + + Brush? IBorderInfoProvider.BorderBrush => null; + + Thickness IBorderInfoProvider.BorderThickness => Thickness.Empty; + + CornerRadius IBorderInfoProvider.CornerRadius => CornerRadius.None; +} diff --git a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.IBorderInfoProvider.cs new file mode 100644 index 000000000000..59d6a9431bce --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.IBorderInfoProvider.cs @@ -0,0 +1,19 @@ +#nullable enable + +using Uno.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; + +namespace Microsoft.UI.Xaml.Controls; + +partial class Panel : IBorderInfoProvider +{ + Brush? IBorderInfoProvider.Background => Background; + + BackgroundSizing IBorderInfoProvider.BackgroundSizing => InternalBackgroundSizing; + + Brush? IBorderInfoProvider.BorderBrush => BorderBrushInternal; + + Thickness IBorderInfoProvider.BorderThickness => BorderThicknessInternal; + + CornerRadius IBorderInfoProvider.CornerRadius => CornerRadiusInternal; +} From ceafe8b8520a1110cffed88ef8f5136610860478 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 2 Feb 2024 16:36:28 +0100 Subject: [PATCH 02/12] refactor: Simplify BorderLayerRenderer on Skia --- .../UI/Xaml/Controls/Border/Border.skia.cs | 19 +-- .../Border/BorderLayerRenderer.Android.cs | 2 +- .../Controls/Border/BorderLayerRenderer.cs | 132 ++++++++++-------- .../Border/BorderLayerRenderer.iOSmacOS.cs | 2 +- .../Border/BorderLayerRenderer.skia.cs | 78 +++-------- .../Border/BorderLayerRenderer.wasm.cs | 2 +- .../Xaml/Controls/Border/BorderLayerState.cs | 41 ++++++ .../CalendarView/CalendarViewBaseItem.h.cs | 4 +- .../CalendarViewBaseItemChrome.uno.cs | 16 ++- .../ContentPresenter/ContentPresenter.skia.cs | 17 +-- src/Uno.UI/UI/Xaml/Controls/Page/Page.cs | 2 + src/Uno.UI/UI/Xaml/Controls/Page/Page.skia.cs | 19 +-- .../UI/Xaml/Controls/Panel/Panel.skia.cs | 19 +-- 13 files changed, 179 insertions(+), 174 deletions(-) create mode 100644 src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerState.cs diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/Border.skia.cs b/src/Uno.UI/UI/Xaml/Controls/Border/Border.skia.cs index 78455f3404ce..aed8d0251e5f 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/Border.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/Border.skia.cs @@ -14,20 +14,19 @@ using System.Numerics; using Windows.Foundation; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { public partial class Border { - private BorderLayerRenderer _borderRenderer = new BorderLayerRenderer(); - public Border() { - Loaded += (s, e) => UpdateBorder(); - Unloaded += (s, e) => _borderRenderer.Clear(); - LayoutUpdated += (s, e) => UpdateBorder(); + BorderRenderer = new BorderLayerRenderer(this); } + internal BorderLayerRenderer BorderRenderer { get; } + partial void OnChildChangedPartial(View previousValue, View newValue) { if (previousValue != null) @@ -42,15 +41,7 @@ private void UpdateBorder() { if (Visual != null) { - _borderRenderer.UpdateLayer( - this, - Background, - BackgroundSizing, - BorderThickness, - BorderBrush, - CornerRadius, - null - ); + BorderRenderer.Update(); } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs index b248e6887b21..4912c69b90fd 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs @@ -18,7 +18,7 @@ using Microsoft.UI.Xaml.Media.Imaging; using Rect = Windows.Foundation.Rect; -namespace Microsoft.UI.Xaml.Controls +namespace Uno.UI.Xaml.Controls { partial class BorderLayerRenderer { diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs index f0269f90d063..2bda831c3e20 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs @@ -4,81 +4,99 @@ using Windows.Foundation; using Microsoft.UI.Xaml; using Uno.UI; +using Uno.Disposables; -namespace Microsoft.UI.Xaml.Shapes; +namespace Uno.UI.Xaml.Controls; +/// +/// Provides Border and Background rendering capabilities to a UI element. +/// internal partial class BorderLayerRenderer { - /* The border is defined by the inner and outer rounded-rectangles. - * Each rounded rectangle is composed of 4x lines and 4x 90' arcs. - * ╭─────╮ - * │A 2 B│ - * │1 3│ - * │D 4 C│ - * ╰─────╯ - * Three factors determine the border: BorderThickness, CornerRadius, and AvailableSize. - * What each part affects: - * - CornerRadius="1A2,2B3,3C4,4D1" - * - BorderThickness="D1A,A2B,B3C,C4D" - * - AvailableSize is used to constrain the radius. - * - * note: technically CornerRadius (together with AvailableSize) also affects the adjacent corner, - * as both points on the same axis will compete for what's available when both add up exceeds - * the available length. (see: h/vRatio in CalculateBorderEllipse) - */ + private readonly FrameworkElement _owner; + private readonly IBorderInfoProvider _borderInfoProvider; + private readonly SerialDisposable _borderBrushSubscription = new(); + private readonly SerialDisposable _backgroundBrushSubscription = new(); - public enum Corner { TopLeft, TopRight, BottomRight, BottomLeft } + private BorderLayerState _currentState; - public static Rect CalculateBorderEllipseBbox(Rect bbox, Corner corner, CornerRadius cr, Thickness bt, bool inner = false) + public BorderLayerRenderer(FrameworkElement owner) { - var (cr0, hThickness, vThickness, hComplement, vComplement) = corner switch + _owner = owner ?? throw new ArgumentNullException(nameof(owner)); + if (owner is not IBorderInfoProvider borderInfoProvider) { - Corner.TopLeft => (cr.TopLeft, bt.Left, bt.Top, cr.TopRight, cr.BottomLeft), - Corner.TopRight => (cr.TopRight, bt.Right, bt.Top, cr.TopLeft, cr.BottomRight), - Corner.BottomRight => (cr.BottomRight, bt.Right, bt.Bottom, cr.BottomLeft, cr.TopRight), - Corner.BottomLeft => (cr.BottomLeft, bt.Left, bt.Bottom, cr.BottomRight, cr.TopLeft), + throw new InvalidOperationException("BorderLayerRenderer requires an owner which implements IBorderInfoProvider"); + } - _ => throw new ArgumentOutOfRangeException($"Invalid corner: {corner}"), - }; + _borderInfoProvider = borderInfoProvider; - // there is still a corner to be painted, albeit not rounded. - if (cr0 == 0) return AlignCorner(bbox, corner, default); + _owner.Loaded += (s, e) => Update(); + _owner.Unloaded += (s, e) => Clear(); + _owner.LayoutUpdated += (s, e) => Update(); + } - // The ellipse can only grow up to twice the available length. - // This is further limited by the ratio between the corner-radius - // of that corner and the adjacent corner on the same line. - var hRatio = hComplement == 0 ? 1 : (cr0 / (cr0 + hComplement)); - var vRatio = vComplement == 0 ? 1 : (cr0 / (cr0 + vComplement)); + /// + /// Updates the border. + /// + internal void Update() + { + if (_owner.IsLoaded) + { + // Subscribe to brushes to observe their changes. + if (_currentState.BorderBrush != _borderInfoProvider.BorderBrush) + { + _borderBrushSubscription.Disposable = null; + if (_borderInfoProvider.BorderBrush is { } borderBrush) + { + borderBrush.InvalidateRender += OnBorderBrushChanged; + _borderBrushSubscription.Disposable = Disposable.Create(() => borderBrush.InvalidateRender -= OnBorderBrushChanged); + } + } - // if size is empty here, there is still a corner to be painted, just not rounded. - var size = new Size( - width: Math.Max(0, Math.Min(bbox.Width * 2 * hRatio, cr0 * 2 + (inner ? -hThickness : hThickness))), - height: Math.Max(0, Math.Min(bbox.Height * 2 * vRatio, cr0 * 2 + (inner ? -vThickness : vThickness))) - ); - var result = AlignCorner(bbox, corner, size); + if (_currentState.Background != _borderInfoProvider.Background) + { + _backgroundBrushSubscription.Disposable = null; + if (_borderInfoProvider.Background is { } backgroundBrush) + { + backgroundBrush.InvalidateRender += OnBackgroundBrushChanged; + _backgroundBrushSubscription.Disposable = Disposable.Create(() => backgroundBrush.InvalidateRender -= OnBackgroundBrushChanged); + } + } - return result; + UpdatePlatform(); + } + } + + private void OnBorderBrushChanged() + { + // Force the border to be recreated during update. + _currentState.BorderBrush = null; + Update(); + } + + private void OnBackgroundBrushChanged() + { + // Force the background to be recreated during update. + _currentState.Background = null; + Update(); } /// - /// Arrange a size on the bounding- so that the borders neighboring - /// to the are overlapped. + /// Removes added layers and subscriptions. /// - /// The resulting rect can be outside of the bounding-box. - private static Rect AlignCorner(Rect bbox, Corner corner, Size size) + internal void Clear() { - // note: the ellipse can project outside the bounding-box - // because only a quarter needs to be constrained within. - var location = (Point)(corner switch - { - Corner.TopLeft => new(bbox.Left, bbox.Top), - Corner.TopRight => new(bbox.Left + bbox.Width - size.Width, bbox.Top), - Corner.BottomRight => new(bbox.Left + bbox.Width - size.Width, bbox.Top + bbox.Height - size.Height), - Corner.BottomLeft => new(bbox.Left, bbox.Top + bbox.Height - size.Height), - - _ => throw new ArgumentOutOfRangeException($"Invalid corner: {corner}"), - }); + UnsubscribeBrushChanges(); + ClearPlatform(); + } - return new(location, size); + private void UnsubscribeBrushChanges() + { + _borderBrushSubscription.Disposable = null; + _backgroundBrushSubscription.Disposable = null; } + + partial void UpdatePlatform(); + + partial void ClearPlatform(); } diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs index d31dee7a9bbf..b6fc7cd9aa10 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs @@ -31,7 +31,7 @@ using RadialGradientBrush = Microsoft/* UWP don't rename */.UI.Xaml.Media.RadialGradientBrush; -namespace Microsoft.UI.Xaml.Shapes +namespace Uno.UI.Xaml.Controls { partial class BorderLayerRenderer { diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs index d2ed70b08d58..10fb94fa6edf 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs @@ -13,16 +13,16 @@ using Microsoft.UI.Composition; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; +using Uno.UI.Xaml.Controls; +using Microsoft.UI.Xaml; -namespace Microsoft.UI.Xaml.Shapes +namespace Uno.UI.Xaml.Controls { partial class BorderLayerRenderer { private static SKPoint[] _outerRadiiStore = new SKPoint[4]; private static SKPoint[] _innerRadiiStore = new SKPoint[4]; - private LayoutState _currentState; - private SerialDisposable _layerDisposable = new SerialDisposable(); /// @@ -34,33 +34,28 @@ partial class BorderLayerRenderer /// The border brush /// The corner radius /// The background image in case of a ImageBrush background - public void UpdateLayer( - FrameworkElement owner, - Brush background, - BackgroundSizing backgroundSizing, - Thickness borderThickness, - Brush borderBrush, - CornerRadius cornerRadius, - object backgroundImage - ) + partial void UpdatePlatform() { - // Bounds is captured to avoid calling twice calls below. - var area = new Rect(0, 0, owner.ActualWidth, owner.ActualHeight); - - var newState = new LayoutState(area, background, backgroundSizing, borderThickness, borderBrush, cornerRadius, backgroundImage); + var newState = new BorderLayerState( + new Size(_owner.ActualWidth, _owner.ActualHeight), + _borderInfoProvider.Background, + _borderInfoProvider.BackgroundSizing, + _borderInfoProvider.BorderBrush, + _borderInfoProvider.BorderThickness, + _borderInfoProvider.CornerRadius); var previousLayoutState = _currentState; if (!newState.Equals(previousLayoutState)) { if ( - background != null || - cornerRadius != CornerRadius.None || - (borderThickness != Thickness.Empty && borderBrush != null) + newState.Background != null || + newState.CornerRadius != CornerRadius.None || + (newState.BorderThickness != Thickness.Empty && newState.BorderBrush != null) ) { _layerDisposable.Disposable = null; - _layerDisposable.Disposable = InnerCreateLayer(owner, newState); + _layerDisposable.Disposable = InnerCreateLayer(_owner, newState); } else { @@ -74,15 +69,15 @@ object backgroundImage /// /// Removes the added layers during a call to . /// - internal void Clear() + partial void ClearPlatform() { _layerDisposable.Disposable = null; - _currentState = null; + _currentState = default; } - private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state) + private static IDisposable InnerCreateLayer(UIElement owner, BorderLayerState state) { - var area = owner.LayoutRound(state.Area); + var area = owner.LayoutRound(new Rect(default, state.ElementSize)); // In case the element has no size, skip everything! if (area.Width == 0 && area.Height == 0) @@ -358,40 +353,5 @@ private static void SetRoundedPath(in SKPath geometry, SKRect area, SKPoint[] ra geometry.AddRoundRect(roundRect); geometry.Close(); } - - private class LayoutState : IEquatable - { - public readonly Rect Area; - public readonly Brush Background; - public readonly BackgroundSizing BackgroundSizing; - public readonly Brush BorderBrush; - public readonly Thickness BorderThickness; - public readonly CornerRadius CornerRadius; - public readonly object BackgroundImage; - - public LayoutState(Rect area, Brush background, BackgroundSizing backgroundSizing, - Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, object backgroundImage) - { - Area = area; - Background = background; - BackgroundSizing = backgroundSizing; - BorderBrush = borderBrush; - CornerRadius = cornerRadius; - BorderThickness = borderThickness; - BackgroundImage = backgroundImage; - } - - public bool Equals(LayoutState other) - { - return other != null - && other.Area == Area - && other.Background == Background - && other.BackgroundSizing == BackgroundSizing - && other.BorderBrush == BorderBrush - && other.BorderThickness == BorderThickness - && other.CornerRadius == CornerRadius - && other.BackgroundImage == BackgroundImage; - } - } } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs index 6ef1a7448e1e..e1e354d05d21 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs @@ -13,7 +13,7 @@ using Uno.UI.Extensions; using Windows.Foundation; -namespace Microsoft.UI.Xaml.Shapes +namespace Uno.UI.Xaml.Controls { partial class BorderLayerRenderer { diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerState.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerState.cs new file mode 100644 index 000000000000..48ae228ef6cc --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerState.cs @@ -0,0 +1,41 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; +using Windows.Foundation; + +namespace Uno.UI.Xaml.Controls; + +/// +/// Represents a state of border layer. +/// +/// Element size. +/// Background brush. +/// Background sizing. +/// Border brush. +/// Border thickness. +/// Corner radius. +internal record struct BorderLayerState( + Size ElementSize, + Brush? Background, + BackgroundSizing BackgroundSizing, + Brush? BorderBrush, + Thickness BorderThickness, + CornerRadius CornerRadius) +{ + internal BorderLayerState(Size elementSize, IBorderInfoProvider borderInfoProvider) : this( + elementSize, + borderInfoProvider.Background, + borderInfoProvider.BackgroundSizing, + borderInfoProvider.BorderBrush, + borderInfoProvider.BorderThickness, + borderInfoProvider.CornerRadius) + { + } +} diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.h.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.h.cs index 4c921faf53bd..c4a432425a00 100644 --- a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.h.cs +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.h.cs @@ -26,8 +26,10 @@ internal CalendarViewBaseItem() // Make sure the ctor is not publically visible Initialize_CalendarViewBaseItemChrome(); this.Loaded += (_, _) => { -#if __ANDROID__ || __IOS__ || __SKIA__ || __WASM__ || __MACOS__ +#if __ANDROID__ || __IOS__ || __WASM__ || __MACOS__ _borderRenderer ??= new(); +#elif __SKIA__ + _borderRenderer ??= new(this); #endif EnterImpl(); }; diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs index f078325e53cc..50a6eab7bf29 100644 --- a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs @@ -4,6 +4,7 @@ using Windows.Foundation; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { @@ -65,6 +66,18 @@ private void UpdateChromeIfNeeded(Rect rect) private void UpdateChrome() { +#if __SKIA__ + _borderRenderer ??= new BorderLayerRenderer(this); + + // DrawBackground => General background for all items + // DrawControlBackground => Control.Background customized by the apps (can be customized in the element changing event) + // DrawDensityBar => Not supported yet + // DrawFocusBorder => Not supported yet + // OR DrawBorder => Draws the border ... + // DrawInnerBorder => The today / selected state + + _borderRenderer.Update(); +#else // DrawBackground => General background for all items // DrawControlBackground => Control.Background customized by the apps (can be customized in the element changing event) // DrawDensityBar => Not supported yet @@ -112,8 +125,9 @@ private void UpdateChrome() } #endif -#if __ANDROID__ || __IOS__ || __SKIA__ || __WASM__ || __MACOS__ +#if __ANDROID__ || __IOS__ || __WASM__ || __MACOS__ _borderRenderer?.UpdateLayer(this, background, BackgroundSizing.InnerBorderEdge, borderThickness, borderBrush, cornerRadius, default); +#endif #endif } diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs index b4430ec386c8..294d926f1df4 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs @@ -9,6 +9,7 @@ using Windows.UI.Core; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { @@ -23,18 +24,20 @@ public partial class ContentPresenter : FrameworkElement { private static Lazy _nativeElementHostingExtension = new Lazy(() => ApiExtensibility.CreateInstance(typeof(ContentPresenter))); - private BorderLayerRenderer _borderRenderer = new BorderLayerRenderer(); + private readonly BorderLayerRenderer _borderRenderer; + private Rect? _lastArrangeRect; private Rect _lastGlobalRect; private bool _nativeHostRegistered; public ContentPresenter() { + _borderRenderer = new BorderLayerRenderer(this); + InitializeContentPresenter(); Loaded += (s, e) => RegisterNativeHostSupport(); Unloaded += (s, e) => UnregisterNativeHostSupport(); - LayoutUpdated += (s, e) => UpdateBorder(); } private void SetUpdateTemplate() @@ -100,15 +103,7 @@ private void UpdateBorder() { if (IsLoaded) { - _borderRenderer.UpdateLayer( - this, - Background, - BackgroundSizing, - BorderThickness, - BorderBrush, - CornerRadius, - null - ); + _borderRenderer.Update(); } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Page/Page.cs b/src/Uno.UI/UI/Xaml/Controls/Page/Page.cs index 0d8555d29d8b..1ffeb68ac0af 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Page/Page.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Page/Page.cs @@ -8,10 +8,12 @@ namespace Microsoft.UI.Xaml.Controls { public partial class Page : UserControl { +#if !__SKIA__ public Page() { InitializeBorder(); } +#endif protected internal virtual void OnNavigatedFrom(NavigationEventArgs e) { } diff --git a/src/Uno.UI/UI/Xaml/Controls/Page/Page.skia.cs b/src/Uno.UI/UI/Xaml/Controls/Page/Page.skia.cs index fdb3ee43f940..fc58497f533a 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Page/Page.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Page/Page.skia.cs @@ -5,33 +5,24 @@ using Uno.UI.DataBinding; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { public partial class Page { - private BorderLayerRenderer _borderRenderer = new BorderLayerRenderer(); + private readonly BorderLayerRenderer _borderRenderer; - private void InitializeBorder() + public Page() { - Loaded += (s, e) => UpdateBorder(); - Unloaded += (s, e) => _borderRenderer.Clear(); - LayoutUpdated += (s, e) => UpdateBorder(); + _borderRenderer = new BorderLayerRenderer(this); } private void UpdateBorder() { if (IsLoaded) { - _borderRenderer.UpdateLayer( - this, - Background, - InternalBackgroundSizing, - Thickness.Empty, - null, - CornerRadius.None, - null - ); + _borderRenderer.Update(); } } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.skia.cs b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.skia.cs index 76a762f53059..8e0b22b479e3 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.skia.cs @@ -15,20 +15,19 @@ using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { public partial class Panel { - private readonly BorderLayerRenderer _borderRenderer = new BorderLayerRenderer(); + private readonly BorderLayerRenderer _borderRenderer; public Panel() { - Initialize(); + _borderRenderer = new BorderLayerRenderer(this); - Loaded += (s, e) => UpdateBorder(); - Unloaded += (s, e) => _borderRenderer.Clear(); - LayoutUpdated += (s, e) => UpdateBorder(); + Initialize(); } partial void Initialize(); @@ -38,15 +37,7 @@ partial void UpdateBorder() // Checking for Window avoids re-creating the layer until it is actually used. if (IsLoaded) { - _borderRenderer.UpdateLayer( - this, - Background, - InternalBackgroundSizing, - BorderThicknessInternal, - BorderBrushInternal, - CornerRadiusInternal, - null - ); + _borderRenderer.Update(); } } From cde9057143fdc00231b0a17973cf0efcb0023baf Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Fri, 2 Feb 2024 16:42:54 +0100 Subject: [PATCH 03/12] chore: Make the refactored implementation Skia-only --- src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs index 2bda831c3e20..0d09ee74e1d9 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs @@ -1,4 +1,5 @@ -using System; +#if __SKIA__ +using System; using System.Collections.Generic; using System.Text; using Windows.Foundation; @@ -100,3 +101,4 @@ private void UnsubscribeBrushChanges() partial void ClearPlatform(); } +#endif From 05c3dfbb084eea096d2b4c72e3610e2c03fe19b3 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Mon, 5 Feb 2024 15:49:42 +0100 Subject: [PATCH 04/12] chore: iOS adjustments for new namespace --- .../Xaml/Controls/Border/Border.iOSmacOS.cs | 2 + .../Border/BorderLayerRenderer.iOSmacOS.cs | 75 +++++++++++++++++++ .../Border/BorderLayerRenderer.reference.cs | 27 ++++--- .../Border/BorderLayerRenderer.unittests.cs | 23 +++--- .../ContentPresenter/ContentPresenter.iOS.cs | 1 + src/Uno.UI/UI/Xaml/Controls/Page/Page.iOS.cs | 1 + .../UI/Xaml/Controls/Panel/Panel.iOS.cs | 1 + 7 files changed, 104 insertions(+), 26 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/Border.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/Controls/Border/Border.iOSmacOS.cs index e57426857e29..5892e46c7433 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/Border.iOSmacOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/Border.iOSmacOS.cs @@ -8,6 +8,8 @@ using Microsoft.UI.Xaml.Media; using CoreGraphics; using Uno.UI.Xaml.Media; +using Uno.UI.Xaml.Controls; + #if __IOS__ using UIKit; using _Image = UIKit.UIImage; diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs index b6fc7cd9aa10..a1084aaaaecb 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs @@ -16,6 +16,8 @@ using Uno.UI.Extensions; using Uno.UI.Xaml.Media; using ObjCRuntime; +using Microsoft.UI.Xaml; + #if __IOS__ using UIKit; @@ -670,5 +672,78 @@ private record LayoutState( { // internal CGPath BoundsPath { get; set; } } + + /* The border is defined by the inner and outer rounded-rectangles. + * Each rounded rectangle is composed of 4x lines and 4x 90' arcs. + * ╭─────╮ + * │A 2 B│ + * │1 3│ + * │D 4 C│ + * ╰─────╯ + * Three factors determine the border: BorderThickness, CornerRadius, and AvailableSize. + * What each part affects: + * - CornerRadius="1A2,2B3,3C4,4D1" + * - BorderThickness="D1A,A2B,B3C,C4D" + * - AvailableSize is used to constrain the radius. + * + * note: technically CornerRadius (together with AvailableSize) also affects the adjacent corner, + * as both points on the same axis will compete for what's available when both add up exceeds + * the available length. (see: h/vRatio in CalculateBorderEllipse) + */ + + public enum Corner { TopLeft, TopRight, BottomRight, BottomLeft } + + public static Rect CalculateBorderEllipseBbox(Rect bbox, Corner corner, CornerRadius cr, Thickness bt, bool inner = false) + { + var (cr0, hThickness, vThickness, hComplement, vComplement) = corner switch + { + Corner.TopLeft => (cr.TopLeft, bt.Left, bt.Top, cr.TopRight, cr.BottomLeft), + Corner.TopRight => (cr.TopRight, bt.Right, bt.Top, cr.TopLeft, cr.BottomRight), + Corner.BottomRight => (cr.BottomRight, bt.Right, bt.Bottom, cr.BottomLeft, cr.TopRight), + Corner.BottomLeft => (cr.BottomLeft, bt.Left, bt.Bottom, cr.BottomRight, cr.TopLeft), + + _ => throw new ArgumentOutOfRangeException($"Invalid corner: {corner}"), + }; + + // there is still a corner to be painted, albeit not rounded. + if (cr0 == 0) return AlignCorner(bbox, corner, default); + + // The ellipse can only grow up to twice the available length. + // This is further limited by the ratio between the corner-radius + // of that corner and the adjacent corner on the same line. + var hRatio = hComplement == 0 ? 1 : (cr0 / (cr0 + hComplement)); + var vRatio = vComplement == 0 ? 1 : (cr0 / (cr0 + vComplement)); + + // if size is empty here, there is still a corner to be painted, just not rounded. + var size = new Size( + width: Math.Max(0, Math.Min(bbox.Width * 2 * hRatio, cr0 * 2 + (inner ? -hThickness : hThickness))), + height: Math.Max(0, Math.Min(bbox.Height * 2 * vRatio, cr0 * 2 + (inner ? -vThickness : vThickness))) + ); + var result = AlignCorner(bbox, corner, size); + + return result; + } + + /// + /// Arrange a size on the bounding- so that the borders neighboring + /// to the are overlapped. + /// + /// The resulting rect can be outside of the bounding-box. + private static Rect AlignCorner(Rect bbox, Corner corner, Size size) + { + // note: the ellipse can project outside the bounding-box + // because only a quarter needs to be constrained within. + var location = (Point)(corner switch + { + Corner.TopLeft => new(bbox.Left, bbox.Top), + Corner.TopRight => new(bbox.Left + bbox.Width - size.Width, bbox.Top), + Corner.BottomRight => new(bbox.Left + bbox.Width - size.Width, bbox.Top + bbox.Height - size.Height), + Corner.BottomLeft => new(bbox.Left, bbox.Top + bbox.Height - size.Height), + + _ => throw new ArgumentOutOfRangeException($"Invalid corner: {corner}"), + }); + + return new(location, size); + } } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs index 65752d72ce11..f9c90a87ac7b 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs @@ -1,19 +1,18 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; -namespace Microsoft.UI.Xaml.Shapes +namespace Uno.UI.Xaml.Controls; + +partial class BorderLayerRenderer { - partial class BorderLayerRenderer - { - public void UpdateLayer( - FrameworkElement owner, - Brush background, - BackgroundSizing backgroundSizing, - Thickness borderThickness, - Brush borderBrush, - CornerRadius cornerRadius, - object backgroundImage - ) - { } - } + public void UpdateLayer( + FrameworkElement owner, + Brush background, + BackgroundSizing backgroundSizing, + Thickness borderThickness, + Brush borderBrush, + CornerRadius cornerRadius, + object backgroundImage + ) + { } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs index 9dbb692a055a..af70ec3fe081 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs @@ -1,19 +1,18 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; -namespace Microsoft.UI.Xaml.Shapes +namespace Uno.UI.Xaml.Controls; + +partial class BorderLayerRenderer { - partial class BorderLayerRenderer + public void UpdateLayer( + UIElement element, + Brush background, + BackgroundSizing backgroundSizing, + Thickness borderThickness, + Brush borderBrush, + CornerRadius cornerRadius, + object image) { - public void UpdateLayer( - UIElement element, - Brush background, - BackgroundSizing backgroundSizing, - Thickness borderThickness, - Brush borderBrush, - CornerRadius cornerRadius, - object image) - { - } } } diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.iOS.cs index d90c6f52e600..56d1790a3f26 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.iOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.iOS.cs @@ -6,6 +6,7 @@ using Uno.UI.DataBinding; using UIKit; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Page/Page.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/Page/Page.iOS.cs index 0a7e8297fea6..43f45fa90ea0 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Page/Page.iOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Page/Page.iOS.cs @@ -6,6 +6,7 @@ using Uno.UI.DataBinding; using UIKit; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.iOS.cs index 7fc5aecb102c..c3ce823291bc 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.iOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.iOS.cs @@ -3,6 +3,7 @@ using Uno.UI.Xaml.Media; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { From 93f0709f5b33741dfa3f0645aa546e08a1451d92 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Mon, 5 Feb 2024 15:55:33 +0100 Subject: [PATCH 05/12] chore: Android adjustments to namespace change --- src/Uno.UI/UI/Xaml/Controls/Border/Border.Android.cs | 1 + .../UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs | 2 ++ .../Xaml/Controls/ContentPresenter/ContentPresenter.Android.cs | 1 + src/Uno.UI/UI/Xaml/Controls/Page/Page.Android.cs | 1 + src/Uno.UI/UI/Xaml/Controls/Panel/Panel.Android.cs | 1 + 5 files changed, 6 insertions(+) diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/Border.Android.cs b/src/Uno.UI/UI/Xaml/Controls/Border/Border.Android.cs index 826995fc8a7b..ae6049c969b0 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/Border.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/Border.Android.cs @@ -13,6 +13,7 @@ using Font = Android.Graphics.Typeface; using Android.Graphics; using Android.Views; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs index 4912c69b90fd..c5306f069813 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs @@ -17,6 +17,8 @@ using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; using Rect = Windows.Foundation.Rect; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; namespace Uno.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.Android.cs index 69aaca92db50..cc7c457952ec 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.Android.cs @@ -15,6 +15,7 @@ using System.Drawing; using System.Linq; using Uno.UI; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Page/Page.Android.cs b/src/Uno.UI/UI/Xaml/Controls/Page/Page.Android.cs index f6796506d400..cf9e3a47e4b9 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Page/Page.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Page/Page.Android.cs @@ -15,6 +15,7 @@ using System.Drawing; using System.Linq; using Uno.UI; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.Android.cs b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.Android.cs index 70faf0f6a685..85da5c33b207 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.Android.cs @@ -16,6 +16,7 @@ using Android.Graphics.Drawables; using Uno.UI; using Microsoft.UI.Xaml.Media; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { From 65bafd8912fc125e18481f4262340eaec37af108 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Mon, 5 Feb 2024 16:11:01 +0100 Subject: [PATCH 06/12] chore: Adjustments for namespace change for the remaining platforms --- .../UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs | 1 - .../UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs | 3 ++- .../UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs | 3 ++- src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs | 2 +- src/Uno.UI/UI/Xaml/FrameworkElement.Interface.wasm.cs | 1 + src/Uno.UI/UI/Xaml/FrameworkElement.wasm.cs | 1 + 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs index a1084aaaaecb..11db2aa36ac2 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs @@ -18,7 +18,6 @@ using ObjCRuntime; using Microsoft.UI.Xaml; - #if __IOS__ using UIKit; using _View = UIKit.UIView; diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs index f9c90a87ac7b..940da385aae9 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.reference.cs @@ -1,4 +1,5 @@ -using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; namespace Uno.UI.Xaml.Controls; diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs index af70ec3fe081..014edacc1b03 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.unittests.cs @@ -1,4 +1,5 @@ -using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; namespace Uno.UI.Xaml.Controls; diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs index e1e354d05d21..701fd8746025 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs @@ -1,12 +1,12 @@ using System; using System.Linq; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using Uno.Disposables; using Uno.Extensions; using Uno.UI.Xaml; using Uno.UI.Xaml.Media; - using RadialGradientBrush = Microsoft/* UWP don't rename */.UI.Xaml.Media.RadialGradientBrush; using Uno; using Uno.UI.Helpers; diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.Interface.wasm.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.Interface.wasm.cs index 7bf2b266aa31..b1b9e0663a89 100644 --- a/src/Uno.UI/UI/Xaml/FrameworkElement.Interface.wasm.cs +++ b/src/Uno.UI/UI/Xaml/FrameworkElement.Interface.wasm.cs @@ -16,6 +16,7 @@ using Uno.UI.Xaml; using Uno.Foundation.Logging; using Uno.UI.Helpers; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml { diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.wasm.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.wasm.cs index 3d0a6873a8ff..8746f8cad2b3 100644 --- a/src/Uno.UI/UI/Xaml/FrameworkElement.wasm.cs +++ b/src/Uno.UI/UI/Xaml/FrameworkElement.wasm.cs @@ -18,6 +18,7 @@ using Windows.UI; using System.Dynamic; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml { From 54c620900fca13530acc0a211bb4b64c853f5e6f Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Mon, 5 Feb 2024 16:16:47 +0100 Subject: [PATCH 07/12] chore: Avoid code duplication for CalendarViewBaseItem --- ...lendarViewBaseItem.IBorderInfoProvider.cs} | 7 ++++ .../CalendarViewBaseItemChrome.uno.cs | 33 +++---------------- 2 files changed, 12 insertions(+), 28 deletions(-) rename src/Uno.UI/UI/Xaml/Controls/CalendarView/{CalendarViewBaseItemChrome.IBorderInfoProvider.cs => CalendarViewBaseItem.IBorderInfoProvider.cs} (79%) diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.IBorderInfoProvider.cs similarity index 79% rename from src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs rename to src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.IBorderInfoProvider.cs index 2852a9a08031..cedbaa161175 100644 --- a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.IBorderInfoProvider.cs +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItem.IBorderInfoProvider.cs @@ -10,6 +10,13 @@ namespace Microsoft.UI.Xaml.Controls; partial class CalendarViewBaseItem : IBorderInfoProvider { + // DrawBackground => General background for all items + // DrawControlBackground => Control.Background customized by the apps (can be customized in the element changing event) + // DrawDensityBar => Not supported yet + // DrawFocusBorder => Not supported yet + // OR DrawBorder => Draws the border ... + // DrawInnerBorder => The today / selected state + Brush IBorderInfoProvider.Background { get diff --git a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs index 50a6eab7bf29..8d77a12c7531 100644 --- a/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs +++ b/src/Uno.UI/UI/Xaml/Controls/CalendarView/CalendarViewBaseItemChrome.uno.cs @@ -85,34 +85,11 @@ private void UpdateChrome() // OR DrawBorder => Draws the border ... // DrawInnerBorder => The today / selected state - var background = Background; - var borderThickness = GetItemBorderThickness(); - var borderBrush = GetItemBorderBrush(forFocus: false); - var cornerRadius = GetItemCornerRadius(); - - if (IsClear(background)) - { - if (FindTodaySelectedBackgroundBrush() is { } todaySelectedBackground - && !IsClear(todaySelectedBackground)) - { - background = todaySelectedBackground; - } - else if (FindSelectedBackgroundBrush() is { } selectedBackground - && !IsClear(selectedBackground)) - { - background = selectedBackground; - } - else - { - background = GetItemBackgroundBrush(); - } - } - - if (m_isToday && m_isSelected && GetItemInnerBorderBrush() is { } selectedBrush) - { - // We don't support inner border yet, so even if not optimal we just use it as border. - borderBrush = selectedBrush; - } + var borderInfoProvider = (IBorderInfoProvider)this; + var background = borderInfoProvider.Background; + var borderThickness = borderInfoProvider.BorderThickness; + var borderBrush = borderInfoProvider.BorderBrush; + var cornerRadius = borderInfoProvider.CornerRadius; #if __WASM__ if (borderBrush is not null) From ffdbc03dabf1b51d37639458e48b1bf7d5fc5942 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Mon, 5 Feb 2024 22:21:14 +0100 Subject: [PATCH 08/12] chore: Adjust for macOS --- .../UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs | 1 + src/Uno.UI/UI/Xaml/Controls/Page/Page.macOS.cs | 1 + src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs index 84f3902155bb..1f84907c2d7d 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs @@ -5,6 +5,7 @@ using Uno.UI; using Uno.UI.DataBinding; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Page/Page.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/Page/Page.macOS.cs index 9f459ebc00a0..2a54153abe29 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Page/Page.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Page/Page.macOS.cs @@ -5,6 +5,7 @@ using Uno.UI; using Uno.UI.DataBinding; using Microsoft.UI.Xaml.Shapes; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { diff --git a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs index f0dc4d1408c5..2060da30e16b 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs @@ -10,6 +10,7 @@ using CoreGraphics; using AppKit; +using Uno.UI.Xaml.Controls; namespace Microsoft.UI.Xaml.Controls { From 6a32079b9139a06cea7d1765ff59e2f94d8170cd Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Tue, 6 Feb 2024 09:01:39 +0100 Subject: [PATCH 09/12] chore: Adjust Gtk --- src/Uno.UI.Runtime.Skia.Gtk/UI/Controls/GtkTextBoxView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Uno.UI.Runtime.Skia.Gtk/UI/Controls/GtkTextBoxView.cs b/src/Uno.UI.Runtime.Skia.Gtk/UI/Controls/GtkTextBoxView.cs index 15d72ff310a4..939b3bc09de7 100644 --- a/src/Uno.UI.Runtime.Skia.Gtk/UI/Controls/GtkTextBoxView.cs +++ b/src/Uno.UI.Runtime.Skia.Gtk/UI/Controls/GtkTextBoxView.cs @@ -12,7 +12,7 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using Uno.UI.Runtime.Skia.Gtk.Extensions; -using static Microsoft.UI.Xaml.Shapes.BorderLayerRenderer; +using static Uno.UI.Xaml.Controls.BorderLayerRenderer; using GtkWindow = Gtk.Window; using Uno.UI.Runtime.Skia.Gtk.Helpers.Dpi; using Windows.Graphics.Display; From 0845300159e74e8b8322cd253a0ef842d2108646 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Tue, 6 Feb 2024 14:35:01 +0100 Subject: [PATCH 10/12] chore: Avoid reacting to CompositionBrushProperty changes --- src/Uno.UI/UI/Xaml/Media/Brush.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Uno.UI/UI/Xaml/Media/Brush.cs b/src/Uno.UI/UI/Xaml/Media/Brush.cs index 7a98190c5adc..bf8114e9019a 100644 --- a/src/Uno.UI/UI/Xaml/Media/Brush.cs +++ b/src/Uno.UI/UI/Xaml/Media/Brush.cs @@ -69,7 +69,7 @@ internal static void SetupBrushChanged(Brush? oldValue, Brush? newValue, ref Act internal virtual void OnPropertyChanged2(DependencyPropertyChangedEventArgs args) { - if (args.Property == DataContextProperty || args.Property == TemplatedParentProperty) + if (args.Property == DataContextProperty || args.Property == TemplatedParentProperty || args.Property == XamlCompositionBrushBase.CompositionBrushProperty) { return; } From bd09142109d64d967b2a92e0f9ff3f461bae1446 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Wed, 7 Feb 2024 16:29:53 +0100 Subject: [PATCH 11/12] chore: Do not generalize listening to brushes --- .../Controls/Border/BorderLayerRenderer.cs | 53 +------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs index 0d09ee74e1d9..3f2fbe6cac10 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.cs @@ -1,10 +1,6 @@ #if __SKIA__ using System; -using System.Collections.Generic; -using System.Text; -using Windows.Foundation; using Microsoft.UI.Xaml; -using Uno.UI; using Uno.Disposables; namespace Uno.UI.Xaml.Controls; @@ -16,8 +12,6 @@ internal partial class BorderLayerRenderer { private readonly FrameworkElement _owner; private readonly IBorderInfoProvider _borderInfoProvider; - private readonly SerialDisposable _borderBrushSubscription = new(); - private readonly SerialDisposable _backgroundBrushSubscription = new(); private BorderLayerState _currentState; @@ -43,59 +37,14 @@ internal void Update() { if (_owner.IsLoaded) { - // Subscribe to brushes to observe their changes. - if (_currentState.BorderBrush != _borderInfoProvider.BorderBrush) - { - _borderBrushSubscription.Disposable = null; - if (_borderInfoProvider.BorderBrush is { } borderBrush) - { - borderBrush.InvalidateRender += OnBorderBrushChanged; - _borderBrushSubscription.Disposable = Disposable.Create(() => borderBrush.InvalidateRender -= OnBorderBrushChanged); - } - } - - if (_currentState.Background != _borderInfoProvider.Background) - { - _backgroundBrushSubscription.Disposable = null; - if (_borderInfoProvider.Background is { } backgroundBrush) - { - backgroundBrush.InvalidateRender += OnBackgroundBrushChanged; - _backgroundBrushSubscription.Disposable = Disposable.Create(() => backgroundBrush.InvalidateRender -= OnBackgroundBrushChanged); - } - } - UpdatePlatform(); } } - private void OnBorderBrushChanged() - { - // Force the border to be recreated during update. - _currentState.BorderBrush = null; - Update(); - } - - private void OnBackgroundBrushChanged() - { - // Force the background to be recreated during update. - _currentState.Background = null; - Update(); - } - /// /// Removes added layers and subscriptions. /// - internal void Clear() - { - UnsubscribeBrushChanges(); - ClearPlatform(); - } - - private void UnsubscribeBrushChanges() - { - _borderBrushSubscription.Disposable = null; - _backgroundBrushSubscription.Disposable = null; - } + internal void Clear() => ClearPlatform(); partial void UpdatePlatform(); From 84f8575e45e7bbac272dd6edb0b823c2cd23ee90 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Wed, 7 Feb 2024 16:34:04 +0100 Subject: [PATCH 12/12] fix: Background image should not adjust area --- .../Border/BorderLayerRenderer.skia.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs index 10fb94fa6edf..8c4ec110fb06 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs @@ -47,20 +47,15 @@ partial void UpdatePlatform() if (!newState.Equals(previousLayoutState)) { - if ( - newState.Background != null || + _layerDisposable.Disposable = null; + + if (newState.Background != null || newState.CornerRadius != CornerRadius.None || - (newState.BorderThickness != Thickness.Empty && newState.BorderBrush != null) - ) + (newState.BorderThickness != Thickness.Empty && newState.BorderBrush != null)) { - _layerDisposable.Disposable = null; _layerDisposable.Disposable = InnerCreateLayer(_owner, newState); } - else - { - _layerDisposable.Disposable = null; - } _currentState = newState; } @@ -112,10 +107,9 @@ private static IDisposable InnerCreateLayer(UIElement owner, BorderLayerState st { var backgroundShape = compositor.CreateSpriteShape(); - // First we set the brush as it might alter the adjustedArea if (background is ImageBrush imgBackground) { - adjustedArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, adjustedArea, imgBackground); + CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, adjustedArea, imgBackground); } else { @@ -283,7 +277,7 @@ void CreateLayer(Action builder, string name) return disposables; } - private static Rect CreateImageLayer(Compositor compositor, CompositeDisposable disposables, Thickness borderThickness, Rect adjustedArea, CompositionSpriteShape backgroundShape, Rect backgroundArea, ImageBrush imgBackground) + private static void CreateImageLayer(Compositor compositor, CompositeDisposable disposables, Thickness borderThickness, Rect adjustedArea, CompositionSpriteShape backgroundShape, Rect backgroundArea, ImageBrush imgBackground) { Action onInvalidateRender = () => { @@ -325,7 +319,6 @@ private static Rect CreateImageLayer(Compositor compositor, CompositeDisposable onInvalidateRender(); imgBackground.InvalidateRender += onInvalidateRender; new DisposableAction(() => imgBackground.InvalidateRender -= onInvalidateRender).DisposeWith(disposables); - return backgroundArea; } private static CompositionPath GetBackgroundPath(SKPoint[] radii, Rect area)