From a3add454b2bf13d5e6dfe5d6ebccedefab8df0fc Mon Sep 17 00:00:00 2001 From: Peter Spada Date: Wed, 9 Oct 2024 16:05:18 -0700 Subject: [PATCH] For Android, respect Minimum/Maximum size requests even when size is set --- .../Items/Android/SizedItemContentView.cs | 4 +- .../Elements/Layout/LayoutTests.cs | 39 +++++++++++++++++++ .../ScrollView/ScrollViewHandler.Android.cs | 4 +- .../Handlers/ViewHandlerExtensions.Android.cs | 4 +- .../src/Platform/Android/ContextExtensions.cs | 12 ++++-- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/Controls/src/Core/Handlers/Items/Android/SizedItemContentView.cs b/src/Controls/src/Core/Handlers/Items/Android/SizedItemContentView.cs index 1049c3fc729b..055292479084 100644 --- a/src/Controls/src/Core/Handlers/Items/Android/SizedItemContentView.cs +++ b/src/Controls/src/Core/Handlers/Items/Android/SizedItemContentView.cs @@ -38,10 +38,10 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { var widthSpec = Context.CreateMeasureSpec(targetWidth, double.IsInfinity(targetWidth) ? double.NaN : targetWidth - , targetWidth); + , minimumSize: double.NaN, maximumSize: targetWidth); var heightSpec = Context.CreateMeasureSpec(targetHeight, double.IsInfinity(targetHeight) ? double.NaN : targetHeight - , targetHeight); + , minimumSize: double.NaN, maximumSize: targetHeight); var size = pvh.MeasureVirtualView(widthSpec, heightSpec); diff --git a/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs b/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs index 9dac063c474d..1591f2270243 100644 --- a/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.cs @@ -535,5 +535,44 @@ await AttachAndRun(grid, async _ => }); }); } + + [Fact] + public async Task SizeRequestIsClampedToMinimumAndMaximum() + { + EnsureHandlerCreated((builder) => + { + builder.ConfigureMauiHandlers(handler => + { + handler.AddHandler(typeof(Button), typeof(ButtonHandler)); + handler.AddHandler(typeof(Layout), typeof(LayoutHandler)); + }); + }); + + var button = new Button() + { + WidthRequest = 20, // request smaller than the minimum + MinimumWidthRequest = 200, + MaximumWidthRequest = 300, + + HeightRequest = 400, // request larger than the maximum + MinimumHeightRequest = 200, + MaximumHeightRequest = 300, + + HorizontalOptions = LayoutOptions.Start, + VerticalOptions = LayoutOptions.Start, + }; + + var grid = new Grid { button }; + + await InvokeOnMainThreadAsync(async () => + { + await AttachAndRun(grid, _ => + { + // The size should be the minimum requested size, since that will easily hold the "X" text + Assert.Equal(button.MinimumWidthRequest, button.Width, 0.5); + Assert.Equal(button.MaximumHeightRequest, button.Height, 0.5); + }); + }); + } } } diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs index f724a68109ff..3a8a4b3e9741 100644 --- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs +++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs @@ -47,8 +47,8 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra } // Create a spec to handle the native measure - var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MaximumWidth); - var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MaximumHeight); + var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth); + var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight); if (platformView.FillViewport) { diff --git a/src/Core/src/Handlers/ViewHandlerExtensions.Android.cs b/src/Core/src/Handlers/ViewHandlerExtensions.Android.cs index f133eb0071dc..654a33f46629 100644 --- a/src/Core/src/Handlers/ViewHandlerExtensions.Android.cs +++ b/src/Core/src/Handlers/ViewHandlerExtensions.Android.cs @@ -87,8 +87,8 @@ internal static Size GetDesiredSizeFromHandler(this IViewHandler viewHandler, do } // Create a spec to handle the native measure - var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MaximumWidth); - var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MaximumHeight); + var widthSpec = Context.CreateMeasureSpec(widthConstraint, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth); + var heightSpec = Context.CreateMeasureSpec(heightConstraint, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight); var packed = PlatformInterop.MeasureAndGetWidthAndHeight(platformView, widthSpec, heightSpec); var measuredWidth = (int)(packed >> 32); diff --git a/src/Core/src/Platform/Android/ContextExtensions.cs b/src/Core/src/Platform/Android/ContextExtensions.cs index 65363e6bb980..88ead155d5dd 100644 --- a/src/Core/src/Platform/Android/ContextExtensions.cs +++ b/src/Core/src/Platform/Android/ContextExtensions.cs @@ -2,7 +2,6 @@ using System.IO; using Android.Content; using Android.Content.Res; -using Android.OS; using Android.Util; using Android.Views; using Android.Views.InputMethods; @@ -378,7 +377,7 @@ internal static int GetNavigationBarHeight(this Context context) return _navigationBarHeight ?? 0; } - internal static int CreateMeasureSpec(this Context context, double constraint, double explicitSize, double maximumSize) + internal static int CreateMeasureSpec(this Context context, double constraint, double explicitSize, double minimumSize, double maximumSize) { var mode = MeasureSpecMode.AtMost; @@ -386,7 +385,14 @@ internal static int CreateMeasureSpec(this Context context, double constraint, d { // We have a set value (i.e., a Width or Height) mode = MeasureSpecMode.Exactly; - constraint = explicitSize; + + // Since the mode is "Exactly", we have to return the exact final value clamped to the minimum/maximum. + constraint = Math.Max(explicitSize, ResolveMinimum(minimumSize)); + + if (IsMaximumSet(maximumSize)) + { + constraint = Math.Min(constraint, maximumSize); + } } else if (IsMaximumSet(maximumSize) && maximumSize < constraint) {