diff --git a/src/Uno.UI/Extensions/ThicknessExtensions.cs b/src/Uno.UI/Extensions/ThicknessExtensions.cs index 2e2cbf731cb5..76e196c4af61 100644 --- a/src/Uno.UI/Extensions/ThicknessExtensions.cs +++ b/src/Uno.UI/Extensions/ThicknessExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Windows.Foundation; using Windows.UI.Xaml; namespace Uno.UI.Extensions @@ -17,5 +18,11 @@ public static bool IsUniform(this Thickness thickness) public static Thickness Minus(this Thickness x, Thickness y) // Minus ==> There is a (not implemented) Substract on struct in mono! => new Thickness(x.Left - y.Left, x.Top - y.Top, x.Right - y.Right, x.Bottom - y.Bottom); + + public static double Horizontal(this Thickness thickness) + => thickness.Left + thickness.Right; + + public static double Vertical(this Thickness thickness) + => thickness.Top + thickness.Bottom; } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Layouter/ISetLayoutSlots.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/Layouter/ISetLayoutSlots.iOS.cs new file mode 100644 index 000000000000..a0c14953c415 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/Layouter/ISetLayoutSlots.iOS.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Uno.UI.UI.Xaml.Controls.Layouter +{ + internal interface ISetLayoutSlots + { + // On iOS lots of elements does not use the Layouter to measure themselves and their children, + // doing this the are bypassing some important steps, including setting LayoutInformation.LayoutSlot. + + // This marker interface is designed to flag elements that does not use the Layouter, + // but which are still taking care to update that LayoutInformation.LayoutSlot for its children. + // (The LayoutSlot is set by parent on its children !). + + // This is to avoid massive refactoring of existing elements and MUST NOT be used in new development, + // use the Layouter instead! + + // Note: For future dev, we might need to add an equivalent marker interface like IUseLayouter! + } +} diff --git a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseScrollContentPresenter.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseScrollContentPresenter.iOS.cs index 8e0e27fb7d95..4988d216f26b 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseScrollContentPresenter.iOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseScrollContentPresenter.iOS.cs @@ -10,7 +10,7 @@ namespace Windows.UI.Xaml.Controls { - public sealed partial class ListViewBaseScrollContentPresenter : ContentPresenter, IScrollContentPresenter + public sealed partial class ListViewBaseScrollContentPresenter : IScrollContentPresenter { public CGPoint UpperScrollLimit => NativePanel?.UpperScrollLimit ?? CGPoint.Empty; @@ -32,9 +32,12 @@ public void SetZoomScale(nfloat scale, bool animated) NativePanel?.SetZoomScale(scale, animated); } - public Rect MakeVisible(UIElement visual, Rect rectangle) - { - return rectangle; - } + bool INativeScrollContentPresenter.Set( + double? horizontalOffset, + double? verticalOffset, + float? zoomFactor, + bool disableAnimation, + bool isIntermediate) + => throw new NotImplementedException(); } } diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/INativeScrollContentPresenter.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/INativeScrollContentPresenter.cs index 870ab5c7dbab..24bbd7606ead 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/INativeScrollContentPresenter.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/INativeScrollContentPresenter.cs @@ -13,8 +13,6 @@ internal interface INativeScrollContentPresenter object Content { get; set; } - Thickness Padding { get; set; } - bool Set( double? horizontalOffset = null, double? verticalOffset = null, @@ -23,6 +21,9 @@ bool Set( bool isIntermediate = false); #if __ANDROID__ + // Padding used by the SIP + Thickness Padding { get; set; } + // To avoid massive refactor DO NOT USE, use 'Set' instead void SmoothScrollBy(int physicalDeltaX, int physicalDeltaY); #endif diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Xamarin.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Xamarin.cs index 7c5dafb14941..04dd8ab1a33b 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Xamarin.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.Xamarin.cs @@ -46,7 +46,7 @@ public bool CanVerticallyScroll public object Content { - get { return _content; } + get => _content; set { var previousView = _content; diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.iOS.cs index 5b40a8a6343f..f3555137e214 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.iOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.iOS.cs @@ -5,7 +5,6 @@ using Windows.UI.Xaml.Data; using System; using System.Collections.Generic; -using System.Drawing; using Uno.Disposables; using System.Runtime.CompilerServices; using System.Text; @@ -16,12 +15,15 @@ using Uno.Logging; using Windows.Foundation; using Windows.UI.Input; +using Windows.UI.Xaml.Controls.Primitives; +using Uno.UI; +using Uno.UI.UI.Xaml.Controls.Layouter; using Uno.UI.Xaml.Input; using DraggingEventArgs = UIKit.DraggingEventArgs; namespace Windows.UI.Xaml.Controls { - partial class NativeScrollContentPresenter : UIScrollView, DependencyObject + partial class NativeScrollContentPresenter : UIScrollView, DependencyObject, ISetLayoutSlots { private readonly WeakReference _scrollViewer; @@ -165,11 +167,7 @@ public override void SetNeedsLayout() base.SetNeedsLayout(); _requiresMeasure = true; - - if (Superview != null) - { - Superview.SetNeedsLayout(); - } + Superview?.SetNeedsLayout(); } #region Layouting @@ -244,46 +242,57 @@ public override void LayoutSubviews() { try { - if (Content != null) + if (_content is null) { - if (_requiresMeasure) - { - _requiresMeasure = false; - SizeThatFits(Frame.Size); - } + return; + } - double horizontalMargin = 0; - double verticalMargin = 0; + var frame = Frame; + if (_requiresMeasure) + { + _requiresMeasure = false; + SizeThatFits(frame.Size); + } - var frameworkElement = _content as IFrameworkElement; + var contentMargin = default(Thickness); + if (_content is IFrameworkElement iFwElt) + { + contentMargin = iFwElt.Margin; - if (frameworkElement != null) - { - horizontalMargin = frameworkElement.Margin.Left + frameworkElement.Margin.Right; - verticalMargin = frameworkElement.Margin.Top + frameworkElement.Margin.Bottom; + var adjustedMeasure = new CGSize( + GetAdjustedArrangeWidth(iFwElt, (nfloat)contentMargin.Horizontal()), + GetAdjustedArrangeHeight(iFwElt, (nfloat)contentMargin.Vertical()) + ); - var adjustedMeasure = new CGSize( - GetAdjustedArrangeWidth(frameworkElement, (nfloat)horizontalMargin), - GetAdjustedArrangeHeight(frameworkElement, (nfloat)verticalMargin) + // Zoom works by applying a transform to the child view. If a view has a non-identity transform, its Frame shouldn't be set. + if (ZoomScale == 1) + { + _content.Frame = new CGRect( + GetAdjustedArrangeX(iFwElt, adjustedMeasure, (nfloat)contentMargin.Horizontal()), + GetAdjustedArrangeY(iFwElt, adjustedMeasure, (nfloat)contentMargin.Vertical()), + adjustedMeasure.Width, + adjustedMeasure.Height ); - - // Zoom works by applying a transform to the child view. If a view has a non-identity transform, its Frame shouldn't be set. - if (ZoomScale == 1) - { - _content.Frame = new CGRect( - GetAdjustedArrangeX(frameworkElement, adjustedMeasure, horizontalMargin), - GetAdjustedArrangeY(frameworkElement, adjustedMeasure, verticalMargin), - adjustedMeasure.Width, - adjustedMeasure.Height - ); - } } + } - ContentSize = AdjustContentSize(_content.Frame.Size + new CGSize(horizontalMargin, verticalMargin)); + // Sets the scroll extents using the effective Frame of the content + // (which might be different than the frame set above if the '_content' has some layouting constraints). + // Noticeably, it will be the case if the '_content' is bigger than the viewport. + var finalRect = (Rect)_content.Frame; + var extentSize = AdjustContentSize(finalRect.InflateBy(contentMargin).Size); - // This prevents unnecessary touch delays (which affects the pressed visual states of buttons) when user can't scroll. - UpdateDelayedTouches(); + ContentSize = extentSize; + + // ISetLayoutSlots contract implementation + LayoutInformation.SetLayoutSlot(_content, new Rect(default, ((Size)extentSize).AtLeast(frame.Size))); + if (_content is UIElement uiElt) + { + uiElt.LayoutSlotWithMarginsAndAlignments = finalRect; } + + // This prevents unnecessary touch delays (which affects the pressed visual states of buttons) when user can't scroll. + UpdateDelayedTouches(); } catch (Exception e) { @@ -499,11 +508,13 @@ private nfloat GetMeasureValue(nfloat value, ScrollBarVisibility scrollBarVisibi } #endregion - public Rect MakeVisible(UIElement visual, Rect rectangle) - { - ScrollViewExtensions.BringIntoView(this, visual, BringIntoViewMode.ClosestEdge); - return rectangle; - } + bool INativeScrollContentPresenter.Set( + double? horizontalOffset, + double? verticalOffset, + float? zoomFactor, + bool disableAnimation, + bool isIntermediate) + => throw new NotImplementedException(); #region Touches diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Android.cs index da31f13e9fb7..6e857d2cacf0 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Android.cs @@ -10,11 +10,9 @@ namespace Windows.UI.Xaml.Controls { partial class ScrollContentPresenter { - public ScrollContentPresenter() + partial void InitializePartial() { PointerMoved += OnPointerMoved; - - RegisterAsScrollPort(this); } private void OnPointerMoved(object sender, Input.PointerRoutedEventArgs e) @@ -35,6 +33,8 @@ private void OnPointerMoved(object sender, Input.PointerRoutedEventArgs e) public Rect MakeVisible(UIElement visual, Rect rectangle) { + // NOTE: We should re-use the computation of the offsets and then use the Set + if (visual is FrameworkElement fe && !(Native is null)) { var scrollRect = new Rect( @@ -86,20 +86,5 @@ private void SetOccludedRectPadding(Thickness occludedRectPadding) Native.Padding = occludedRectPadding; } #endregion - - #region Native SCP to SCP - internal void OnNativeScroll(double horizontalOffset, double verticalOffset, bool isIntermediate) - { - Scroller?.OnPresenterScrolled(horizontalOffset, verticalOffset, isIntermediate); - - ScrollOffsets = new Point(horizontalOffset, verticalOffset); - InvalidateViewport(); - } - - internal void OnNativeZoom(float zoomFactor) - { - Scroller?.OnPresenterZoomed(zoomFactor); - } - #endregion } } diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs index 6b3a26327306..c0161feaa145 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs @@ -74,7 +74,7 @@ private static double GetHorizontalScrollWheelDelta(Size size, double delta) // an appropriate size. const double ScrollViewerMinHeightToReflowAroundOcclusions = 32.0f; - private readonly IScrollStrategy _strategy; + private /*readonly - partial*/ IScrollStrategy _strategy; public bool CanHorizontallyScroll { get; set; } @@ -88,7 +88,7 @@ private static double GetHorizontalScrollWheelDelta(Size size, double delta) internal Size? CustomContentExtent => null; - public ScrollContentPresenter() + partial void InitializePartial() { #if __SKIA__ _strategy = CompositorScrollStrategy.Instance; @@ -106,10 +106,6 @@ public ScrollContentPresenter() ManipulationStarting += PrepareTouchScroll; ManipulationDelta += UpdateTouchScroll; ManipulationCompleted += CompleteTouchScroll; - - // On Skia and macOS (as UWP), the Scrolling is managed by the ScrollContentPresenter, not the ScrollViewer. - // Note: This as direct consequences in UIElement.GetTransform and VisualTreeHelper.SearchDownForTopMostElementAt - RegisterAsScrollPort(this); } public void SetVerticalOffset(double offset) diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Native.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Native.cs new file mode 100644 index 000000000000..c7f6be340208 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Native.cs @@ -0,0 +1,80 @@ +using Windows.Foundation; +using Uno.UI.Extensions; + +#if !UNO_HAS_MANAGED_SCROLL_PRESENTER && !__WASM__ + +namespace Windows.UI.Xaml.Controls +{ + // This file only contains support of NativeScrollContentPresenter + + partial class ScrollContentPresenter + { + internal INativeScrollContentPresenter Native { get; set; } + + private object RealContent => Native?.Content; + + #region SCP to Native SCP + public ScrollBarVisibility NativeHorizontalScrollBarVisibility + { + set + { + if (Native is { } native) + { + native.HorizontalScrollBarVisibility = value; + } + } + } + + public ScrollBarVisibility NativeVerticalScrollBarVisibility + { + set + { + if (Native is { } native) + { + native.VerticalScrollBarVisibility = value; + } + } + } + + public bool CanHorizontallyScroll + { + get => Native?.CanHorizontallyScroll ?? false; + set + { + if (Native is { } native) + { + native.CanHorizontallyScroll = value; + } + } + } + + public bool CanVerticallyScroll + { + get => Native?.CanVerticallyScroll ?? false; + set + { + if (Native is { } native) + { + native.CanVerticallyScroll = value; + } + } + } + #endregion + + #region Native SCP to SCP + internal void OnNativeScroll(double horizontalOffset, double verticalOffset, bool isIntermediate) + { + Scroller?.OnPresenterScrolled(horizontalOffset, verticalOffset, isIntermediate); + + ScrollOffsets = new Point(horizontalOffset, verticalOffset); + InvalidateViewport(); + } + + internal void OnNativeZoom(float zoomFactor) + { + Scroller?.OnPresenterZoomed(zoomFactor); + } + #endregion + } +} +#endif diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.cs index c41a9b61e031..e754feebcc20 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.cs @@ -30,6 +30,13 @@ namespace Windows.UI.Xaml.Controls { public partial class ScrollContentPresenter : ContentPresenter, ILayoutConstraints { + public ScrollContentPresenter() + { + InitializePartial(); + RegisterAsScrollPort(this); + } + partial void InitializePartial(); + #region ScrollOwner private ManagedWeakReference _scroller; @@ -75,56 +82,6 @@ private void OnParentChanged(object instance, object key, DependencyObjectParent } } -#if __IOS__ || __ANDROID__ - internal INativeScrollContentPresenter Native { get; set; } - private object RealContent => Native?.Content; - - public ScrollBarVisibility NativeHorizontalScrollBarVisibility - { - set - { - if (Native is { } native) - { - native.HorizontalScrollBarVisibility = value; - } - } - } - - public ScrollBarVisibility NativeVerticalScrollBarVisibility - { - set - { - if (Native is { } native) - { - native.VerticalScrollBarVisibility = value; - } - } - } - public bool CanHorizontallyScroll - { - get => Native?.CanHorizontallyScroll ?? false; - set - { - if (Native is { } native) - { - native.CanHorizontallyScroll = value; - } - } - } - - public bool CanVerticallyScroll - { - get => Native?.CanVerticallyScroll ?? false; - set - { - if (Native is { } native) - { - native.CanVerticallyScroll = value; - } - } - } -#endif - bool ILayoutConstraints.IsWidthConstrained(View requester) { if (requester != null && CanHorizontallyScroll) @@ -279,5 +236,5 @@ protected override Size ArrangeOverride(Size finalSize) return result; } #endif - } - } + } +} diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.iOS.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.iOS.cs new file mode 100644 index 000000000000..157b3a7d087e --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.iOS.cs @@ -0,0 +1,24 @@ +using Windows.Foundation; +using Uno.UI.Extensions; + +namespace Windows.UI.Xaml.Controls +{ + partial class ScrollContentPresenter + { + public Rect MakeVisible(UIElement visual, Rect rectangle) + { + // NOTE: We should re-use the computation of the offsets and then use the Set + + if (Native is UIKit.UIScrollView nativeScroller) + { + ScrollViewExtensions.BringIntoView(nativeScroller, visual, BringIntoViewMode.ClosestEdge); + } + else if (Native is ListViewBaseScrollContentPresenter lv) + { + ScrollViewExtensions.BringIntoView(lv.NativePanel, visual, BringIntoViewMode.ClosestEdge); + } + + return rectangle; + } + } +} diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.wasm.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.wasm.cs index b299be72af1d..f0bdba96702b 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.wasm.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.wasm.cs @@ -35,11 +35,6 @@ internal Size ScrollBarSize } } - public ScrollContentPresenter() - { - RegisterAsScrollPort(this); - } - private void TryRegisterEvents(ScrollBarVisibility visibility) { if ( diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollViewer/ScrollViewer.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollViewer/ScrollViewer.cs index 21b151276ccd..31eefbd16632 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollViewer/ScrollViewer.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollViewer/ScrollViewer.cs @@ -2,10 +2,6 @@ #pragma warning disable CS0067 #endif -#if !UNO_HAS_MANAGED_SCROLL_PRESENTER && !__WASM__ && !__ANDROID__ -#define IS_SCROLL_PORT -#endif - #nullable enable using System; @@ -123,13 +119,6 @@ public ScrollViewer() { DefaultStyleKey = typeof(ScrollViewer); -#if IS_SCROLL_PORT - // On Skia, the Scrolling is managed by the ScrollContentPresenter (as UWP), which is flagged as IsScrollPort. - // Note: We should still add support for the zoom factor ... which is not yet supported on Skia. - // Note 2: This as direct consequences in UIElement.GetTransform and VisualTreeHelper.SearchDownForTopMostElementAt - UIElement.RegisterAsScrollPort(this); -#endif - UpdatesMode = Uno.UI.Xaml.Controls.ScrollViewer.GetUpdatesMode(this); InitializePartial(); @@ -1297,12 +1286,6 @@ private void Update(bool isIntermediate) UpdatePartial(isIntermediate); -#if IS_SCROLL_PORT - // Effective viewport support - ScrollOffsets = new Point(_pendingHorizontalOffset, _pendingVerticalOffset); - InvalidateViewport(); -#endif - ViewChanged?.Invoke(this, new ScrollViewerViewChangedEventArgs { IsIntermediate = isIntermediate }); } diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.EffectiveViewport.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.EffectiveViewport.cs index 0b7bd20b0ce2..21ce058f456c 100644 --- a/src/Uno.UI/UI/Xaml/FrameworkElement.EffectiveViewport.cs +++ b/src/Uno.UI/UI/Xaml/FrameworkElement.EffectiveViewport.cs @@ -2,6 +2,12 @@ // #define TRACE_EFFECTIVE_VIEWPORT +#if !(IS_NATIVE_ELEMENT && __IOS__) +// On iOS lots of native elements are not using the Layouter and will never invoke the IFrameworkElement_EffectiveViewport.OnLayoutUpdated() +// so avoid check of the '_isLayouted' flag +#define CHECK_LAYOUTED +#endif + using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,7 +37,9 @@ partial class FrameworkElement : IFrameworkElement_EffectiveViewport private IDisposable? _parentViewportUpdatesSubscription; private ViewportInfo _parentViewport = ViewportInfo.Empty; // WARNING: Stored in parent's coordinates space, use GetParentViewport() private ViewportInfo _lastEffectiveViewport; +#if CHECK_LAYOUTED private bool _isLayouted; +#endif public event TypedEventHandler<_This, EffectiveViewportChangedEventArgs> EffectiveViewportChanged { @@ -99,10 +107,12 @@ private void ReconfigureViewportPropagation(bool isInternal = false, IFrameworkE } else { +#if CHECK_LAYOUTED if (!IsLoaded) { _isLayouted = false; } +#endif if (_parentViewportUpdatesSubscription != null) { @@ -167,7 +177,9 @@ void IFrameworkElement_EffectiveViewport.OnParentViewportChanged( // Native elements cannot be clipped (using Uno), so the _localViewport will always be an empty rect, and we only react to LayoutSlot updates void IFrameworkElement_EffectiveViewport.OnLayoutUpdated() { +#if CHECK_LAYOUTED _isLayouted = true; +#endif PropagateEffectiveViewportChange(); } #else @@ -278,11 +290,13 @@ private void PropagateEffectiveViewportChange( return; } +#if CHECK_LAYOUTED if (!_isLayouted) // For perf consideration, we try to not propagate the VP until teh element get a valid LayoutSlot { TRACE_EFFECTIVE_VIEWPORT($"Element has been layouted yet, stop propagation (reason:{caller} | isInitial:{isInitial} | isInternal:{isInternal})."); return; } +#endif var parentViewport = GetParentViewport(); var viewport = GetEffectiveViewport(parentViewport); diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs index a68f13a7ceb4..f58495c67a5a 100644 --- a/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs +++ b/src/Uno.UI/UI/Xaml/FrameworkElement.iOS.cs @@ -7,8 +7,10 @@ using UIKit; using System.ComponentModel; using Windows.Foundation; +using Windows.UI.Xaml.Controls.Primitives; using Uno.Logging; using Uno.UI; +using Uno.UI.UI.Xaml.Controls.Layouter; namespace Windows.UI.Xaml { @@ -60,7 +62,23 @@ public override void LayoutSubviews() OnBeforeArrange(); - var finalRect = Superview is UIElement ? LayoutSlotWithMarginsAndAlignments : RectFromUIRect(Frame); + Rect finalRect; + var parent = Superview; + if (parent is UIElement || parent is ISetLayoutSlots) + { + finalRect = LayoutSlotWithMarginsAndAlignments; + } + else + { + // Here the "arrange" is coming from a native element, + // so we convert those measurements to logical ones. + finalRect = RectFromUIRect(Frame); + + // We also need to set the LayoutSlot as it was not by the parent. + // Note: This is only an approximation of the LayoutSlot as margin and alignment might already been applied at this point. + LayoutInformation.SetLayoutSlot(this, finalRect); + LayoutSlotWithMarginsAndAlignments = finalRect; + } _layouter.Arrange(finalRect); diff --git a/src/Uno.UI/UI/Xaml/UIElement.cs b/src/Uno.UI/UI/Xaml/UIElement.cs index 452c85392750..82424feff1ef 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.cs @@ -344,31 +344,20 @@ internal static Matrix3x2 GetTransform(UIElement from, UIElement to) offsetY = layoutSlot.Y; } -#if !UNO_HAS_MANAGED_SCROLL_PRESENTER && !__WASM__ && !__ANDROID__ - // On Skia, the Scrolling is managed by the ScrollContentPresenter (as UWP), which is flagged as IsScrollPort. - // Note: We should still add support for the zoom factor ... which is not yet supported on Skia. if (elt is ScrollViewer sv // Don't adjust for scroll offsets if it's the ScrollViewer itself calling TransformToVisual - && elt != from - ) + && elt != from) { + // Scroll offsets are handled at SCP level using the IsScrollPort + var zoom = sv.ZoomFactor; if (zoom != 1) { matrix *= Matrix3x2.CreateTranslation((float)offsetX, (float)offsetY); matrix *= Matrix3x2.CreateScale(zoom); - - offsetX = -sv.HorizontalOffset; - offsetY = -sv.VerticalOffset; - } - else - { - offsetX -= sv.HorizontalOffset; - offsetY -= sv.VerticalOffset; } } - else -#endif + #if !__MACOS__ // On macOS the SCP is using RenderTransforms for scrolling which has already been included. if (elt.IsScrollPort // Don't adjust for scroll offsets if it's the scroll port itself calling TransformToVisual, only for ancestors