Skip to content

Commit

Permalink
fix(effectiveviewport): Fix effective viewport computation
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Oct 6, 2021
1 parent ceba136 commit 3448722
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 124 deletions.
20 changes: 19 additions & 1 deletion src/Uno.Foundation/Rect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,24 @@ public Rect(Point point1, Point point2)

public bool IsEmpty => Empty.Equals(this);

/// <summary>
/// This indicates that this rect is equals to the <see cref="Infinite"/>.
/// Unlike the <see cref="IsFinite"/>, this **DOES NOT** indicates that the rect is infinite on at least one of its axis.
/// </summary>
internal bool IsInfinite => Infinite.Equals(this);

/// <summary>
/// This make sure that this rect does not have any infinite value on any of its axis.
/// </summary>
/// <remarks>This is **NOT** the opposite of <see cref="IsInfinite"/>.</remarks>
internal bool IsFinite => !double.IsInfinity(X) && !double.IsInfinity(Y) && !double.IsInfinity(Width) && !double.IsInfinity(Height);

/// <summary>
/// Indicates that this rect does not have any infinite or NaN on any on its axis.
/// (I.e. it's a valid rect for standard layouting logic)
/// </summary>
internal bool IsValid => IsFinite && !double.IsNaN(X) && !double.IsNaN(Y) && !double.IsNaN(Width) && !double.IsNaN(Height);

internal bool IsUniform => Math.Abs(Left - Top) < Epsilon && Math.Abs(Left - Right) < Epsilon && Math.Abs(Left - Bottom) < Epsilon;

public static implicit operator Rect(string text)
Expand Down Expand Up @@ -146,7 +162,9 @@ public static implicit operator string(Rect rect)
public override string ToString() => (string)this;

internal string ToDebugString()
=> IsEmpty ? "--empty--" : FormattableString.Invariant($"{Size.ToDebugString()}@{Location.ToDebugString()}");
=> IsEmpty ? "--empty--"
: IsInfinite ? "--infinite--"
: FormattableString.Invariant($"{Width:F2}x{Height:F2}@{Location.ToDebugString()}");

/// <summary>
/// Provides the size of this rectangle.
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/Controls/Layouter/Layouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal abstract partial class Layouter : ILayouter

internal Size _unclippedDesiredSize;

private UIElement _elementAsUIElement;
private readonly UIElement _elementAsUIElement;

public IFrameworkElement Panel { get; }

Expand Down
103 changes: 103 additions & 0 deletions src/Uno.UI/UI/Xaml/FrameworkElement.EffectiveViewport.ViewportInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#nullable enable

using System;
using System.Linq;
using Windows.Foundation;
using Uno.Extensions;

namespace Windows.UI.Xaml
{
// Internal interface used to allow communication between the real FrameworkElement
// and presenters that are only implementing the IFrameworkElement interface (cf. FrameworkElementMixins.tt).
// It must not be used anywhere else out of the file.
internal interface IFrameworkElement_EffectiveViewport
{
void InitializeEffectiveViewport();
IDisposable RequestViewportUpdates(IFrameworkElement_EffectiveViewport? child = null);
void OnParentViewportChanged(IFrameworkElement_EffectiveViewport parent, ViewportInfo viewport, bool isInitial = false);
void OnLayoutUpdated();
}

internal struct ViewportInfo : IEquatable<ViewportInfo>
{
public static ViewportInfo Empty { get; } = new ViewportInfo { Effective = Rect.Empty, Clip = Rect.Empty };

/// <summary>
/// The element used as reference coordinate space to express the values.
/// </summary>
public IFrameworkElement_EffectiveViewport? Reference;

/// <summary>
/// The public effective viewport
/// </summary>
public Rect Effective;

/// <summary>
/// The cumulative clipping applied by all parents scroll ports.
/// </summary>
public Rect Clip;

public ViewportInfo(IFrameworkElement_EffectiveViewport reference, Rect viewport)
{
Reference = reference;
Effective = Clip = viewport;
}

public ViewportInfo(IFrameworkElement_EffectiveViewport reference, Rect effective, Rect clip)
{
Reference = reference;
Effective = effective;
Clip = clip;
}

public ViewportInfo GetRelativeTo(IFrameworkElement_EffectiveViewport element)
{
Rect effective = Effective, clip = Clip;
if (element is UIElement uiElement && Reference is UIElement usuallyTheParentOfElement)
{
var parentToElement = UIElement.GetTransform(uiElement, usuallyTheParentOfElement).Inverse();

if (Effective.IsValid)
{
effective = parentToElement.Transform(Effective);
}

if (Clip.IsValid)
{
clip = parentToElement.Transform(Clip);
}
}

// If the Reference or the element is not a UIElement (native), we don't have any RenderTransform,
// and we assume that the 'element' is stretched in it's parent usually true except for ListView,
// but we won't even try to support that case.

return new ViewportInfo(element, effective, clip);
}

/// <inheritdoc />
public override string ToString()
=> $"{Effective.ToDebugString()} (clip: {Clip.ToDebugString()})";

/// <inheritdoc />
public override int GetHashCode()
=> Effective.GetHashCode();

/// <inheritdoc />
public bool Equals(ViewportInfo other)
=> Equals(this, other);

/// <inheritdoc />
public override bool Equals(object obj)
=> obj is ViewportInfo vp && Equals(this, vp);

private static bool Equals(ViewportInfo left, ViewportInfo right)
=> left.Effective.Equals(right.Effective);

public static bool operator ==(ViewportInfo left, ViewportInfo right)
=> Equals(left, right);

public static bool operator !=(ViewportInfo left, ViewportInfo right)
=> !Equals(left, right);
}
}
Loading

0 comments on commit 3448722

Please sign in to comment.