Skip to content

Commit d411bd2

Browse files
authored
Fix TextBlock TextAlignment issues when a HorizontalAlignment is defined (#17402)
* Always measure TextBlock with infinite width * Make sure the constraint is always fulfilled * Add some tests * Adjust tests because we no longer retain the TextLayout in the arrange pass
1 parent b461267 commit d411bd2

10 files changed

+124
-22
lines changed

src/Avalonia.Controls/TextBlock.cs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ protected virtual TextLayout CreateTextLayout(string? text)
653653
TextDecorations,
654654
Foreground);
655655

656-
var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false,
656+
var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, IsMeasureValid ? TextAlignment : TextAlignment.Left, true, false,
657657
defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
658658
{
659659
LineSpacing = LineSpacing
@@ -703,7 +703,7 @@ protected override Size MeasureOverride(Size availableSize)
703703
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
704704
var deflatedSize = availableSize.Deflate(padding);
705705

706-
if(_constraint != deflatedSize)
706+
if (_constraint != deflatedSize)
707707
{
708708
//Reset TextLayout when the constraint is not matching.
709709
_textLayout?.Dispose();
@@ -733,9 +733,7 @@ protected override Size MeasureOverride(Size availableSize)
733733

734734
var width = textLayout.OverhangLeading + textLayout.WidthIncludingTrailingWhitespace + textLayout.OverhangTrailing;
735735

736-
var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1);
737-
738-
_constraint = size;
736+
var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1);
739737

740738
return size;
741739
}
@@ -747,15 +745,12 @@ protected override Size ArrangeOverride(Size finalSize)
747745

748746
var availableSize = finalSize.Deflate(padding);
749747

750-
//Fixes: #11019
751-
if (availableSize != _constraint)
752-
{
753-
_textLayout?.Dispose();
754-
_textLayout = null;
755-
_constraint = availableSize;
756-
}
748+
//ToDo: Introduce a text run cache to be able to reuse shaped runs etc.
749+
_textLayout?.Dispose();
750+
_textLayout = null;
751+
_constraint = availableSize;
757752

758-
//This implicitly recreated the TextLayout with a new constraint if we previously reset it.
753+
//This implicitly recreated the TextLayout with a new constraint.
759754
var textLayout = TextLayout;
760755

761756
if (HasComplexContent)

tests/Avalonia.Controls.UnitTests/TextBlockTests.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void Default_Text_Value_Should_Be_Null()
2727
}
2828

2929
[Fact]
30-
public void Calling_Measure_Should_Update_Constraint_And_TextLayout()
30+
public void Calling_Measure_Should_Update_TextLayout()
3131
{
3232
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
3333
{
@@ -39,8 +39,6 @@ public void Calling_Measure_Should_Update_Constraint_And_TextLayout()
3939

4040
var textLayout = textBlock.TextLayout;
4141

42-
Assert.Equal(new Size(110, 10), textBlock.Constraint);
43-
4442
textBlock.Measure(new Size(50, 100));
4543

4644
Assert.NotEqual(textLayout, textBlock.TextLayout);
@@ -60,13 +58,12 @@ public void Calling_Arrange_With_Different_Size_Should_Update_Constraint_And_Tex
6058

6159
var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1);
6260

63-
Assert.Equal(constraint, textBlock.Constraint);
64-
6561
textBlock.Arrange(new Rect(constraint));
6662

67-
Assert.Equal(constraint, textBlock.Constraint);
63+
//TextLayout is recreated after arrange
64+
textLayout = textBlock.TextLayout;
6865

69-
Assert.Equal(textLayout, textBlock.TextLayout);
66+
Assert.Equal(constraint, textBlock.Constraint);
7067

7168
textBlock.Measure(constraint);
7269

@@ -78,6 +75,7 @@ public void Calling_Arrange_With_Different_Size_Should_Update_Constraint_And_Tex
7875

7976
Assert.Equal(constraint, textBlock.Constraint);
8077

78+
//TextLayout is recreated after arrange
8179
Assert.NotEqual(textLayout, textBlock.TextLayout);
8280
}
8381
}
@@ -93,8 +91,6 @@ public void Calling_Measure_With_Infinite_Space_Should_Set_DesiredSize()
9391

9492
var textLayout = textBlock.TextLayout;
9593

96-
Assert.Equal(new Size(110, 10), textBlock.Constraint);
97-
9894
var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1);
9995

10096
Assert.Equal(constraint, textBlock.DesiredSize);

tests/Avalonia.RenderTests/Controls/TextBlockTests.cs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Avalonia.Layout;
66
using Avalonia.Media;
77
using Xunit;
8+
using static System.Net.Mime.MediaTypeNames;
89

910
#if AVALONIA_SKIA
1011
namespace Avalonia.Skia.RenderTests
@@ -176,5 +177,106 @@ public async Task Should_Draw_Run_With_Background()
176177
await RenderToFile(target);
177178
CompareImages();
178179
}
180+
181+
182+
[InlineData(150, 200, TextWrapping.NoWrap)]
183+
[InlineData(44, 200, TextWrapping.NoWrap)]
184+
[InlineData(44, 400, TextWrapping.Wrap)]
185+
[Win32Theory("Has text")]
186+
public async Task Should_Measure_Arrange_TextBlock(double width, double height, TextWrapping textWrapping)
187+
{
188+
var text = "Hello World";
189+
190+
var target = new StackPanel { Width = 200, Height = height };
191+
192+
target.Children.Add(new TextBlock
193+
{
194+
Text = text,
195+
Background = Brushes.Red,
196+
HorizontalAlignment = HorizontalAlignment.Left,
197+
TextAlignment = TextAlignment.Left,
198+
Width = width,
199+
TextWrapping = textWrapping
200+
});
201+
target.Children.Add(new TextBlock
202+
{
203+
Text = text,
204+
Background = Brushes.Red,
205+
HorizontalAlignment = HorizontalAlignment.Left,
206+
TextAlignment = TextAlignment.Center,
207+
Width = width,
208+
TextWrapping = textWrapping
209+
});
210+
target.Children.Add(new TextBlock
211+
{
212+
Text = text,
213+
Background = Brushes.Red,
214+
HorizontalAlignment = HorizontalAlignment.Left,
215+
TextAlignment = TextAlignment.Right,
216+
Width = width,
217+
TextWrapping = textWrapping
218+
});
219+
220+
target.Children.Add(new TextBlock
221+
{
222+
Text = text,
223+
Background = Brushes.Red,
224+
HorizontalAlignment = HorizontalAlignment.Center,
225+
TextAlignment = TextAlignment.Left,
226+
Width = width,
227+
TextWrapping = textWrapping
228+
});
229+
target.Children.Add(new TextBlock
230+
{
231+
Text = text,
232+
Background = Brushes.Red,
233+
HorizontalAlignment = HorizontalAlignment.Center,
234+
TextAlignment = TextAlignment.Center,
235+
Width = width,
236+
TextWrapping = textWrapping
237+
});
238+
target.Children.Add(new TextBlock
239+
{
240+
Text = text,
241+
Background = Brushes.Red,
242+
HorizontalAlignment = HorizontalAlignment.Center,
243+
TextAlignment = TextAlignment.Right,
244+
Width = width,
245+
TextWrapping = textWrapping
246+
});
247+
248+
target.Children.Add(new TextBlock
249+
{
250+
Text = text,
251+
Background = Brushes.Red,
252+
HorizontalAlignment = HorizontalAlignment.Right,
253+
TextAlignment = TextAlignment.Left,
254+
Width = width,
255+
TextWrapping = textWrapping
256+
});
257+
target.Children.Add(new TextBlock
258+
{
259+
Text = text,
260+
Background = Brushes.Red,
261+
HorizontalAlignment = HorizontalAlignment.Right,
262+
TextAlignment = TextAlignment.Center,
263+
Width = width,
264+
TextWrapping = textWrapping
265+
});
266+
target.Children.Add(new TextBlock
267+
{
268+
Text = text,
269+
Background = Brushes.Red,
270+
HorizontalAlignment = HorizontalAlignment.Right,
271+
TextAlignment = TextAlignment.Right,
272+
Width = width,
273+
TextWrapping = textWrapping
274+
});
275+
276+
var testName = $"Should_Measure_Arrange_TextBlock_{width}_{textWrapping}";
277+
278+
await RenderToFile(target, testName);
279+
CompareImages(testName);
280+
}
179281
}
180282
}

tests/Avalonia.RenderTests/TestSkip.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,14 @@ public Win32Fact(string message)
1717
Skip = message;
1818
}
1919
}
20+
21+
public class Win32Theory : TheoryAttribute
22+
{
23+
public Win32Theory(string message)
24+
{
25+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
26+
Skip = message;
27+
}
28+
}
2029
}
2130

Loading

0 commit comments

Comments
 (0)