diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
index 9318fcc68e..b116249fd4 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
@@ -31,7 +31,8 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.WrapWithOverflow:
case TextWrapping.Wrap:
{
- textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties);
+ textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties,
+ nextLineBreak);
break;
}
default:
@@ -118,7 +119,7 @@ namespace Avalonia.Media.TextFormatting
/// The text run's.
/// The length to split at.
/// The split text runs.
- internal static SplitTextRunsResult SplitTextRuns(IReadOnlyList textRuns, int length)
+ internal static SplitTextRunsResult SplitTextRuns(List textRuns, int length)
{
var currentLength = 0;
@@ -134,13 +135,13 @@ namespace Avalonia.Media.TextFormatting
var firstCount = currentRun.GlyphRun.Characters.Length >= 1 ? i + 1 : i;
- var first = new ShapedTextCharacters[firstCount];
+ var first = new List(firstCount);
if (firstCount > 1)
{
for (var j = 0; j < i; j++)
{
- first[j] = textRuns[j];
+ first.Add(textRuns[j]);
}
}
@@ -148,7 +149,7 @@ namespace Avalonia.Media.TextFormatting
if (currentLength + currentRun.GlyphRun.Characters.Length == length)
{
- var second = new ShapedTextCharacters[secondCount];
+ var second = new List(secondCount);
var offset = currentRun.GlyphRun.Characters.Length > 1 ? 1 : 0;
@@ -156,11 +157,11 @@ namespace Avalonia.Media.TextFormatting
{
for (var j = 0; j < secondCount; j++)
{
- second[j] = textRuns[i + j + offset];
+ second.Add(textRuns[i + j + offset]);
}
}
- first[i] = currentRun;
+ first.Add(currentRun);
return new SplitTextRunsResult(first, second);
}
@@ -168,22 +169,22 @@ namespace Avalonia.Media.TextFormatting
{
secondCount++;
- var second = new ShapedTextCharacters[secondCount];
+ var second = new List(secondCount);
+
+ var split = currentRun.Split(length - currentLength);
+
+ first.Add(split.First);
+
+ second.Add(split.Second);
if (secondCount > 0)
{
for (var j = 1; j < secondCount; j++)
{
- second[j] = textRuns[i + j];
+ second.Add(textRuns[i + j]);
}
}
- var split = currentRun.Split(length - currentLength);
-
- first[i] = split.First;
-
- second[0] = split.Second;
-
return new SplitTextRunsResult(first, second);
}
}
@@ -201,7 +202,7 @@ namespace Avalonia.Media.TextFormatting
///
/// The formatted text runs.
///
- private static IReadOnlyList FetchTextRuns(ITextSource textSource,
+ private static List FetchTextRuns(ITextSource textSource,
int firstTextSourceIndex, TextLineBreak previousLineBreak, out TextLineBreak nextLineBreak)
{
nextLineBreak = default;
@@ -212,8 +213,10 @@ namespace Avalonia.Media.TextFormatting
if (previousLineBreak != null)
{
- foreach (var shapedCharacters in previousLineBreak.RemainingCharacters)
+ for (var index = 0; index < previousLineBreak.RemainingCharacters.Count; index++)
{
+ var shapedCharacters = previousLineBreak.RemainingCharacters[index];
+
if (shapedCharacters == null)
{
continue;
@@ -225,6 +228,14 @@ namespace Avalonia.Media.TextFormatting
{
var splitResult = SplitTextRuns(textRuns, currentLength + runLineBreak.PositionWrap);
+ if (++index < previousLineBreak.RemainingCharacters.Count)
+ {
+ for (; index < previousLineBreak.RemainingCharacters.Count; index++)
+ {
+ splitResult.Second.Add(previousLineBreak.RemainingCharacters[index]);
+ }
+ }
+
nextLineBreak = new TextLineBreak(splitResult.Second);
return splitResult.First;
@@ -323,9 +334,10 @@ namespace Avalonia.Media.TextFormatting
/// The text range that is covered by the text runs.
/// The paragraph width.
/// The text paragraph properties.
+ /// The current line break if the line was explicitly broken.
/// The wrapped text line.
- private static TextLine PerformTextWrapping(IReadOnlyList textRuns, TextRange textRange,
- double paragraphWidth, TextParagraphProperties paragraphProperties)
+ private static TextLine PerformTextWrapping(List textRuns, TextRange textRange,
+ double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak currentLineBreak)
{
var availableWidth = paragraphWidth;
var currentWidth = 0.0;
@@ -388,8 +400,22 @@ namespace Avalonia.Media.TextFormatting
var textLineMetrics = TextLineMetrics.Create(splitResult.First,
new TextRange(textRange.Start, currentLength), paragraphWidth, paragraphProperties);
- var lineBreak = splitResult.Second != null && splitResult.Second.Count > 0 ?
- new TextLineBreak(splitResult.Second) :
+ var remainingCharacters = splitResult.Second;
+
+ if (currentLineBreak?.RemainingCharacters != null)
+ {
+ if (remainingCharacters != null)
+ {
+ remainingCharacters.AddRange(currentLineBreak.RemainingCharacters);
+ }
+ else
+ {
+ remainingCharacters = new List(currentLineBreak.RemainingCharacters);
+ }
+ }
+
+ var lineBreak = remainingCharacters != null && remainingCharacters.Count > 0 ?
+ new TextLineBreak(remainingCharacters) :
null;
return new TextLineImpl(splitResult.First, textLineMetrics, lineBreak);
@@ -403,7 +429,10 @@ namespace Avalonia.Media.TextFormatting
}
return new TextLineImpl(textRuns,
- TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties));
+ TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties),
+ currentLineBreak?.RemainingCharacters != null ?
+ new TextLineBreak(currentLineBreak.RemainingCharacters) :
+ null);
}
///
@@ -434,7 +463,7 @@ namespace Avalonia.Media.TextFormatting
internal readonly struct SplitTextRunsResult
{
- public SplitTextRunsResult(IReadOnlyList first, IReadOnlyList second)
+ public SplitTextRunsResult(List first, List second)
{
First = first;
@@ -447,7 +476,7 @@ namespace Avalonia.Media.TextFormatting
///
/// The first text runs.
///
- public IReadOnlyList First { get; }
+ public List First { get; }
///
/// Gets the second text runs.
@@ -455,7 +484,7 @@ namespace Avalonia.Media.TextFormatting
///
/// The second text runs.
///
- public IReadOnlyList Second { get; }
+ public List Second { get; }
}
private struct TextRunEnumerator
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
index 14602a2560..fa7d6cb4bf 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
@@ -183,7 +183,10 @@ namespace Avalonia.Media.TextFormatting
var glyphRun = TextShaper.Current.ShapeText(new ReadOnlySlice(s_empty, startingIndex, 1),
properties.Typeface, properties.FontRenderingEmSize, properties.CultureInfo);
- var textRuns = new[] { new ShapedTextCharacters(glyphRun, _paragraphProperties.DefaultTextRunProperties) };
+ var textRuns = new List
+ {
+ new ShapedTextCharacters(glyphRun, _paragraphProperties.DefaultTextRunProperties)
+ };
return new TextLineImpl(textRuns,
TextLineMetrics.Create(textRuns, new TextRange(startingIndex, 1), MaxWidth, _paragraphProperties));
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
index 435752160e..8b44e32c48 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
@@ -6,9 +6,9 @@ namespace Avalonia.Media.TextFormatting
{
internal class TextLineImpl : TextLine
{
- private readonly IReadOnlyList _textRuns;
+ private readonly List _textRuns;
- public TextLineImpl(IReadOnlyList textRuns, TextLineMetrics lineMetrics,
+ public TextLineImpl(List textRuns, TextLineMetrics lineMetrics,
TextLineBreak lineBreak = null, bool hasCollapsed = false)
{
_textRuns = textRuns;
diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
index b0384a1fdf..61075171fe 100644
--- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs
+++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
@@ -123,10 +123,7 @@ namespace Avalonia.Skia
return;
}
- if (offsetBuffer == null)
- {
- offsetBuffer = new Vector[glyphPositions.Length];
- }
+ offsetBuffer ??= new Vector[glyphPositions.Length];
var offsetX = position.XOffset * textScale;
@@ -138,10 +135,7 @@ namespace Avalonia.Skia
private static void SetAdvance(ReadOnlySpan glyphPositions, int index, double textScale,
ref double[] advanceBuffer)
{
- if (advanceBuffer == null)
- {
- advanceBuffer = new double[glyphPositions.Length];
- }
+ advanceBuffer ??= new double[glyphPositions.Length];
// Depends on direction of layout
// advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale;