Browse Source

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
pull/17483/head
Benedikt Stebner 1 year ago
committed by GitHub
parent
commit
d411bd2156
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 21
      src/Avalonia.Controls/TextBlock.cs
  2. 14
      tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
  3. 102
      tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
  4. 9
      tests/Avalonia.RenderTests/TestSkip.cs
  5. BIN
      tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png
  6. BIN
      tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png
  7. BIN
      tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png
  8. BIN
      tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png
  9. BIN
      tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png
  10. BIN
      tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png

21
src/Avalonia.Controls/TextBlock.cs

@ -653,7 +653,7 @@ namespace Avalonia.Controls
TextDecorations, TextDecorations,
Foreground); 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) defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
{ {
LineSpacing = LineSpacing LineSpacing = LineSpacing
@ -703,7 +703,7 @@ namespace Avalonia.Controls
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
var deflatedSize = availableSize.Deflate(padding); var deflatedSize = availableSize.Deflate(padding);
if(_constraint != deflatedSize) if (_constraint != deflatedSize)
{ {
//Reset TextLayout when the constraint is not matching. //Reset TextLayout when the constraint is not matching.
_textLayout?.Dispose(); _textLayout?.Dispose();
@ -733,9 +733,7 @@ namespace Avalonia.Controls
var width = textLayout.OverhangLeading + textLayout.WidthIncludingTrailingWhitespace + textLayout.OverhangTrailing; var width = textLayout.OverhangLeading + textLayout.WidthIncludingTrailingWhitespace + textLayout.OverhangTrailing;
var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1); var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1);
_constraint = size;
return size; return size;
} }
@ -747,15 +745,12 @@ namespace Avalonia.Controls
var availableSize = finalSize.Deflate(padding); var availableSize = finalSize.Deflate(padding);
//Fixes: #11019 //ToDo: Introduce a text run cache to be able to reuse shaped runs etc.
if (availableSize != _constraint) _textLayout?.Dispose();
{ _textLayout = null;
_textLayout?.Dispose(); _constraint = availableSize;
_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; var textLayout = TextLayout;
if (HasComplexContent) if (HasComplexContent)

14
tests/Avalonia.Controls.UnitTests/TextBlockTests.cs

@ -27,7 +27,7 @@ namespace Avalonia.Controls.UnitTests
} }
[Fact] [Fact]
public void Calling_Measure_Should_Update_Constraint_And_TextLayout() public void Calling_Measure_Should_Update_TextLayout()
{ {
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{ {
@ -39,8 +39,6 @@ namespace Avalonia.Controls.UnitTests
var textLayout = textBlock.TextLayout; var textLayout = textBlock.TextLayout;
Assert.Equal(new Size(110, 10), textBlock.Constraint);
textBlock.Measure(new Size(50, 100)); textBlock.Measure(new Size(50, 100));
Assert.NotEqual(textLayout, textBlock.TextLayout); Assert.NotEqual(textLayout, textBlock.TextLayout);
@ -60,13 +58,12 @@ namespace Avalonia.Controls.UnitTests
var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1); var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1);
Assert.Equal(constraint, textBlock.Constraint);
textBlock.Arrange(new Rect(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); textBlock.Measure(constraint);
@ -78,6 +75,7 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(constraint, textBlock.Constraint); Assert.Equal(constraint, textBlock.Constraint);
//TextLayout is recreated after arrange
Assert.NotEqual(textLayout, textBlock.TextLayout); Assert.NotEqual(textLayout, textBlock.TextLayout);
} }
} }
@ -93,8 +91,6 @@ namespace Avalonia.Controls.UnitTests
var textLayout = textBlock.TextLayout; var textLayout = textBlock.TextLayout;
Assert.Equal(new Size(110, 10), textBlock.Constraint);
var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1); var constraint = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height), 1, 1);
Assert.Equal(constraint, textBlock.DesiredSize); Assert.Equal(constraint, textBlock.DesiredSize);

102
tests/Avalonia.RenderTests/Controls/TextBlockTests.cs

@ -5,6 +5,7 @@ using Avalonia.Controls.Documents;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Xunit; using Xunit;
using static System.Net.Mime.MediaTypeNames;
#if AVALONIA_SKIA #if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests namespace Avalonia.Skia.RenderTests
@ -176,5 +177,106 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
await RenderToFile(target); await RenderToFile(target);
CompareImages(); 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);
}
} }
} }

9
tests/Avalonia.RenderTests/TestSkip.cs

@ -17,5 +17,14 @@ namespace Avalonia.Direct2D1.RenderTests
Skip = message; Skip = message;
} }
} }
public class Win32Theory : TheoryAttribute
{
public Win32Theory(string message)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
Skip = message;
}
}
} }

BIN
tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
tests/TestFiles/Direct2D1/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_150_NoWrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_NoWrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
tests/TestFiles/Skia/Controls/TextBlock/Should_Measure_Arrange_TextBlock_44_Wrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Loading…
Cancel
Save