Skip to content

Commit 0fd4268

Browse files
Fix Android StaticLayout crash with comprehensive negative width prevention
Co-authored-by: PaulAndersonS <[email protected]>
1 parent eb79439 commit 0fd4268

File tree

3 files changed

+43
-11
lines changed

3 files changed

+43
-11
lines changed

maui/src/Core/Extensions/CanvasExtensions.Android.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Android.Graphics;
33
using Android.Text;
44
using Microsoft.Maui.Graphics.Platform;
5+
using System;
56
using Font = Microsoft.Maui.Font;
67
using Paint = Android.Graphics.Paint;
78
using Rect = Microsoft.Maui.Graphics.Rect;
@@ -106,7 +107,9 @@ public static void DrawText(this ICanvas canvas, string value, Rect rect, Horizo
106107
alignment = Android.Text.Layout.Alignment.AlignOpposite;
107108
}
108109

109-
StaticTextLayout layout = new StaticTextLayout(value, paint, (int)(rect.Width * nativeCanvas.DisplayScale), alignment, 1.0f, 0.0f, false);
110+
// Ensure width is positive to prevent Android StaticLayout crashes
111+
int layoutWidth = Math.Max(1, (int)(rect.Width * nativeCanvas.DisplayScale));
112+
StaticTextLayout layout = new StaticTextLayout(value, paint, layoutWidth, alignment, 1.0f, 0.0f, false);
110113
double rectDensityHeight = rect.Height * nativeCanvas.DisplayScale;
111114
//// Check the layout does not accommodate the text inside the specified rect then
112115
//// restrict the text rendering with line count.

maui/src/TextInputLayout/SfTextInputLayout.Methods.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Syncfusion.Maui.Toolkit.NumericUpDown;
55
using Syncfusion.Maui.Toolkit.NumericEntry;
66
using Syncfusion.Maui.Toolkit.Themes;
7+
using System;
78
using Path = Microsoft.Maui.Controls.Shapes.Path;
89

910
namespace Syncfusion.Maui.Toolkit.TextInputLayout
@@ -1044,7 +1045,7 @@ void UpdateContentPosition()
10441045
_viewBounds.X = (int)_leadViewWidth;
10451046
UpdatePosition();
10461047
_viewBounds.Y = 0;
1047-
_viewBounds.Width = (int)(Width - _leadViewWidth - _trailViewWidth);
1048+
_viewBounds.Width = (int)Math.Max(1, Width - _leadViewWidth - _trailViewWidth);
10481049
_viewBounds.Height = (int)Height;
10491050

10501051
if (EnablePasswordVisibilityToggle && !ShowUpDownButton)
@@ -1061,6 +1062,9 @@ void UpdateContentPosition()
10611062
_viewBounds.Width -= (float)(IconSize * (IsUpDownVerticalAlignment ? 1 : 2));
10621063
}
10631064

1065+
// Ensure width doesn't become negative after all subtractions
1066+
_viewBounds.Width = (float)Math.Max(1, _viewBounds.Width);
1067+
10641068
if (_viewBounds.Height >= 0 && _viewBounds.Width >= 0)
10651069
{
10661070
AbsoluteLayout.SetLayoutBounds(Content, _viewBounds);
@@ -1454,7 +1458,7 @@ void UpdateHelperTextPosition()
14541458
_helperTextRect.Y = (int)(Height - TotalAssistiveTextHeight());
14551459
}
14561460

1457-
_helperTextRect.Width = (int)(Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
1461+
_helperTextRect.Width = (int)Math.Max(1, Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
14581462
_helperTextRect.Height = HelperTextSize.Height;
14591463

14601464
if (IsRTL)
@@ -1471,11 +1475,13 @@ double GetAssistiveTextWidth()
14711475

14721476
if (Width >= 0)
14731477
{
1474-
return Width - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
1478+
var calculatedWidth = Width - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
1479+
return Math.Max(1, calculatedWidth);
14751480
}
14761481
else if (WidthRequest != -1)
14771482
{
1478-
return WidthRequest - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
1483+
var calculatedWidth = WidthRequest - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
1484+
return Math.Max(1, calculatedWidth);
14791485
}
14801486
else
14811487
{
@@ -1495,11 +1501,13 @@ double GetHintTextWidth()
14951501

14961502
if (Width >= 0)
14971503
{
1498-
return Width - (IsNone ? 0 : ((2 * StartX) + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
1504+
var calculatedWidth = Width - (IsNone ? 0 : ((2 * StartX) + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
1505+
return Math.Max(1, calculatedWidth);
14991506
}
15001507
else if (WidthRequest != -1)
15011508
{
1502-
return WidthRequest - (IsNone ? 0 : 2 * (StartX + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
1509+
var calculatedWidth = WidthRequest - (IsNone ? 0 : 2 * (StartX + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
1510+
return Math.Max(1, calculatedWidth);
15031511
}
15041512
else
15051513
{
@@ -1525,7 +1533,7 @@ void UpdateErrorTextPosition()
15251533
_errorTextRect.Y = (int)(Height - TotalAssistiveTextHeight());
15261534
}
15271535

1528-
_errorTextRect.Width = (int)(Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
1536+
_errorTextRect.Width = (int)Math.Max(1, Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
15291537
_errorTextRect.Height = ErrorTextSize.Height;
15301538

15311539
if (IsRTL)
@@ -1585,11 +1593,11 @@ void UpdateOutlineRectF()
15851593
UpdateTrailViewWidthForBorder();
15861594
if (BaseLineMaxHeight <= 2)
15871595
{
1588-
_outlineRectF.Width = (float)((Width - (BaseLineMaxHeight * 2)) - _leadViewWidth - _trailViewWidth);
1596+
_outlineRectF.Width = (float)Math.Max(1, (Width - (BaseLineMaxHeight * 2)) - _leadViewWidth - _trailViewWidth);
15891597
}
15901598
else
15911599
{
1592-
_outlineRectF.Width = (float)((Width - (BaseLineMaxHeight)) - _leadViewWidth - _trailViewWidth);
1600+
_outlineRectF.Width = (float)Math.Max(1, (Width - (BaseLineMaxHeight)) - _leadViewWidth - _trailViewWidth);
15931601
}
15941602

15951603
_outlineRectF.Height = (float)(Height - _outlineRectF.Y - TotalAssistiveTextHeight() - AssistiveLabelPadding);
@@ -1623,7 +1631,7 @@ void UpdateBackgroundRectF()
16231631
UpdateTrailViewWidthForBorder();
16241632
_backgroundRectF.X = (float)_leadViewWidth;
16251633
_backgroundRectF.Y = 0;
1626-
_backgroundRectF.Width = (float)(Width - _leadViewWidth - _trailViewWidth);
1634+
_backgroundRectF.Width = (float)Math.Max(1, Width - _leadViewWidth - _trailViewWidth);
16271635
if (BaseLineMaxHeight <= 2)
16281636
{
16291637
_backgroundRectF.Height = (float)(Height - TotalAssistiveTextHeight() - AssistiveLabelPadding);

maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Layout/SfTextInputLayoutUnitTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,27 @@ public void VerySmallContainerWidthWithLeadingAndTrailingViews_ShouldNotCrash()
12361236
Assert.True(inputLayout.ShowTrailingView);
12371237
}
12381238

1239+
[Fact]
1240+
public void VerySmallContainerWidthWithErrorAndHelperText_ShouldNotCrash()
1241+
{
1242+
// Test for the fix where very small container widths with error/helper text cause crashes
1243+
var inputLayout = new SfTextInputLayout
1244+
{
1245+
Content = new Entry { Text = "Test" },
1246+
Hint = "Name",
1247+
ErrorText = "This is an error message",
1248+
HelperText = "This is a helper message",
1249+
WidthRequest = 10, // Very small width that could cause negative text layout bounds
1250+
HeightRequest = 50
1251+
};
1252+
1253+
// The control should handle very small dimensions gracefully without throwing exceptions
1254+
Assert.NotNull(inputLayout);
1255+
Assert.NotNull(inputLayout.Content);
1256+
Assert.Equal("This is an error message", inputLayout.ErrorText);
1257+
Assert.Equal("This is a helper message", inputLayout.HelperText);
1258+
}
1259+
12391260

12401261
#endregion
12411262
}

0 commit comments

Comments
 (0)