diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs index 75249ff7e7..bcc83da06e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs +++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs @@ -31,6 +31,8 @@ namespace Avalonia.Media.TextFormatting var currentPosition = textLine.FirstTextSourceIndex; + var whiteSpaceWidth = 0.0; + for (var i = 0; i < lineImpl.TextRuns.Count; ++i) { var textRun = lineImpl.TextRuns[i]; @@ -41,15 +43,38 @@ namespace Avalonia.Media.TextFormatting continue; } - var lineBreakEnumerator = new LineBreakEnumerator(text.Span); - - while (lineBreakEnumerator.MoveNext(out var currentBreak)) + if (textRun is ShapedTextRun shapedText) { - if (!currentBreak.Required && currentBreak.PositionWrap != textRun.Length) + var glyphRun = shapedText.GlyphRun; + var shapedBuffer = shapedText.ShapedBuffer; + + var lineBreakEnumerator = new LineBreakEnumerator(text.Span); + + while (lineBreakEnumerator.MoveNext(out var currentBreak)) { - breakOportunities.Enqueue(currentPosition + currentBreak.PositionMeasure); + //Ignore the break at the end + if(currentPosition + currentBreak.PositionWrap == textLine.Length - TextRun.DefaultTextSourceLength) + { + break; + } + + if (!currentBreak.Required) + { + breakOportunities.Enqueue(currentPosition + currentBreak.PositionWrap); + + var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster); + + var characterIndex = currentPosition - offset + currentBreak.PositionWrap - 1; + var glyphIndex = glyphRun.FindGlyphIndex(characterIndex); + var glyphInfo = shapedBuffer[glyphIndex]; + + if (Codepoint.ReadAt(text.Span, currentBreak.PositionWrap - 1, out _).IsWhiteSpace) + { + whiteSpaceWidth += glyphInfo.GlyphAdvance; + } + } } - } + } currentPosition += textRun.Length; } @@ -59,7 +84,9 @@ namespace Avalonia.Media.TextFormatting return; } - var remainingSpace = Math.Max(0, paragraphWidth - lineImpl.WidthIncludingTrailingWhitespace); + //Adjust remaining space by whiteSpace width + var remainingSpace = Math.Max(0, paragraphWidth - lineImpl.Width) + whiteSpaceWidth; + var spacing = remainingSpace / breakOportunities.Count; currentPosition = textLine.FirstTextSourceIndex; @@ -82,17 +109,25 @@ namespace Avalonia.Media.TextFormatting { var characterIndex = breakOportunities.Dequeue(); - if (characterIndex < currentPosition) + var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster); + + if (characterIndex + offset < currentPosition) { continue; } - var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster); - var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset); + var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset - 1); var glyphInfo = shapedBuffer[glyphIndex]; + var isWhitespace = Codepoint.ReadAt(text.Span, characterIndex - 1 - currentPosition, out _).IsWhiteSpace; + shapedBuffer[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, - glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing); + glyphInfo.GlyphCluster, isWhitespace ? spacing : glyphInfo.GlyphAdvance + spacing); + + if (glyphIndex == shapedBuffer.Length - 1) + { + break; + } } glyphRun.GlyphInfos = shapedBuffer; diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 622727ccbe..6e59f1e2fc 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -627,14 +627,14 @@ namespace Avalonia.Controls.Presenters InvalidateArrange(); - var textWidth = TextLayout.OverhangLeading + TextLayout.WidthIncludingTrailingWhitespace + TextLayout.OverhangTrailing; + var textWidth = Math.Ceiling(TextLayout.MinTextWidth); return new Size(textWidth, TextLayout.Height); } protected override Size ArrangeOverride(Size finalSize) { - var textWidth = Math.Ceiling(TextLayout.OverhangLeading + TextLayout.WidthIncludingTrailingWhitespace + TextLayout.OverhangTrailing); + var textWidth = Math.Ceiling(TextLayout.MinTextWidth); if (finalSize.Width < textWidth) {