From 5b65d844f9792e26a44f34f5a1b702b09e3454eb Mon Sep 17 00:00:00 2001 From: Alberto Aldegheri Date: Fri, 30 Aug 2024 10:23:49 +0200 Subject: [PATCH] Improve measure invalidation and legacy Layout(s) performance - Do not bubble up `MeasureInvalidated` on scrollable views and pages - Do not synchronously measure/arrange children on legacy layout upon child measure invalidated: simply wait for the next native layout pass --- src/Controls/src/Core/Items/ItemsView.cs | 5 ++++ src/Controls/src/Core/ItemsView.cs | 5 ++++ src/Controls/src/Core/LegacyLayouts/Layout.cs | 15 ++++------ src/Controls/src/Core/Page/Page.cs | 28 ++----------------- .../src/Core/ScrollView/ScrollView.cs | 5 ++++ 5 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/Controls/src/Core/Items/ItemsView.cs b/src/Controls/src/Core/Items/ItemsView.cs index fc2a3ce343bb..884c675553d6 100644 --- a/src/Controls/src/Core/Items/ItemsView.cs +++ b/src/Controls/src/Core/Items/ItemsView.cs @@ -224,5 +224,10 @@ protected override void OnBindingContextChanged() if (InternalItemsLayout is BindableObject bo) SetInheritedBindingContext(bo, BindingContext); } + + internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger) + { + // No need to trigger and propagate MeasureInvalidated given this is a scrollable area + } } } diff --git a/src/Controls/src/Core/ItemsView.cs b/src/Controls/src/Core/ItemsView.cs index 6da264ece396..2544c4ad76db 100644 --- a/src/Controls/src/Core/ItemsView.cs +++ b/src/Controls/src/Core/ItemsView.cs @@ -85,5 +85,10 @@ static void OnItemsSourceChanged(BindableObject bindable, object oldValue, objec } protected virtual bool ValidateItemTemplate(DataTemplate template) => true; + + internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger) + { + // No need to trigger and propagate MeasureInvalidated given this is a scrollable area + } } } diff --git a/src/Controls/src/Core/LegacyLayouts/Layout.cs b/src/Controls/src/Core/LegacyLayouts/Layout.cs index 0f3b6d0b0d7f..f2606f8b893e 100644 --- a/src/Controls/src/Core/LegacyLayouts/Layout.cs +++ b/src/Controls/src/Core/LegacyLayouts/Layout.cs @@ -321,6 +321,8 @@ protected virtual void InvalidateLayout() internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger) { + base.OnChildMeasureInvalidatedInternal(child, trigger); + // TODO: once we remove old Xamarin public signatures we can invoke `OnChildMeasureInvalidated(VisualElement, InvalidationTrigger)` directly OnChildMeasureInvalidated(child, new InvalidationEventArgs(trigger)); } @@ -503,7 +505,7 @@ internal virtual void OnChildMeasureInvalidated(VisualElement child, Invalidatio int count = children.Count; for (var index = 0; index < count; index++) { - if (LogicalChildrenInternal[index] is VisualElement v && v.IsVisible && (!v.IsPlatformEnabled || !v.IsPlatformStateConsistent)) + if (LogicalChildrenInternal[index] is VisualElement { IsVisible: true } v && (!v.IsPlatformEnabled || !v.IsPlatformStateConsistent)) { return; } @@ -517,20 +519,13 @@ internal virtual void OnChildMeasureInvalidated(VisualElement child, Invalidatio { return; } - if (trigger == InvalidationTrigger.HorizontalOptionsChanged || trigger == InvalidationTrigger.VerticalOptionsChanged) + if (trigger is InvalidationTrigger.HorizontalOptionsChanged or InvalidationTrigger.VerticalOptionsChanged) { ComputeConstraintForView(view); } } - if (trigger == InvalidationTrigger.RendererReady) - { - InvalidateMeasureInternal(InvalidationTrigger.RendererReady); - } - else - { - InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged); - } + // Nothing else to do here: platform will take care of arranging the children if needed on the next layout pass } internal override void OnIsVisibleChanged(bool oldValue, bool newValue) diff --git a/src/Controls/src/Core/Page/Page.cs b/src/Controls/src/Core/Page/Page.cs index 53b083d64504..5102f0824f08 100644 --- a/src/Controls/src/Core/Page/Page.cs +++ b/src/Controls/src/Core/Page/Page.cs @@ -499,7 +499,7 @@ protected override void OnBindingContextChanged() internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger) { - // TODO: once we remove old Xamarin public signatures we can invoke `OnChildMeasureInvalidated(VisualElement, InvalidationTrigger)` directly + // No need to trigger and propagate MeasureInvalidated considering Page is the root node OnChildMeasureInvalidated(child, new InvalidationEventArgs(trigger)); } @@ -510,8 +510,7 @@ internal override void OnChildMeasureInvalidatedInternal(VisualElement child, In /// The event arguments. protected virtual void OnChildMeasureInvalidated(object sender, EventArgs e) { - InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined; - OnChildMeasureInvalidated((VisualElement)sender, trigger); + // Nothing to do here: platform will take care of arranging the children if needed on the next layout pass } /// @@ -583,29 +582,6 @@ protected void UpdateChildrenLayout() } } - internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger) - { - var container = this as IPageContainer; - if (container != null) - { - Page page = container.CurrentPage; - if (page != null && page.IsVisible && (!page.IsPlatformEnabled || !page.IsPlatformStateConsistent)) - return; - } - else - { - var logicalChildren = this.InternalChildren; - for (var i = 0; i < logicalChildren.Count; i++) - { - var v = logicalChildren[i] as VisualElement; - if (v != null && v.IsVisible && (!v.IsPlatformEnabled || !v.IsPlatformStateConsistent)) - return; - } - } - - InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged); - } - internal void OnAppearing(Action action) { if (_hasAppeared) diff --git a/src/Controls/src/Core/ScrollView/ScrollView.cs b/src/Controls/src/Core/ScrollView/ScrollView.cs index f4e555d44b1d..21158ea6ae68 100644 --- a/src/Controls/src/Core/ScrollView/ScrollView.cs +++ b/src/Controls/src/Core/ScrollView/ScrollView.cs @@ -489,5 +489,10 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds) return bounds.Size; } + + internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger) + { + // No need to trigger and propagate MeasureInvalidated given this is a scrollable area + } } }