From c8f9917899189ed9e409d4895ae44d6781335442 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Thu, 2 Apr 2020 17:34:59 -0400 Subject: [PATCH] fix: Adjust measure/arrange phase 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. --- .../ContentPresenter.macOS.cs | 2 +- .../UI/Xaml/Controls/Image/Image.macOS.cs | 2 +- .../MenuBar/NativeMenuBarPresenter.macOS.cs | 2 +- .../UI/Xaml/Controls/Panel/Panel.macOS.cs | 6 +- .../ScrollContentPresenter.macOS.cs | 2 + src/Uno.UI/UI/Xaml/FrameworkElement.macOS.cs | 65 +++++++++++++------ src/Uno.UI/UI/Xaml/IFrameworkElement.cs | 4 +- .../IFrameworkElementImplementation.macOS.tt | 29 +++++++-- src/Uno.UI/UI/Xaml/Shapes/Path.iOSmacOS.cs | 2 +- 9 files changed, 77 insertions(+), 37 deletions(-) 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 541515e8bc8f..becca802c949 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.macOS.cs @@ -29,7 +29,7 @@ public ContentPresenter() private void SetUpdateTemplate() { UpdateContentTemplateRoot(); - this.SetNeedsLayout(); + this.InvalidateMeasure(); } partial void RegisterContentTemplateRoot() diff --git a/src/Uno.UI/UI/Xaml/Controls/Image/Image.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/Image/Image.macOS.cs index fdc40145f349..5102c980e31e 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Image/Image.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Image/Image.macOS.cs @@ -209,7 +209,7 @@ private void SetNeedsLayoutOrDisplay() } else { - this.SetNeedsLayout(); + this.InvalidateMeasure(); } } diff --git a/src/Uno.UI/UI/Xaml/Controls/MenuBar/NativeMenuBarPresenter.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/MenuBar/NativeMenuBarPresenter.macOS.cs index 8464c39fea89..884c5e23973f 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MenuBar/NativeMenuBarPresenter.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MenuBar/NativeMenuBarPresenter.macOS.cs @@ -8,7 +8,7 @@ namespace Windows.UI.Xaml.Controls { - public class NativeMenuBarPresenter : FrameworkElement + public partial class NativeMenuBarPresenter : FrameworkElement { private MenuBar _menuBar; 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 1c61b50e9868..631b269e83f1 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Panel/Panel.macOS.cs @@ -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) @@ -114,7 +114,7 @@ private void UpdateBackground(NSImage backgroundImage = null) protected virtual void OnChildrenChanged() { - NeedsLayout = true; + InvalidateMeasure(); } protected override void OnAfterArrange() diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.macOS.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.macOS.cs index ccaf75db9ca9..9feefdd09553 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.macOS.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.macOS.cs @@ -22,6 +22,8 @@ public ScrollContentPresenter() InitializeScrollContentPresenter(); Notifications.ObserveDidLiveScroll(this, OnLiveScroll); + + DrawsBackground = false; } public nfloat ZoomScale { diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.macOS.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.macOS.cs index 95b3e403b626..79a212b20681 100644 --- a/src/Uno.UI/UI/Xaml/FrameworkElement.macOS.cs +++ b/src/Uno.UI/UI/Xaml/FrameworkElement.macOS.cs @@ -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(); + } } } @@ -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; + } } + /// /// Called before Arrange is called, this method will be deprecated /// once OnMeasure/OnArrange will be implemented completely @@ -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. diff --git a/src/Uno.UI/UI/Xaml/IFrameworkElement.cs b/src/Uno.UI/UI/Xaml/IFrameworkElement.cs index ea3277e1bab9..314f86ad0f5a 100644 --- a/src/Uno.UI/UI/Xaml/IFrameworkElement.cs +++ b/src/Uno.UI/UI/Xaml/IFrameworkElement.cs @@ -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); } @@ -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); } diff --git a/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.macOS.tt b/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.macOS.tt index a4d93b1871ab..812213930b2d 100644 --- a/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.macOS.tt +++ b/src/Uno.UI/UI/Xaml/IFrameworkElementImplementation.macOS.tt @@ -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(); } } @@ -576,7 +580,7 @@ namespace <#= mixin.NamespaceName #> base.ViewDidMoveToSuperview(); OnViewDidMoveToSuperview(); - this.SetNeedsLayout(); + this.InvalidateMeasure(); } partial void OnViewDidMoveToSuperview(); @@ -599,7 +603,7 @@ namespace <#= mixin.NamespaceName #> { IsLoaded = true; - this.SetNeedsLayout(); + this.InvalidateMeasure(); OnLoadedPartial(); Loaded?.Invoke(this, new RoutedEventArgs(this)); @@ -706,7 +710,7 @@ namespace <#= mixin.NamespaceName #> if (base.Hidden != newVisibility.IsHidden()) { base.Hidden = newVisibility.IsHidden(); - this.SetNeedsLayout(); + this.InvalidateMeasure(); if (newVisibility == Visibility.Visible) { @@ -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); diff --git a/src/Uno.UI/UI/Xaml/Shapes/Path.iOSmacOS.cs b/src/Uno.UI/UI/Xaml/Shapes/Path.iOSmacOS.cs index de56cccd510c..020299da2dd8 100644 --- a/src/Uno.UI/UI/Xaml/Shapes/Path.iOSmacOS.cs +++ b/src/Uno.UI/UI/Xaml/Shapes/Path.iOSmacOS.cs @@ -25,7 +25,7 @@ protected override CGPath GetPath(Size availableSize) partial void OnDataChanged() { - this.SetNeedsLayout(); + this.InvalidateMeasure(); } } }