Skip to content

Commit 76a76fb

Browse files
authored
For Android, respect Minimum/Maximum size requests even when size is set (#25164)
### Description of Change This change is for Android layout only: Clamp the final width/height within the minimum/maximum sizes. Since the layout mode is being set to `MeasureSpecMode.Exactly`, we need to give Android the exact final measurement, it will not take minimum/maximum size requests into account. See the bug for full info. ### Issues Fixed Fixes: * #25163
2 parents 3725647 + a3add45 commit 76a76fb

5 files changed

Lines changed: 54 additions & 9 deletions

File tree

src/Controls/src/Core/Handlers/Items/Android/SizedItemContentView.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
3838
{
3939
var widthSpec = Context.CreateMeasureSpec(targetWidth,
4040
double.IsInfinity(targetWidth) ? double.NaN : targetWidth
41-
, targetWidth);
41+
, minimumSize: double.NaN, maximumSize: targetWidth);
4242

4343
var heightSpec = Context.CreateMeasureSpec(targetHeight, double.IsInfinity(targetHeight) ? double.NaN : targetHeight
44-
, targetHeight);
44+
, minimumSize: double.NaN, maximumSize: targetHeight);
4545

4646
var size = pvh.MeasureVirtualView(widthSpec, heightSpec);
4747

src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,5 +535,44 @@ await AttachAndRun(grid, async _ =>
535535
});
536536
});
537537
}
538+
539+
[Fact]
540+
public async Task SizeRequestIsClampedToMinimumAndMaximum()
541+
{
542+
EnsureHandlerCreated((builder) =>
543+
{
544+
builder.ConfigureMauiHandlers(handler =>
545+
{
546+
handler.AddHandler(typeof(Button), typeof(ButtonHandler));
547+
handler.AddHandler(typeof(Layout), typeof(LayoutHandler));
548+
});
549+
});
550+
551+
var button = new Button()
552+
{
553+
WidthRequest = 20, // request smaller than the minimum
554+
MinimumWidthRequest = 200,
555+
MaximumWidthRequest = 300,
556+
557+
HeightRequest = 400, // request larger than the maximum
558+
MinimumHeightRequest = 200,
559+
MaximumHeightRequest = 300,
560+
561+
HorizontalOptions = LayoutOptions.Start,
562+
VerticalOptions = LayoutOptions.Start,
563+
};
564+
565+
var grid = new Grid { button };
566+
567+
await InvokeOnMainThreadAsync(async () =>
568+
{
569+
await AttachAndRun(grid, _ =>
570+
{
571+
// The size should be the minimum requested size, since that will easily hold the "X" text
572+
Assert.Equal(button.MinimumWidthRequest, button.Width, 0.5);
573+
Assert.Equal(button.MaximumHeightRequest, button.Height, 0.5);
574+
});
575+
});
576+
}
538577
}
539578
}

src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra
4747
}
4848

4949
// Create a spec to handle the native measure
50-
var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MaximumWidth);
51-
var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MaximumHeight);
50+
var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
51+
var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
5252

5353
if (platformView.FillViewport)
5454
{

src/Core/src/Handlers/ViewHandlerExtensions.Android.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ internal static Size GetDesiredSizeFromHandler(this IViewHandler viewHandler, do
8787
}
8888

8989
// Create a spec to handle the native measure
90-
var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MaximumWidth);
91-
var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MaximumHeight);
90+
var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
91+
var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
9292

9393
var packed = PlatformInterop.MeasureAndGetWidthAndHeight(platformView, widthSpec, heightSpec);
9494
var measuredWidth = (int)(packed >> 32);

src/Core/src/Platform/Android/ContextExtensions.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.IO;
33
using Android.Content;
44
using Android.Content.Res;
5-
using Android.OS;
65
using Android.Util;
76
using Android.Views;
87
using Android.Views.InputMethods;
@@ -378,15 +377,22 @@ internal static int GetNavigationBarHeight(this Context context)
378377
return _navigationBarHeight ?? 0;
379378
}
380379

381-
internal static int CreateMeasureSpec(this Context context, double constraint, double explicitSize, double maximumSize)
380+
internal static int CreateMeasureSpec(this Context context, double constraint, double explicitSize, double minimumSize, double maximumSize)
382381
{
383382
var mode = MeasureSpecMode.AtMost;
384383

385384
if (IsExplicitSet(explicitSize))
386385
{
387386
// We have a set value (i.e., a Width or Height)
388387
mode = MeasureSpecMode.Exactly;
389-
constraint = explicitSize;
388+
389+
// Since the mode is "Exactly", we have to return the exact final value clamped to the minimum/maximum.
390+
constraint = Math.Max(explicitSize, ResolveMinimum(minimumSize));
391+
392+
if (IsMaximumSet(maximumSize))
393+
{
394+
constraint = Math.Min(constraint, maximumSize);
395+
}
390396
}
391397
else if (IsMaximumSet(maximumSize) && maximumSize < constraint)
392398
{

0 commit comments

Comments
 (0)