Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip invalidation on propagation (#25652) #25696

Merged
merged 7 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Controls/src/Core/ContentPresenter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable
using System;
using System.ComponentModel;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Layouts;

Expand Down Expand Up @@ -122,5 +123,10 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)
this.ArrangeContent(bounds);
return bounds.Size;
}

private protected override void InvalidateMeasureLegacy(InvalidationTrigger trigger, int depth, int depthLeveltoInvalidate)
{
base.InvalidateMeasureLegacy(trigger, depth, 1);
}
}
}
8 changes: 8 additions & 0 deletions src/Controls/src/Core/InvalidationEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ public InvalidationEventArgs(InvalidationTrigger trigger)
{
Trigger = trigger;
}
public InvalidationEventArgs(InvalidationTrigger trigger, int depth) : this(trigger)
{
CurrentInvalidationDepth = depth;
}


public InvalidationTrigger Trigger { get; private set; }


public int CurrentInvalidationDepth { set; get; }
}
}
42 changes: 33 additions & 9 deletions src/Controls/src/Core/LegacyLayouts/Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,10 @@ protected virtual void InvalidateLayout()
/// It is suggested to still call the base method and modify its calculated results.</remarks>
protected abstract void LayoutChildren(double x, double y, double width, double height);

internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger)
internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger, int depth)
{
// TODO: once we remove old Xamarin public signatures we can invoke `OnChildMeasureInvalidated(VisualElement, InvalidationTrigger)` directly
OnChildMeasureInvalidated(child, new InvalidationEventArgs(trigger));
OnChildMeasureInvalidated(child, new InvalidationEventArgs(trigger, depth));
}

/// <summary>
Expand All @@ -334,8 +334,19 @@ internal override void OnChildMeasureInvalidatedInternal(VisualElement child, In
/// <remarks>This method has a default implementation and application developers must call the base implementation.</remarks>
protected void OnChildMeasureInvalidated(object sender, EventArgs e)
{
InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
OnChildMeasureInvalidated((VisualElement)sender, trigger);
var depth = 0;
InvalidationTrigger trigger;
if (e is InvalidationEventArgs args)
{
trigger = args.Trigger;
depth = args.CurrentInvalidationDepth;
}
else
{
trigger = InvalidationTrigger.Undefined;
}

OnChildMeasureInvalidated((VisualElement)sender, trigger, depth);
OnChildMeasureInvalidated();
}

Expand Down Expand Up @@ -497,7 +508,7 @@ internal static void LayoutChildIntoBoundingRegion(View child, Rect region, Size
child.Layout(region);
}

internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger)
internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger, int depth)
{
IReadOnlyList<Element> children = LogicalChildrenInternal;
int count = children.Count;
Expand All @@ -523,13 +534,26 @@ internal virtual void OnChildMeasureInvalidated(VisualElement child, Invalidatio
}
}

if (trigger == InvalidationTrigger.RendererReady)
{
InvalidateMeasureInternal(InvalidationTrigger.RendererReady);
InvalidateMeasureLegacy(trigger, depth, int.MaxValue);
}

// This lets us override the rules for invalidation on MAUI controls that unfortunately still inheirt from the legacy layout
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inherit

private protected virtual void InvalidateMeasureLegacy(InvalidationTrigger trigger, int depth, int depthLeveltoInvalidate)
{
if (depth <= depthLeveltoInvalidate || UseLegacyMeasureInvalidatedBehaviorEnabled)
{
if (trigger == InvalidationTrigger.RendererReady)
{
InvalidateMeasureInternal(new InvalidationEventArgs(InvalidationTrigger.RendererReady, depth));
}
else
{
InvalidateMeasureInternal(new InvalidationEventArgs(InvalidationTrigger.MeasureChanged, depth));
}
}
else
{
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
FireMeasureChanged(trigger, depth);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/LegacyLayouts/StackLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ internal override void ComputeConstraintForView(View view)
ComputeConstraintForView(view, false);
}

internal override void InvalidateMeasureInternal(InvalidationTrigger trigger)
internal override void InvalidateMeasureInternal(InvalidationEventArgs trigger)
{
_layoutInformation = new LayoutInformation();
base.InvalidateMeasureInternal(trigger);
Expand Down
32 changes: 26 additions & 6 deletions src/Controls/src/Core/Page/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,10 +498,11 @@ protected override void OnBindingContextChanged()
SetInheritedBindingContext(TitleView, BindingContext);
}

internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger)

internal override void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger, int depth)
{
// TODO: once we remove old Xamarin public signatures we can invoke `OnChildMeasureInvalidated(VisualElement, InvalidationTrigger)` directly
OnChildMeasureInvalidated(child, new InvalidationEventArgs(trigger));
OnChildMeasureInvalidated(child, new InvalidationEventArgs(trigger, depth));
}

/// <summary>
Expand All @@ -511,8 +512,19 @@ internal override void OnChildMeasureInvalidatedInternal(VisualElement child, In
/// <param name="e">The event arguments.</param>
protected virtual void OnChildMeasureInvalidated(object sender, EventArgs e)
{
InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
OnChildMeasureInvalidated((VisualElement)sender, trigger);
var depth = 0;
InvalidationTrigger trigger;
if (e is InvalidationEventArgs args)
{
trigger = args.Trigger;
depth = args.CurrentInvalidationDepth;
}
else
{
trigger = InvalidationTrigger.Undefined;
}

OnChildMeasureInvalidated((VisualElement)sender, trigger, depth);
}

/// <summary>
Expand Down Expand Up @@ -585,7 +597,7 @@ protected void UpdateChildrenLayout()
}
}

internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger)
internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger, int depth)
{
var container = this as IPageContainer<Page>;
if (container != null)
Expand All @@ -606,7 +618,15 @@ internal virtual void OnChildMeasureInvalidated(VisualElement child, Invalidatio
}

_allocatedFlag = false;
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
if (depth <= 1 || UseLegacyMeasureInvalidatedBehaviorEnabled)
{
InvalidateMeasureInternal(new InvalidationEventArgs(InvalidationTrigger.MeasureChanged, depth));
}
else
{
FireMeasureChanged(trigger, depth);
}

if (!_allocatedFlag && Width >= 0 && Height >= 0 && UseLegacyMeasureInvalidatedBehaviorEnabled)
{
SizeAllocated(Width, Height);
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/ScrollView/ScrollView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,5 +489,10 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)

return bounds.Size;
}

private protected override void InvalidateMeasureLegacy(InvalidationTrigger trigger, int depth, int depthLeveltoInvalidate)
{
base.InvalidateMeasureLegacy(trigger, depth, 1);
}
}
}
7 changes: 7 additions & 0 deletions src/Controls/src/Core/TemplatedView/TemplatedView.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Layouts;

Expand Down Expand Up @@ -140,6 +141,12 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)
this.ArrangeContent(bounds);
return bounds.Size;
}

private protected override void InvalidateMeasureLegacy(InvalidationTrigger trigger, int depth, int depthLeveltoInvalidate)
{
base.InvalidateMeasureLegacy(trigger, depth, 1);
}

#nullable disable

}
Expand Down
51 changes: 35 additions & 16 deletions src/Controls/src/Core/VisualElement/VisualElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,15 @@ static void OnTransformChanged(BindableObject bindable, object oldValue, object
public static readonly BindableProperty BackgroundProperty = BindableProperty.Create(nameof(Background), typeof(Brush), typeof(VisualElement), Brush.Default,
propertyChanging: (bindable, oldvalue, newvalue) =>
{
if (oldvalue == null) return;
if (oldvalue == null)
return;

(bindable as VisualElement)?.StopNotifyingBackgroundChanges();
},
propertyChanged: (bindable, oldvalue, newvalue) =>
{
if (newvalue == null) return;
if (newvalue == null)
return;

(bindable as VisualElement)?.NotifyBackgroundChanges();
});
Expand Down Expand Up @@ -318,7 +320,7 @@ void NotifyBackgroundChanges()
_backgroundChanged ??= (sender, e) => OnPropertyChanged(nameof(Background));
_backgroundProxy ??= new();
_backgroundProxy.Subscribe(background, _backgroundChanged);

OnParentResourcesChanged(this.GetMergedResources());
((IElementDefinition)this).AddResourcesChangedListener(background.OnParentResourcesChanged);
}
Expand Down Expand Up @@ -1362,14 +1364,19 @@ public void InvalidateMeasureNonVirtual(InvalidationTrigger trigger)
InvalidateMeasureInternal(trigger);
}

internal virtual void InvalidateMeasureInternal(InvalidationTrigger trigger)
internal void InvalidateMeasureInternal(InvalidationTrigger trigger)
{
InvalidateMeasureInternal(new InvalidationEventArgs(trigger, 0));
}

internal virtual void InvalidateMeasureInternal(InvalidationEventArgs eventArgs)
{
_measureCache.Clear();

// TODO ezhart Once we get InvalidateArrange sorted, HorizontalOptionsChanged and
// VerticalOptionsChanged will need to call ParentView.InvalidateArrange() instead

switch (trigger)
switch (eventArgs.Trigger)
{
case InvalidationTrigger.MarginChanged:
case InvalidationTrigger.HorizontalOptionsChanged:
Expand All @@ -1381,14 +1388,29 @@ internal virtual void InvalidateMeasureInternal(InvalidationTrigger trigger)
break;
}

MeasureInvalidated?.Invoke(this, new InvalidationEventArgs(trigger));
FireMeasureChanged(eventArgs);
}

private protected void FireMeasureChanged(InvalidationTrigger trigger, int depth)
{
FireMeasureChanged(new InvalidationEventArgs(trigger, depth));
}


private protected void FireMeasureChanged(InvalidationEventArgs args)
{
var depth = args.CurrentInvalidationDepth;
MeasureInvalidated?.Invoke(this, args);
if (!UseLegacyMeasureInvalidatedBehaviorEnabled)
{
(Parent as VisualElement)?.OnChildMeasureInvalidatedInternal(this, trigger);
}
(Parent as VisualElement)?.OnChildMeasureInvalidatedInternal(this, args.Trigger, ++depth);
}

internal virtual void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger)

// We don't want to change the execution path of Page or Layout when they are calling "InvalidationMeasure"
// If you look at page it calls OnChildMeasureInvalidated from OnChildMeasureInvalidatedInternal
// Because OnChildMeasureInvalidated is public API and the user might override it, we need to keep it as is
//private protected int CurrentInvalidationDepth { get; set; }

internal virtual void OnChildMeasureInvalidatedInternal(VisualElement child, InvalidationTrigger trigger, int depth)
{
switch (trigger)
{
Expand All @@ -1400,17 +1422,14 @@ internal virtual void OnChildMeasureInvalidatedInternal(VisualElement child, Inv
case InvalidationTrigger.RendererReady:
// Undefined happens in many cases, including when `IsVisible` changes
case InvalidationTrigger.Undefined:
MeasureInvalidated?.Invoke(this, new InvalidationEventArgs(trigger));
(Parent as VisualElement)?.OnChildMeasureInvalidatedInternal(this, trigger);
FireMeasureChanged(trigger, depth);
return;
default:
// When visibility changes `InvalidationTrigger.Undefined` is used,
// so here we're sure that visibility didn't change
if (child.IsVisible)
{
// We need to invalidate measures only if child is actually visible
MeasureInvalidated?.Invoke(this, new InvalidationEventArgs(InvalidationTrigger.MeasureChanged));
(Parent as VisualElement)?.OnChildMeasureInvalidatedInternal(this, InvalidationTrigger.MeasureChanged);
FireMeasureChanged(InvalidationTrigger.MeasureChanged, depth);
}
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/tests/Core.UnitTests/GridTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ public void UnconstrainedStarRowWithMultipleStarColumnsAllowsTextToGrow()
static void AssertEqualWithTolerance(double a, double b, double tolerance)
{
var diff = Math.Abs(a - b);
Assert.True(diff <= tolerance);
Assert.True(diff <= tolerance, $"a: {a} b: {b} tolerance: {tolerance}");
}

[Theory]
Expand Down
Loading