From 6dd9106fe55ed51aa9ecf34768b3138c067d04e6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 31 Mar 2023 08:15:28 +0200 Subject: [PATCH] Don't layout invisible controls. And fix unit tests that were relying on this behavior. --- src/Avalonia.Base/Layout/LayoutManager.cs | 24 ++++++++++++++----- .../MenuItemTests.cs | 2 ++ .../Primitives/PopupTests.cs | 1 + .../Xaml/ControlThemeTests.cs | 2 ++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs index c4742bcba4..e16be3fa85 100644 --- a/src/Avalonia.Base/Layout/LayoutManager.cs +++ b/src/Avalonia.Base/Layout/LayoutManager.cs @@ -269,21 +269,25 @@ namespace Avalonia.Layout } } - private void Measure(Layoutable control) + private bool Measure(Layoutable control) { + if (!control.IsVisible || !control.IsAttachedToVisualTree) + return false; + // Controls closest to the visual root need to be arranged first. We don't try to store // ordered invalidation lists, instead we traverse the tree upwards, measuring the // controls closest to the root first. This has been shown by benchmarks to be the // fastest and most memory-efficient algorithm. if (control.VisualParent is Layoutable parent) { - Measure(parent); + if (!Measure(parent)) + return false; } // If the control being measured has IsMeasureValid == true here then its measure was // handed by an ancestor and can be ignored. The measure may have also caused the // control to be removed. - if (!control.IsMeasureValid && control.IsAttachedToVisualTree) + if (!control.IsMeasureValid) { if (control is ILayoutRoot root) { @@ -294,16 +298,22 @@ namespace Avalonia.Layout control.Measure(control.PreviousMeasure.Value); } } + + return true; } - private void Arrange(Layoutable control) + private bool Arrange(Layoutable control) { + if (!control.IsVisible || !control.IsAttachedToVisualTree) + return false; + if (control.VisualParent is Layoutable parent) { - Arrange(parent); + if (!Arrange(parent)) + return false; } - if (!control.IsArrangeValid && control.IsAttachedToVisualTree) + if (control.IsMeasureValid && !control.IsArrangeValid) { if (control is IEmbeddedLayoutRoot embeddedRoot) control.Arrange(new Rect(embeddedRoot.AllocatedSize)); @@ -316,6 +326,8 @@ namespace Avalonia.Layout control.Arrange(control.PreviousArrange.Value); } } + + return true; } private void QueueLayoutPass() diff --git a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs index 6fda5209ad..fc189fb3c3 100644 --- a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs @@ -334,6 +334,7 @@ namespace Avalonia.Controls.UnitTests }; var window = new Window { Content = menu }; + window.Show(); window.LayoutManager.ExecuteInitialLayoutPass(); topLevelMenu.IsSubMenuOpen = true; @@ -371,6 +372,7 @@ namespace Avalonia.Controls.UnitTests }; var window = new Window { Content = menu }; + window.Show(); window.LayoutManager.ExecuteInitialLayoutPass(); var panel = Assert.IsType(menu.Presenter.Panel); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 1e22aa9129..34311949ef 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -1116,6 +1116,7 @@ namespace Avalonia.Controls.UnitTests.Primitives private static Window PreparedWindow(object content = null) { var w = new Window { Content = content }; + w.Show(); w.ApplyStyling(); w.ApplyTemplate(); return w; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs index 9eb48311df..1e4c89b33a 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlThemeTests.cs @@ -27,6 +27,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var button = Assert.IsType(window.Content); + window.Show(); window.LayoutManager.ExecuteInitialLayoutPass(); Assert.NotNull(button.Template); @@ -63,6 +64,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var button = Assert.IsType(window.Content); + window.Show(); window.LayoutManager.ExecuteInitialLayoutPass(); Assert.NotNull(button.Template);