diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 16fcf1eed98..377cfc1ba83 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -162,7 +162,7 @@ public class TextBlock : Control, IInlineHost nameof(Inlines), t => t.Inlines, (t, v) => t.Inlines = v); private TextLayout? _textLayout; - protected Size _constraint; + protected Size _constraint = Size.Infinity; protected IReadOnlyList? _textRuns; private InlineCollection? _inlines; @@ -701,13 +701,19 @@ protected override Size MeasureOverride(Size availableSize) { var scale = LayoutHelper.GetLayoutScale(this); var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); + var deflatedSize = availableSize.Deflate(padding); - _constraint = availableSize.Deflate(padding); - - //Reset TextLayout otherwise constraint might be outdated. - _textLayout?.Dispose(); - _textLayout = null; + if(_constraint != deflatedSize) + { + //Reset TextLayout when the constraint is not matching. + _textLayout?.Dispose(); + _textLayout = null; + _constraint = deflatedSize; + //Force arrange so text will be properly alligned. + InvalidateArrange(); + } + var inlines = Inlines; if (HasComplexContent) @@ -722,9 +728,16 @@ protected override Size MeasureOverride(Size availableSize) _textRuns = textRuns; } - var width = TextLayout.OverhangLeading + TextLayout.WidthIncludingTrailingWhitespace + TextLayout.OverhangTrailing; + //This implicitly recreated the TextLayout with a new constraint if we previously reset it. + var textLayout = TextLayout; + + var width = textLayout.OverhangLeading + textLayout.WidthIncludingTrailingWhitespace + textLayout.OverhangTrailing; + + var size = LayoutHelper.RoundLayoutSizeUp(new Size(width, textLayout.Height).Inflate(padding), 1, 1); - return new Size(width, TextLayout.Height).Inflate(padding); + _constraint = size; + + return size; } protected override Size ArrangeOverride(Size finalSize) @@ -732,19 +745,24 @@ protected override Size ArrangeOverride(Size finalSize) var scale = LayoutHelper.GetLayoutScale(this); var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); + var availableSize = finalSize.Deflate(padding); + //Fixes: #11019 - if (finalSize.Width < _constraint.Width) + if (availableSize != _constraint) { _textLayout?.Dispose(); _textLayout = null; - _constraint = finalSize.Deflate(padding); + _constraint = availableSize; } + //This implicitly recreated the TextLayout with a new constraint if we previously reset it. + var textLayout = TextLayout; + if (HasComplexContent) { var currentY = padding.Top; - foreach (var textLine in TextLayout.TextLines) + foreach (var textLine in textLayout.TextLines) { var currentX = padding.Left + textLine.Start; diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs index bd2532afbaa..8f9baed8d13 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs @@ -1,13 +1,9 @@ -using System; using Avalonia.Controls.Documents; using Avalonia.Controls.Templates; using Avalonia.Data; -using Avalonia.Input; +using Avalonia.Layout; using Avalonia.Media; -using Avalonia.Metadata; -using Avalonia.Rendering; using Avalonia.UnitTests; -using Moq; using Xunit; namespace Avalonia.Controls.UnitTests @@ -37,20 +33,74 @@ public void Calling_Measure_Should_Update_Constraint_And_TextLayout() { var textBlock = new TestTextBlock { Text = "Hello World" }; + Assert.Equal(Size.Infinity, textBlock.Constraint); + textBlock.Measure(new Size(100, 100)); var textLayout = textBlock.TextLayout; - Assert.Equal(new Size(100,100), textBlock.Constraint); + Assert.Equal(new Size(110, 10), textBlock.Constraint); textBlock.Measure(new Size(50, 100)); - Assert.Equal(new Size(50, 100), textBlock.Constraint); + Assert.NotEqual(textLayout, textBlock.TextLayout); + } + } + + [Fact] + public void Calling_Arrange_With_Different_Size_Should_Update_Constraint_And_TextLayout() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var textBlock = new TestTextBlock { Text = "Hello World" }; + + textBlock.Measure(Size.Infinity); + + var textLayout = textBlock.TextLayout; + + 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); + + Assert.Equal(textLayout, textBlock.TextLayout); + + textBlock.Measure(constraint); + + Assert.Equal(textLayout, textBlock.TextLayout); + + constraint += new Size(50, 0); + + textBlock.Arrange(new Rect(constraint)); + + Assert.Equal(constraint, textBlock.Constraint); Assert.NotEqual(textLayout, textBlock.TextLayout); } } + [Fact] + public void Calling_Measure_With_Infinite_Space_Should_Set_DesiredSize() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var textBlock = new TestTextBlock { Text = "Hello World" }; + + textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + 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); + } + } + [Fact] public void Changing_InlinesCollection_Should_Invalidate_Measure() {