diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index afa7d3542ae..0afafe44b87 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -653,7 +653,7 @@ protected virtual TextLayout CreateTextLayout(string? text) TextDecorations, Foreground); - var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false, + var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, IsMeasureValid ? TextAlignment : TextAlignment.Left, true, false, defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing) { LineSpacing = LineSpacing @@ -703,7 +703,7 @@ protected override Size MeasureOverride(Size availableSize) var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); var deflatedSize = availableSize.Deflate(padding); - if(_constraint != deflatedSize) + if (_constraint != deflatedSize) { //Reset TextLayout when the constraint is not matching. _textLayout?.Dispose(); @@ -733,9 +733,7 @@ protected override Size MeasureOverride(Size availableSize) var width = textLayout.OverhangLeading + textLayout.WidthIncludingTrailingWhitespace + textLayout.OverhangTrailing; - var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1); - - _constraint = size; + var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1); return size; } @@ -747,15 +745,12 @@ protected override Size ArrangeOverride(Size finalSize) var availableSize = finalSize.Deflate(padding); - //Fixes: #11019 - if (availableSize != _constraint) - { - _textLayout?.Dispose(); - _textLayout = null; - _constraint = availableSize; - } + //ToDo: Introduce a text run cache to be able to reuse shaped runs etc. + _textLayout?.Dispose(); + _textLayout = null; + _constraint = availableSize; - //This implicitly recreated the TextLayout with a new constraint if we previously reset it. + //This implicitly recreated the TextLayout with a new constraint. var textLayout = TextLayout; if (HasComplexContent) diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs index 8f9baed8d13..3d7c6e6c1bd 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs @@ -27,7 +27,7 @@ public void Default_Text_Value_Should_Be_Null() } [Fact] - public void Calling_Measure_Should_Update_Constraint_And_TextLayout() + public void Calling_Measure_Should_Update_TextLayout() { using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) { @@ -39,8 +39,6 @@ public void Calling_Measure_Should_Update_Constraint_And_TextLayout() var textLayout = textBlock.TextLayout; - Assert.Equal(new Size(110, 10), textBlock.Constraint); - textBlock.Measure(new Size(50, 100)); Assert.NotEqual(textLayout, textBlock.TextLayout); @@ -60,13 +58,12 @@ public void Calling_Arrange_With_Different_Size_Should_Update_Constraint_And_Tex var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1); - Assert.Equal(constraint, textBlock.Constraint); - textBlock.Arrange(new Rect(constraint)); - Assert.Equal(constraint, textBlock.Constraint); + //TextLayout is recreated after arrange + textLayout = textBlock.TextLayout; - Assert.Equal(textLayout, textBlock.TextLayout); + Assert.Equal(constraint, textBlock.Constraint); textBlock.Measure(constraint); @@ -78,6 +75,7 @@ public void Calling_Arrange_With_Different_Size_Should_Update_Constraint_And_Tex Assert.Equal(constraint, textBlock.Constraint); + //TextLayout is recreated after arrange Assert.NotEqual(textLayout, textBlock.TextLayout); } } @@ -93,8 +91,6 @@ public void Calling_Measure_With_Infinite_Space_Should_Set_DesiredSize() var textLayout = textBlock.TextLayout; - Assert.Equal(new Size(110, 10), textBlock.Constraint); - var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1); Assert.Equal(constraint, textBlock.DesiredSize); diff --git a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs index 14c56299e3a..473461dcb45 100644 --- a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs +++ b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs @@ -5,6 +5,7 @@ using Avalonia.Layout; using Avalonia.Media; using Xunit; +using static System.Net.Mime.MediaTypeNames; #if AVALONIA_SKIA namespace Avalonia.Skia.RenderTests @@ -176,5 +177,106 @@ public async Task Should_Draw_Run_With_Background() await RenderToFile(target); CompareImages(); } + + + [InlineData(150, 200, TextWrapping.NoWrap)] + [InlineData(44, 200, TextWrapping.NoWrap)] + [InlineData(44, 400, TextWrapping.Wrap)] + [Win32Theory("Has text")] + public async Task Should_Measure_Arrange_TextBlock(double width, double height, TextWrapping textWrapping) + { + var text = "Hello World"; + + var target = new StackPanel { Width = 200, Height = height }; + + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Left, + TextAlignment = TextAlignment.Left, + Width = width, + TextWrapping = textWrapping + }); + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Left, + TextAlignment = TextAlignment.Center, + Width = width, + TextWrapping = textWrapping + }); + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Left, + TextAlignment = TextAlignment.Right, + Width = width, + TextWrapping = textWrapping + }); + + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Center, + TextAlignment = TextAlignment.Left, + Width = width, + TextWrapping = textWrapping + }); + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Center, + TextAlignment = TextAlignment.Center, + Width = width, + TextWrapping = textWrapping + }); + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Center, + TextAlignment = TextAlignment.Right, + Width = width, + TextWrapping = textWrapping + }); + + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Right, + TextAlignment = TextAlignment.Left, + Width = width, + TextWrapping = textWrapping + }); + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Right, + TextAlignment = TextAlignment.Center, + Width = width, + TextWrapping = textWrapping + }); + target.Children.Add(new TextBlock + { + Text = text, + Background = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Right, + TextAlignment = TextAlignment.Right, + Width = width, + TextWrapping = textWrapping + }); + + var testName = $"Should_Measure_Arrange_TextBlock_{width}_{textWrapping}"; + + await RenderToFile(target, testName); + CompareImages(testName); + } } } diff --git a/tests/Avalonia.RenderTests/TestSkip.cs b/tests/Avalonia.RenderTests/TestSkip.cs index 75407332e3a..c09a25b4c1f 100644 --- a/tests/Avalonia.RenderTests/TestSkip.cs +++ b/tests/Avalonia.RenderTests/TestSkip.cs @@ -17,5 +17,14 @@ public Win32Fact(string message) Skip = message; } } + + public class Win32Theory : TheoryAttribute + { + public Win32Theory(string message) + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + Skip = message; + } + } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png new file mode 100644 index 00000000000..5b83d6404d5 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png new file mode 100644 index 00000000000..1f5f1e5bc51 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png new file mode 100644 index 00000000000..db493fa9e7d Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png differ diff --git a/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png new file mode 100644 index 00000000000..bcda7c59ee3 Binary files /dev/null and b/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png differ diff --git a/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png new file mode 100644 index 00000000000..0d407ba1ee5 Binary files /dev/null and b/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png differ diff --git a/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png new file mode 100644 index 00000000000..39f21bd8077 Binary files /dev/null and b/tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png differ