diff --git a/src/Controls/src/Core/Layout/FlexLayout.cs b/src/Controls/src/Core/Layout/FlexLayout.cs index 235c6dd5917a..1e43f5c0ee5a 100644 --- a/src/Controls/src/Core/Layout/FlexLayout.cs +++ b/src/Controls/src/Core/Layout/FlexLayout.cs @@ -497,22 +497,32 @@ void AddFlexItem(int index, IView child) { item.SelfSizing = (Flex.Item it, ref float w, ref float h) => { - var sizeConstraints = item.GetConstraints(); + Size request; - sizeConstraints.Width = (InMeasureMode && sizeConstraints.Width == 0) ? double.PositiveInfinity : sizeConstraints.Width; - sizeConstraints.Height = (InMeasureMode && sizeConstraints.Height == 0) ? double.PositiveInfinity : sizeConstraints.Height; - - if (child is Image) + if (InMeasureMode) { - // This is a hack to get FlexLayout to behave like it did in Forms - // Forms always did its initial image measure unconstrained, which would return - // the intrinsic size of the image (no scaling or aspect ratio adjustments) + var sizeConstraints = item.GetConstraints(); - sizeConstraints.Width = double.PositiveInfinity; - sizeConstraints.Height = double.PositiveInfinity; - } + sizeConstraints.Width = (InMeasureMode && sizeConstraints.Width == 0) ? double.PositiveInfinity : sizeConstraints.Width; + sizeConstraints.Height = (InMeasureMode && sizeConstraints.Height == 0) ? double.PositiveInfinity : sizeConstraints.Height; + + if (child is Image) + { + // This is a hack to get FlexLayout to behave like it did in Forms + // Forms always did its initial image measure unconstrained, which would return + // the intrinsic size of the image (no scaling or aspect ratio adjustments) + + sizeConstraints.Width = double.PositiveInfinity; + sizeConstraints.Height = double.PositiveInfinity; + } - var request = child.Measure(sizeConstraints.Width, sizeConstraints.Height); + request = child.Measure(sizeConstraints.Width, sizeConstraints.Height); + } + else + { + // Arrange pass, do not ever run a measure here! + request = child.DesiredSize; + } w = (float)request.Width; h = (float)request.Height; }; diff --git a/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs b/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs index 558ebb7b9222..8757a843e418 100644 --- a/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs +++ b/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs @@ -43,8 +43,7 @@ public void FlexLayoutMeasuresImagesUnconstrained() root.Add(flexLayout); flexLayout.Add(image as IView); - var manager = new FlexLayoutManager(flexLayout); - _ = manager.Measure(1000, 1000); + flexLayout.CrossPlatformMeasure(1000, 1000); Assert.True(image.Passed, "Image should be measured unconstrained even if the FlexLayout is constrained."); } @@ -61,11 +60,9 @@ public void FlexLayoutRecognizesVisibilityChange() flexLayout.Add(view as IView); flexLayout.Add(view2 as IView); - var manager = new FlexLayoutManager(flexLayout); - // Measure and arrange the layout while the first view is visible - var measure = manager.Measure(1000, 1000); - manager.ArrangeChildren(new Rect(Point.Zero, measure)); + var measure = flexLayout.CrossPlatformMeasure(1000, 1000); + flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure)); // Keep track of where the second view is arranged var whenVisible = view2.Frame.X; @@ -73,9 +70,9 @@ public void FlexLayoutRecognizesVisibilityChange() // Change the visibility view.IsVisible = false; - // Measure and arrange againg - measure = manager.Measure(1000, 1000); - manager.ArrangeChildren(new Rect(Point.Zero, measure)); + // Measure and arrange again + measure = flexLayout.CrossPlatformMeasure(1000, 1000); + flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure)); var whenInvisible = view2.Frame.X; diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20696.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20696.cs new file mode 100644 index 000000000000..1e3901cebe47 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20696.cs @@ -0,0 +1,30 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue20696 : _IssuesUITest + { + public override string Issue => "[iOS] FlyoutHeader does not change its size after adding new content"; + + public Issue20696(TestDevice device) : base(device) + { + } + + #if IOS + [Test] + #endif + public void FlyoutHeaderShouldBeResized() + { + _ = App.WaitForElement("GoToTest"); + App.Tap("GoToTest"); + + _ = App.WaitForElement("button"); + App.Tap("button"); + + //The test passes if the second button is visible in the flyout header + _ = App.WaitForElement("TestButton2"); + } + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9075.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9075.cs new file mode 100644 index 000000000000..f38521234fe0 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9075.cs @@ -0,0 +1,25 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue9075 : _IssuesUITest +{ + public override string Issue => "FlexLayout trigger Cycle GUI exception"; + + public Issue9075(TestDevice device) + : base(device) + { } + + #if WINDOWS + [Test] + [Category(UITestCategories.Layout)] + #endif + public void FlexLayoutCycleException() + { + App.WaitForElement("Item2"); + + // Without exceptions, the test has passed. + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases/Issues/Issue9075.xaml b/src/Controls/tests/TestCases/Issues/Issue9075.xaml new file mode 100644 index 000000000000..47956e52a11d --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue9075.xaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Controls/tests/TestCases/Issues/Issue9075.xaml.cs b/src/Controls/tests/TestCases/Issues/Issue9075.xaml.cs new file mode 100644 index 000000000000..8a3c555ff100 --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue9075.xaml.cs @@ -0,0 +1,60 @@ +using System.Collections.ObjectModel; +using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Controls; +using System.Collections.Generic; +using System.Linq; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.None, 9075, "FlexLayout trigger Cycle GUI exception", PlatformAffected.UWP)] + public partial class Issue9075 : ContentPage + { + public Issue9075() + { + InitializeComponent(); + + BindingContext = new Issue9075ViewModel(); + } + } + + [Preserve(AllMembers = true)] + public partial class Issue9075ViewModel : BindableObject + { + List _carouselCards = new() + { + 49, + 12, + 50, + 10, + 25, + 9, + 99, + 77 + }; + + ObservableCollection> _carouselGrids = new(); + + public ObservableCollection> CarouselGrids + { + get => _carouselGrids; + set + { + _carouselGrids = value; + OnPropertyChanged(); + } + } + + public Issue9075ViewModel() + { + _InitGrids(); + } + + private void _InitGrids() + { + foreach (var card in _carouselCards) + { + _carouselGrids.Add(new ObservableCollection(Enumerable.Range(1, card).Select(x=> $"Item{x}"))); + } + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue20696.cs b/src/Controls/tests/UITests/Tests/Issues/Issue20696.cs deleted file mode 100644 index 2c5cb341d287..000000000000 --- a/src/Controls/tests/UITests/Tests/Issues/Issue20696.cs +++ /dev/null @@ -1,37 +0,0 @@ -using NUnit.Framework; -using UITest.Appium; -using UITest.Core; - -namespace Microsoft.Maui.AppiumTests.Issues -{ - public class Issue20696 : _IssuesUITest - { - public override string Issue => "[iOS] FlyoutHeader does not change its size after adding new content"; - - public Issue20696(TestDevice device) : base(device) - { - } - - [Test] - public void FlyoutHeaderShouldBeResized() - { - this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.Android, TestDevice.Mac, TestDevice.Windows }); - - try - { - _ = App.WaitForElement("GoToTest"); - App.Click("GoToTest"); - - _ = App.WaitForElement("button"); - App.Click("button"); - - //The test passes if the second button is visible in the flyout header - _ = App.WaitForElement("TestButton2"); - } - finally - { - Reset(); - } - } - } -}