Skip to content

Commit

Permalink
When measuring FlexLayout unconstrained, allow child to be desired si…
Browse files Browse the repository at this point in the history
…ze (rather than zero)

Fixes #11909
  • Loading branch information
hartez committed Feb 8, 2023
1 parent be1e6ff commit 39ba91c
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 2 deletions.
8 changes: 8 additions & 0 deletions src/Controls/src/Core/Layout/FlexLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,14 @@ void AddFlexItem(IView child)
{
if (_root == null)
return;

if (child is not BindableObject)
{
// If this is a pure Core IView, we need to track all the flex properties
// locally because we don't have attached properties for them
_viewInfo.Add(child, new FlexInfo());
}

var item = (child as FlexLayout)?._root ?? new Flex.Item();
InitItemProperties(child, item);
if (child is not FlexLayout)
Expand Down
49 changes: 49 additions & 0 deletions src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Layouts;
using Xunit;
using NSubstitute;

namespace Microsoft.Maui.Controls.Core.UnitTests.Layouts
{
Expand Down Expand Up @@ -80,5 +81,53 @@ public void FlexLayoutRecognizesVisibilityChange()
// now that the first view is not visible
Assert.True(whenInvisible != whenVisible);
}

/*
* These next two tests deal with unconstrained measure of FlexLayout. Be default, FL
* wants to stretch children across each axis. But you can't stretch things across infinity
* without it getting weird. So for _measurement_ purposes, we treat infinity as zero and
* just give the children their desired size in the unconstrained direction. Otherwise, FL
* would just set their flex frame sizes to zero, which can either cause blanks or layout cycles,
* depending on the target platform.
*/

(IFlexLayout, IView) SetUpUnconstrainedTest()
{
var root = new Grid(); // FlexLayout requires a parent, at least for now
var flexLayout = new FlexLayout() as IFlexLayout;

var view = Substitute.For<IView>();
var size = new Size(100, 100);
view.Measure(Arg.Any<double>(), Arg.Any<double>()).Returns(size);

root.Add(flexLayout);
flexLayout.Add(view);

return (flexLayout, view);
}

[Fact]
public void UnconstrainedHeightChildrenHaveHeight()
{
(var flexLayout, var view) = SetUpUnconstrainedTest();

_ = flexLayout.CrossPlatformMeasure(400, double.PositiveInfinity);

var flexFrame = flexLayout.GetFlexFrame(view);

Assert.Equal(100, flexFrame.Height);
}

[Fact]
public void UnconstrainedWidthChildrenHaveWidth()
{
(var flexLayout, var view) = SetUpUnconstrainedTest();

_ = flexLayout.CrossPlatformMeasure(double.PositiveInfinity, 400);

var flexFrame = flexLayout.GetFlexFrame(view);

Assert.Equal(100, flexFrame.Width);
}
}
}
45 changes: 45 additions & 0 deletions src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using Xunit;

namespace Microsoft.Maui.DeviceTests
Expand Down Expand Up @@ -151,5 +152,49 @@ static void CreateLayout(Type layoutType, out Layout layout, out Label label)
layout.Add(label);
}
}

[Fact, Category(TestCategory.FlexLayout)]
public async Task FlexLayoutInVerticalStackLayoutDoesNotCycle()
{
var root = new VerticalStackLayout();
var flexLayout = new FlexLayout();
var label = new Label { Text = "Hello" };

flexLayout.Add(label);
root.Add(flexLayout);

await InvokeOnMainThreadAsync(() =>
{
var labelHandler = CreateHandler<LabelHandler>(label);
var flexLayoutHandler = CreateHandler<LayoutHandler>(flexLayout);
var layoutHandler = CreateHandler<LayoutHandler>(root);

// If this can be attached to the hierarchy and make it through a layout
// without crashing, then we're good.
root.ToPlatform(MauiContext).AttachAndRun(() => { });
});
}

[Fact, Category(TestCategory.FlexLayout)]
public async Task FlexLayoutInHorizontalStackLayoutDoesNotCycle()
{
var root = new HorizontalStackLayout();
var flexLayout = new FlexLayout();
var label = new Label { Text = "Hello" };

flexLayout.Add(label);
root.Add(flexLayout);

await InvokeOnMainThreadAsync(() =>
{
var labelHandler = CreateHandler<LabelHandler>(label);
var flexLayoutHandler = CreateHandler<LayoutHandler>(flexLayout);
var layoutHandler = CreateHandler<LayoutHandler>(root);

// If this can be attached to the hierarchy and make it through a layout
// without crashing, then we're good.
root.ToPlatform(MauiContext).AttachAndRun(() => { });
});
}
}
}
3 changes: 2 additions & 1 deletion src/Controls/tests/DeviceTests/TestCategory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ public static class TestCategory
public const string Editor = "Editor";
public const string Element = "Element";
public const string Entry = "Entry";
public const string Frame = "Frame";
public const string FlexLayout = "FlexLayout";
public const string FlyoutPage = "FlyoutPage";
public const string Frame = "Frame";
public const string Gesture = "Gesture";
public const string Image = "Image";
public const string Label = "Label";
Expand Down
2 changes: 1 addition & 1 deletion src/Core/src/Layouts/Flex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ static void layout_item(Item item, float width, float height)
for (int j = 0; j < 2; j++)
{
int size_off = j + 2;
if (size_off == layout.frame_size2_i && child_align(child, item) == AlignItems.Stretch)
if (size_off == layout.frame_size2_i && child_align(child, item) == AlignItems.Stretch && layout.align_dim > 0)
continue;
float val = size[j];
if (!float.IsNaN(val))
Expand Down

0 comments on commit 39ba91c

Please sign in to comment.