Skip to content

Commit

Permalink
fix: Adjust measure/arrange phase
Browse files Browse the repository at this point in the history
This change removes the reliance on NSView.NeedsLayout to invalidate the measure/arrange phases for self and parents. NeedsLayout is set to true when NSView.Frame is changed, causing a chain of multiple unneeded updates for the element and its parents.
  • Loading branch information
jeromelaban committed Apr 2, 2020
1 parent 210b30b commit c8f9917
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public ContentPresenter()
private void SetUpdateTemplate()
{
UpdateContentTemplateRoot();
this.SetNeedsLayout();
this.InvalidateMeasure();
}

partial void RegisterContentTemplateRoot()
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/Controls/Image/Image.macOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private void SetNeedsLayoutOrDisplay()
}
else
{
this.SetNeedsLayout();
this.InvalidateMeasure();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Windows.UI.Xaml.Controls
{
public class NativeMenuBarPresenter : FrameworkElement
public partial class NativeMenuBarPresenter : FrameworkElement
{
private MenuBar _menuBar;

Expand Down
6 changes: 3 additions & 3 deletions src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ partial void OnBorderBrushChangedPartial(Brush oldValue, Brush newValue)

partial void OnBorderThicknessChangedPartial(Thickness oldValue, Thickness newValue)
{
NeedsLayout = true;
InvalidateMeasure();
UpdateBackground();
}

partial void OnPaddingChangedPartial(Thickness oldValue, Thickness newValue)
{
NeedsLayout = true;
InvalidateMeasure();
}

protected override void OnBackgroundChanged(DependencyPropertyChangedEventArgs args)
Expand Down Expand Up @@ -114,7 +114,7 @@ private void UpdateBackground(NSImage backgroundImage = null)

protected virtual void OnChildrenChanged()
{
NeedsLayout = true;
InvalidateMeasure();
}

protected override void OnAfterArrange()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public ScrollContentPresenter()
InitializeScrollContentPresenter();

Notifications.ObserveDidLiveScroll(this, OnLiveScroll);

DrawsBackground = false;
}

public nfloat ZoomScale {
Expand Down
65 changes: 44 additions & 21 deletions src/Uno.UI/UI/Xaml/FrameworkElement.macOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,38 @@ public override bool NeedsLayout
base.NeedsLayout = value;
}

RequiresMeasure = true;
RequiresArrange = true;

if (ShouldInterceptInvalidate)
{
return;
}
}
}

protected internal override void OnInvalidateMeasure()
{
base.OnInvalidateMeasure();

// Note that the reliance on NSView.NeedsLayout to invalidate the measure / arrange phases for
// self and parents.NeedsLayout is set to true when NSView.Frame is different from iOS, causing
// a chain of multiple unneeded updates for the element and its parents. OnInvalidateMeasure
// sets NeedsLayout to true and propagates to the parent but NeedsLayout by itself does not.

SetSuperviewNeedsLayout();
if (!RequiresMeasure)
{
RequiresArrange = true;
RequiresMeasure = true;

if (Parent is FrameworkElement fe)
{
if (!fe.RequiresMeasure)
{
fe.InvalidateMeasure();
}
}
else if (Parent is IFrameworkElement ife)
{
ife.InvalidateMeasure();
}
}
}

Expand All @@ -60,33 +83,33 @@ public override void Layout()
{
try
{
try
_inLayoutSubviews = true;

if (RequiresMeasure)
{
_inLayoutSubviews = true;
XamlMeasure(Bounds.Size);
}

if (RequiresMeasure)
{
XamlMeasure(Bounds.Size);
}
OnBeforeArrange();

OnBeforeArrange();
var size = SizeFromUISize(Bounds.Size);

var size = SizeFromUISize(Bounds.Size);
_layouter.Arrange(new Rect(0, 0, size.Width, size.Height));
_layouter.Arrange(new Rect(0, 0, size.Width, size.Height));

OnAfterArrange();
}
finally
{
_inLayoutSubviews = false;
RequiresArrange = false;
}
OnAfterArrange();
}
catch (Exception e)
{
this.Log().Error($"Layout failed in {GetType()}", e);
}
finally
{
_inLayoutSubviews = false;
RequiresMeasure = false;
RequiresArrange = false;
}
}

/// <summary>
/// Called before Arrange is called, this method will be deprecated
/// once OnMeasure/OnArrange will be implemented completely
Expand All @@ -107,7 +130,7 @@ protected virtual void OnAfterArrange()

}

private CGSize? XamlMeasure(CGSize availableSize)
internal CGSize? XamlMeasure(CGSize availableSize)
{
// If set layout has not been called, we can
// return a previously cached result for the same available size.
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI/UI/Xaml/IFrameworkElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public static CGSize Measure(this IFrameworkElement element, _Size availableSize
}
else if (element is FrameworkElement fe)
{
fe.Measure(new Size(availableSize.Width, availableSize.Height));
fe.XamlMeasure(new CoreGraphics.CGSize(availableSize.Width, availableSize.Height));
var desiredSize = fe.DesiredSize;
return new CGSize(desiredSize.Width, desiredSize.Height);
}
Expand Down Expand Up @@ -298,7 +298,7 @@ public static CGSize Measure(this View element, _Size availableSize)
}
else if (element is FrameworkElement fe)
{
fe.Measure(new Size(availableSize.Width, availableSize.Height));
fe.XamlMeasure(new Size(availableSize.Width, availableSize.Height));
var desiredSize = fe.DesiredSize;
return new CGSize(desiredSize.Width, desiredSize.Height);
}
Expand Down
29 changes: 22 additions & 7 deletions src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.macOS.tt
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,14 @@ namespace <#= mixin.NamespaceName #>
{
// Resolve the property only once, to avoid paying the cost of the interop.
var actualSuperview = Superview;

if (actualSuperview != null)

if(actualSuperview is FrameworkElement fe)
{
fe.InvalidateMeasure();
}
else if (actualSuperview is IFrameworkElement ife)
{
actualSuperview.NeedsLayout = true;
ife.InvalidateMeasure();
}
}

Expand Down Expand Up @@ -576,7 +580,7 @@ namespace <#= mixin.NamespaceName #>
base.ViewDidMoveToSuperview();
OnViewDidMoveToSuperview();

this.SetNeedsLayout();
this.InvalidateMeasure();
}

partial void OnViewDidMoveToSuperview();
Expand All @@ -599,7 +603,7 @@ namespace <#= mixin.NamespaceName #>
{
IsLoaded = true;

this.SetNeedsLayout();
this.InvalidateMeasure();
OnLoadedPartial();

Loaded?.Invoke(this, new RoutedEventArgs(this));
Expand Down Expand Up @@ -706,7 +710,7 @@ namespace <#= mixin.NamespaceName #>
if (base.Hidden != newVisibility.IsHidden())
{
base.Hidden = newVisibility.IsHidden();
this.SetNeedsLayout();
this.InvalidateMeasure();

if (newVisibility == Visibility.Visible)
{
Expand Down Expand Up @@ -990,7 +994,18 @@ namespace <#= mixin.NamespaceName #>
private static void OnGenericPropertyUpdated(object dependencyObject, DependencyPropertyChangedEventArgs args)
{
OnGenericPropertyUpdatedPartial(dependencyObject, args);
((NSView)dependencyObject).SetNeedsLayout();

#if <#= mixin.IsFrameworkElement #>
if(dependencyObject is FrameworkElement fe)
{
fe.InvalidateMeasure();
}
#else
if(dependencyObject is IFrameworkElement ife)
{
ife.InvalidateMeasure();
}
#endif
}

static partial void OnGenericPropertyUpdatedPartial(object dependencyObject, DependencyPropertyChangedEventArgs args);
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/Shapes/Path.iOSmacOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected override CGPath GetPath(Size availableSize)

partial void OnDataChanged()
{
this.SetNeedsLayout();
this.InvalidateMeasure();
}
}
}

0 comments on commit c8f9917

Please sign in to comment.