Skip to content

Commit

Permalink
fix(effectiveviewport): [iOS] The SCP is now flagged as ScrollHost
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Oct 6, 2021
1 parent 99bd1b7 commit f4b1937
Show file tree
Hide file tree
Showing 16 changed files with 248 additions and 164 deletions.
7 changes: 7 additions & 0 deletions src/Uno.UI/Extensions/ThicknessExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
}
21 changes: 21 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/Layouter/ISetLayoutSlots.iOS.cs
Original file line number Diff line number Diff line change
@@ -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!
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ internal interface INativeScrollContentPresenter

object Content { get; set; }

Thickness Padding { get; set; }

bool Set(
double? horizontalOffset = null,
double? verticalOffset = null,
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public bool CanVerticallyScroll

public object Content
{
get { return _content; }
get => _content;
set
{
var previousView = _content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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> _scrollViewer;

Expand Down Expand Up @@ -165,11 +167,7 @@ public override void SetNeedsLayout()
base.SetNeedsLayout();

_requiresMeasure = true;

if (Superview != null)
{
Superview.SetNeedsLayout();
}
Superview?.SetNeedsLayout();
}

#region Layouting
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand All @@ -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;
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit f4b1937

Please sign in to comment.