Skip to content
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
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);
}
}
}
35 changes: 35 additions & 0 deletions src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using Xunit;
using Xunit.Sdk;

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

[Fact, Category(TestCategory.FlexLayout)]
public async Task FlexLayoutInVerticalStackLayoutDoesNotCycle()
{
await FlexLayoutInStackLayoutDoesNotCycle(new VerticalStackLayout());
}

[Fact, Category(TestCategory.FlexLayout)]
public async Task FlexLayoutInHorizontalStackLayoutDoesNotCycle()
{
await FlexLayoutInStackLayoutDoesNotCycle(new HorizontalStackLayout());
}

async Task FlexLayoutInStackLayoutDoesNotCycle(IStackLayout root)
{
var flexLayout = new FlexLayout();
var label = new Label { Text = "Hello" };

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

await InvokeOnMainThreadAsync(async () =>
{
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.

await 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
10 changes: 8 additions & 2 deletions 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 Expand Up @@ -579,7 +579,13 @@ static void layout_item(Item item, float width, float height)
layout.flex_grows += child.Grow;
layout.flex_shrinks += child.Shrink;

layout.flex_dim -= child_size + child.MarginThickness(layout.vertical);
if(layout.flex_dim > 0)
{
// If flex_dim is zero, it's because we're measuring unconstrained in that direction
// So we don't need to keep a running tally of available space

layout.flex_dim -= child_size + child.MarginThickness(layout.vertical);
}

relative_children_count++;

Expand Down