Skip to content

Conversation

@Ahamed-Ali
Copy link
Contributor

@Ahamed-Ali Ahamed-Ali commented May 22, 2025

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause of the issue

  • When using the label inside the collectionview , on orientation change of device cause the Layout.Width not properly measured, due to the small width , clipping issue occurred.

Description of Change

  • Instead of relying on the Layout property , we can use the StaticLayout from the TextLayoutUtils Extensions and working fine properly

Issues Fixed

Fixes #29542
Fixes #29727
Fixes #28928

Tested the behaviour in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Screenshot

Before Issue Fix After Issue Fix
CollectionViewLabelIssue.mov
CollectionViewLabelFix.mov
BlankSpaceIssue.mov
BlankSpaceIssuefixed.mov

@dotnet-policy-service
Copy link
Contributor

Hey there @@Ahamed-Ali! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels May 22, 2025
@jsuarezruiz jsuarezruiz added the area-controls-label Label, Span label May 22, 2025
@jsuarezruiz
Copy link
Contributor

/azp run MAUI-UITests-public

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@Ahamed-Ali Ahamed-Ali marked this pull request as ready for review May 23, 2025 11:40
Copilot AI review requested due to automatic review settings May 23, 2025 11:40
@Ahamed-Ali Ahamed-Ali requested a review from a team as a code owner May 23, 2025 11:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes label clipping issues on Android when rotating devices by switching to a static layout measurement and adds automated UI tests to verify correct sizing in a CollectionView scenario.

  • Replaces reliance on Layout with TextLayoutUtils.CreateLayout in MauiTextView.OnMeasure
  • Introduces a UI test (Issue29542) to ensure labels size properly after orientation changes
  • Adds a HostApp reproduction page for the issue with a CollectionView and test automation IDs

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

File Description
src/Core/src/Platform/Android/MauiTextView.cs Use StaticLayout from TextLayoutUtils for width measurement instead of the native Layout logic
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29542.cs Added a UI test that rotates the emulator and verifies label sizing in a CollectionView
src/Controls/tests/TestCases.HostApp/Issues/Issue29542.cs Provided a sample page with a CollectionView and navigation button to reproduce the clipping bug
Comments suppressed due to low confidence (1)

src/Core/src/Platform/Android/MauiTextView.cs:28

  • [nitpick] The local variable layout shadows the Layout property from the base class; consider renaming it to something more descriptive like staticLayout to avoid confusion.
var layout = TextLayoutUtils.CreateLayout(Text, Paint, availableWidth - totalPadding, Android.Text.Layout.Alignment.AlignNormal);

Comment on lines 22 to 29
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && Layout is not null)
if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && !string.IsNullOrEmpty(Text))
{
// Ensure the Layout is valid and measured before reading LineCount or GetLineWidth(i) to avoid unnecessary calculations.
if (Layout.Width > 0)
{
// Calculate the total width needed based on text content plus padding
int contentWidth = (int)Math.Ceiling(GetMaxLineWidth(Layout));
int totalPadding = CompoundPaddingLeft + CompoundPaddingRight;
int requiredWidth = contentWidth + totalPadding;

// Constrain to maximum available width from original spec
int availableWidth = MeasureSpec.GetSize(widthMeasureSpec);
int desiredWidth = Math.Min(requiredWidth, availableWidth);
widthMeasureSpec = MeasureSpec.MakeMeasureSpec(desiredWidth, MeasureSpecMode.AtMost);
}
int availableWidth = MeasureSpec.GetSize(widthMeasureSpec);
int totalPadding = CompoundPaddingLeft + CompoundPaddingRight;
var layout = TextLayoutUtils.CreateLayout(Text, Paint, availableWidth - totalPadding, Android.Text.Layout.Alignment.AlignNormal);
int contentWidth = (int)Math.Ceiling(GetMaxLineWidth(layout));
Copy link

Copilot AI May 23, 2025

Choose a reason for hiding this comment

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

Creating a new StaticLayout on every measure may be expensive; consider caching the layout when the text or width changes to avoid redundant allocations.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of creating a new layout every time OnMeasure is called, caching the layout if the text and width haven’t changed might improve performance

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have created a new layout only when necessary to improve performance. @jsuarezruiz

@@ -0,0 +1,25 @@
#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android.
Copy link

Copilot AI May 23, 2025

Choose a reason for hiding this comment

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

The preprocessor directive uses && which means the test will only compile when both macros are defined; likely you intended || so it’s excluded on either platform where it fails.

Suggested change
#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android.
#if TEST_FAILS_ON_CATALYST || TEST_FAILS_ON_WINDOWS //The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android.

Copilot uses AI. Check for mistakes.
@jsuarezruiz
Copy link
Contributor

/azp run MAUI-UITests-public

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && Layout is not null)
if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && !string.IsNullOrEmpty(Text))
Copy link
Contributor

Choose a reason for hiding this comment

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

You check !string.IsNullOrEmpty(Text), but if Text is empty later, will the component resize correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have verified this by dynamically setting the text to an empty string (string.Empty); it resizes correctly, just as it did before the fix.

Comment on lines 22 to 29
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && Layout is not null)
if (MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.AtMost && !string.IsNullOrEmpty(Text))
{
// Ensure the Layout is valid and measured before reading LineCount or GetLineWidth(i) to avoid unnecessary calculations.
if (Layout.Width > 0)
{
// Calculate the total width needed based on text content plus padding
int contentWidth = (int)Math.Ceiling(GetMaxLineWidth(Layout));
int totalPadding = CompoundPaddingLeft + CompoundPaddingRight;
int requiredWidth = contentWidth + totalPadding;

// Constrain to maximum available width from original spec
int availableWidth = MeasureSpec.GetSize(widthMeasureSpec);
int desiredWidth = Math.Min(requiredWidth, availableWidth);
widthMeasureSpec = MeasureSpec.MakeMeasureSpec(desiredWidth, MeasureSpecMode.AtMost);
}
int availableWidth = MeasureSpec.GetSize(widthMeasureSpec);
int totalPadding = CompoundPaddingLeft + CompoundPaddingRight;
var layout = TextLayoutUtils.CreateLayout(Text, Paint, availableWidth - totalPadding, Android.Text.Layout.Alignment.AlignNormal);
int contentWidth = (int)Math.Ceiling(GetMaxLineWidth(layout));
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of creating a new layout every time OnMeasure is called, caching the layout if the text and width haven’t changed might improve performance

// Calculate the required width based on the content and padding
int requiredWidth = contentWidth + totalPadding;
int desiredWidth = Math.Min(requiredWidth, availableWidth);
widthMeasureSpec = MeasureSpec.MakeMeasureSpec(desiredWidth, MeasureSpecMode.AtMost);
Copy link
Contributor

Choose a reason for hiding this comment

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

Assumes that desiredWidth is always valid. If padding is larger than available width, does this result in unexpected behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tested with a higher padding value, and it behaves the same as before the fix.
However, I have restricted the creation of a new layout if the padding exceeds the available width, as base.Measure can handle this scenario. @jsuarezruiz

@jsuarezruiz
Copy link
Contributor

/azp run MAUI-UITests-public

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

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

Is similar to 294542 but, could you include another test using the sample case from #29727?

@Ahamed-Ali
Copy link
Contributor Author

Is similar to 294542 but, could you include another test using the sample case from #29727?

Yes, I have added another test using the sample case from issue #29727. @jsuarezruiz

@jsuarezruiz
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen PureWeen added this to the .NET 9 SR8 milestone Jun 2, 2025
@PureWeen PureWeen added the p/0 Current heighest priority issues that we are targeting for a release. label Jun 2, 2025
@PureWeen PureWeen moved this from Todo to Ready To Review in MAUI SDK Ongoing Jun 2, 2025
@jsuarezruiz
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

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

LabelShouldSizeProperlyOnCollectionViewWithoutBlankSpace is failing

@github-project-automation github-project-automation bot moved this from Ready To Review to Changes Requested in MAUI SDK Ongoing Jun 4, 2025
@Ahamed-Ali
Copy link
Contributor Author

LabelShouldSizeProperlyOnCollectionViewWithoutBlankSpace is failing

I modified the test case to resolve the test failure. @PureWeen

@PureWeen
Copy link
Member

PureWeen commented Jun 6, 2025

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen PureWeen modified the milestones: .NET 9 SR8, .NET 9 SR9 Jun 10, 2025
Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

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

I'm a bit curious about the build up of changes from OnMeasure that started here

#28215

If I run all the tests from this PR and 28215 and I completely revert all the changes added into OnMeasure everything seems to work fine.

There's a slight adjustment of text size but the issue articulated here #27614 doesn't reproduce

Here's the diff when I revert this PR and 28215 against the tests added in 28215

image

Also here's the test added to this PR with all changes reverted

image

I'm not sure I'm seeing a use case yet for why we keep adding code into OnMeasure ?

@PureWeen
Copy link
Member

Closing in favor of
#30023

@PureWeen PureWeen closed this Jun 18, 2025
@github-project-automation github-project-automation bot moved this from Changes Requested to Done in MAUI SDK Ongoing Jun 18, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Jul 19, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-label Label, Span community ✨ Community Contribution p/0 Current heighest priority issues that we are targeting for a release. partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android

Projects

Status: Done

5 participants