Skip to content

Commit

Permalink
Fix TextBlock TextAlignment issues when a HorizontalAlignment is defi…
Browse files Browse the repository at this point in the history
…ned (#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
  • Loading branch information
Gillibald authored Nov 10, 2024
1 parent b461267 commit d411bd2
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 22 deletions.
21 changes: 8 additions & 13 deletions src/Avalonia.Controls/TextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand All @@ -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)
Expand Down
14 changes: 5 additions & 9 deletions tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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);
}
}
Expand All @@ -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);
Expand Down
102 changes: 102 additions & 0 deletions tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}
}
9 changes: 9 additions & 0 deletions tests/Avalonia.RenderTests/TestSkip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d411bd2

Please sign in to comment.