|
|
|
@ -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 |
|
|
|
/// <param name="textRuns">The text run's.</param>
|
|
|
|
/// <param name="length">The length to split at.</param>
|
|
|
|
/// <returns>The split text runs.</returns>
|
|
|
|
internal static SplitTextRunsResult SplitTextRuns(IReadOnlyList<ShapedTextCharacters> textRuns, int length) |
|
|
|
internal static SplitTextRunsResult SplitTextRuns(List<ShapedTextCharacters> 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<ShapedTextCharacters>(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<ShapedTextCharacters>(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<ShapedTextCharacters>(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 |
|
|
|
/// <returns>
|
|
|
|
/// The formatted text runs.
|
|
|
|
/// </returns>
|
|
|
|
private static IReadOnlyList<ShapedTextCharacters> FetchTextRuns(ITextSource textSource, |
|
|
|
private static List<ShapedTextCharacters> 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 |
|
|
|
/// <param name="textRange">The text range that is covered by the text runs.</param>
|
|
|
|
/// <param name="paragraphWidth">The paragraph width.</param>
|
|
|
|
/// <param name="paragraphProperties">The text paragraph properties.</param>
|
|
|
|
/// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param>
|
|
|
|
/// <returns>The wrapped text line.</returns>
|
|
|
|
private static TextLine PerformTextWrapping(IReadOnlyList<ShapedTextCharacters> textRuns, TextRange textRange, |
|
|
|
double paragraphWidth, TextParagraphProperties paragraphProperties) |
|
|
|
private static TextLine PerformTextWrapping(List<ShapedTextCharacters> 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<ShapedTextCharacters>(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); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -434,7 +463,7 @@ namespace Avalonia.Media.TextFormatting |
|
|
|
|
|
|
|
internal readonly struct SplitTextRunsResult |
|
|
|
{ |
|
|
|
public SplitTextRunsResult(IReadOnlyList<ShapedTextCharacters> first, IReadOnlyList<ShapedTextCharacters> second) |
|
|
|
public SplitTextRunsResult(List<ShapedTextCharacters> first, List<ShapedTextCharacters> second) |
|
|
|
{ |
|
|
|
First = first; |
|
|
|
|
|
|
|
@ -447,7 +476,7 @@ namespace Avalonia.Media.TextFormatting |
|
|
|
/// <value>
|
|
|
|
/// The first text runs.
|
|
|
|
/// </value>
|
|
|
|
public IReadOnlyList<ShapedTextCharacters> First { get; } |
|
|
|
public List<ShapedTextCharacters> First { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the second text runs.
|
|
|
|
@ -455,7 +484,7 @@ namespace Avalonia.Media.TextFormatting |
|
|
|
/// <value>
|
|
|
|
/// The second text runs.
|
|
|
|
/// </value>
|
|
|
|
public IReadOnlyList<ShapedTextCharacters> Second { get; } |
|
|
|
public List<ShapedTextCharacters> Second { get; } |
|
|
|
} |
|
|
|
|
|
|
|
private struct TextRunEnumerator |
|
|
|
|