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

Have Frame use MinimumHeight/Width for minimums instead of constraints #13336

Merged
merged 9 commits into from
Feb 17, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
using Android.Views.Animations;
using AndroidX.CardView.Widget;
using AndroidX.Core.View;
using Microsoft.Maui.Controls.Platform;
Expand Down Expand Up @@ -96,17 +97,28 @@ protected Frame? Element
}
}

Size IViewHandler.GetDesiredSize(double widthMeasureSpec, double heightMeasureSpec)
Size IViewHandler.GetDesiredSize(double widthConstraint, double heightConstraint)
{
double minWidth = 20;
if (Primitives.Dimension.IsExplicitSet(widthMeasureSpec) && !double.IsInfinity(widthMeasureSpec))
minWidth = widthMeasureSpec;
var virtualView = (this as IViewHandler)?.VirtualView;
if (virtualView is null)
{
Copy link
Member

Choose a reason for hiding this comment

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

Do we have a way to test this also?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's a test in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unless you mean the null scenario?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah when would it get Size zero?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If someone disconnect the handler in a non-UI thread, or something weird like that. The point is that if the VirtualView is null, then having any size at all doesn't make sense.

return Size.Zero;
}

var minWidth = virtualView.MinimumWidth;
var minHeight = virtualView.MinimumHeight;

if (!Primitives.Dimension.IsExplicitSet(minWidth))
{
minWidth = 20; // Legacy minimum value
}

double minHeight = 20;
if (Primitives.Dimension.IsExplicitSet(widthMeasureSpec) && !double.IsInfinity(heightMeasureSpec))
minHeight = heightMeasureSpec;
if (!Primitives.Dimension.IsExplicitSet(minHeight))
{
minHeight = 20; // Legacy minimum value
hartez marked this conversation as resolved.
Show resolved Hide resolved
}

return VisualElementRenderer<Frame>.GetDesiredSize(this, widthMeasureSpec, heightMeasureSpec,
return VisualElementRenderer<Frame>.GetDesiredSize(this, widthConstraint, heightConstraint,
new Size(minWidth, minHeight));
}

Expand Down
112 changes: 88 additions & 24 deletions src/Controls/tests/DeviceTests/Elements/Frame/FrameTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,7 @@ public async Task FrameWithEntryMeasuresCorrectly()
}
};

var layoutFrame =
await InvokeOnMainThreadAsync(() =>
layout.ToPlatform(MauiContext).AttachAndRun(async () =>
{
var size = (layout as IView).Measure(double.PositiveInfinity, double.PositiveInfinity);
(layout as IView).Arrange(new Graphics.Rect(0, 0, size.Width, size.Height));

await OnFrameSetToNotEmpty(layout);
await OnFrameSetToNotEmpty(frame);

// verify that the PlatformView was measured
var frameControlSize = (frame.Handler as IPlatformViewHandler).PlatformView.GetBoundingBox();
Assert.True(frameControlSize.Width > 0);
Assert.True(frameControlSize.Width > 0);

// if the control sits inside a container make sure that also measured
var containerControlSize = frame.ToPlatform().GetBoundingBox();
Assert.True(frameControlSize.Width > 0);
Assert.True(frameControlSize.Width > 0);

return layout.Frame;

})
);
var layoutFrame = await LayoutFrame(layout, frame, double.PositiveInfinity, double.PositiveInfinity);

Assert.True(entry.Width > 0);
Assert.True(entry.Height > 0);
Expand Down Expand Up @@ -187,5 +164,92 @@ await InvokeOnMainThreadAsync(() =>
platformView.AssertContainsColor(expectedColor);
});
}

[Fact(DisplayName = "Frame Respects minimum height/width")]
public async Task FrameRespectsMinimums()
{
SetupBuilder();

var content = new Button { Text = "Hey", WidthRequest = 50, HeightRequest = 50 };

var frame = new Frame()
{
Content = content,
MinimumHeightRequest = 100,
MinimumWidthRequest = 100,
VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.Start
};

var layout = new StackLayout()
{
Children =
{
frame
}
};

var layoutFrame = await LayoutFrame(layout, frame, 500, 500);

Assert.True(100 <= layoutFrame.Height);
Assert.True(100 <= layoutFrame.Width);
}

[Fact]
public async Task FrameDoesNotInterpretConstraintsAsMinimums()
{
SetupBuilder();

var content = new Button { Text = "Hey", WidthRequest = 50, HeightRequest = 50 };

var frame = new Frame()
{
Content = content,
MinimumHeightRequest = 100,
MinimumWidthRequest = 100,
VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.Start
};

var layout = new StackLayout()
{
Children =
{
frame
}
};

var layoutFrame = await LayoutFrame(layout, frame, 500, 500);

Assert.True(500 > layoutFrame.Width);
Assert.True(500 > layoutFrame.Height);
}

async Task<Rect> LayoutFrame(Layout layout, Frame frame, double measureWidth, double measureHeight)
{
return await InvokeOnMainThreadAsync(() =>
layout.ToPlatform(MauiContext).AttachAndRun(async () =>
{
var size = (layout as IView).Measure(measureWidth, measureHeight);
(layout as IView).Arrange(new Graphics.Rect(0, 0, size.Width, size.Height));

await OnFrameSetToNotEmpty(layout);
await OnFrameSetToNotEmpty(frame);

// verify that the PlatformView was measured
var frameControlSize = (frame.Handler as IPlatformViewHandler).PlatformView.GetBoundingBox();
Assert.True(frameControlSize.Width > 0);
Assert.True(frameControlSize.Width > 0);

// if the control sits inside a container make sure that also measured
var containerControlSize = frame.ToPlatform().GetBoundingBox();
Assert.True(frameControlSize.Width > 0);
Assert.True(frameControlSize.Width > 0);

return layout.Frame;

})
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,14 @@ public async Task ReturnsNonEmptyNativeBoundingBounds(int size)
{
// https://github.com/dotnet/maui/issues/11020
}

// This type name check is gross, but this project doesn't know anything about Frame/FrameStub
PureWeen marked this conversation as resolved.
Show resolved Hide resolved
else if(size == 1 && view.GetType().Name.EndsWith("FrameStub"))
{
// Frames have a legacy hard-coded minimum size of 20x20
Assert.True(CloseEnough(20, nativeBoundingBox.Size.Width));
Assert.True(CloseEnough(20, nativeBoundingBox.Size.Height));
}
#endif
else if (view is IProgress)
{
Expand Down