|
|
@@ -10,15 +10,7 @@ namespace Avalonia.Media.TextFormatting
|
|
|
{
|
|
|
private static readonly ReadOnlySlice<char> s_ellipsis = new ReadOnlySlice<char>(new[] { '\u2026' });
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Formats a text line.
|
|
|
- /// </summary>
|
|
|
- /// <param name="textSource">The text source.</param>
|
|
|
- /// <param name="firstTextSourceIndex">The first character index to start the text line from.</param>
|
|
|
- /// <param name="paragraphWidth">A <see cref="double"/> value that specifies the width of the paragraph that the line fills.</param>
|
|
|
- /// <param name="paragraphProperties">A <see cref="TextParagraphProperties"/> value that represents paragraph properties,
|
|
|
- /// such as TextWrapping, TextAlignment, or TextStyle.</param>
|
|
|
- /// <returns>The formatted line.</returns>
|
|
|
+ /// <inheritdoc cref="TextFormatter.FormatLine"/>
|
|
|
public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
|
|
|
TextParagraphProperties paragraphProperties)
|
|
|
{
|
|
|
@@ -61,17 +53,18 @@ namespace Avalonia.Media.TextFormatting
|
|
|
/// </returns>
|
|
|
private List<ShapedTextRun> FormatTextRuns(ITextSource textSource, int firstTextSourceIndex, out TextPointer textPointer)
|
|
|
{
|
|
|
- var start = firstTextSourceIndex;
|
|
|
+ var start = -1;
|
|
|
+ var length = 0;
|
|
|
|
|
|
var textRuns = new List<ShapedTextRun>();
|
|
|
|
|
|
while (true)
|
|
|
{
|
|
|
- var textRun = textSource.GetTextRun(firstTextSourceIndex);
|
|
|
+ var textRun = textSource.GetTextRun(firstTextSourceIndex + length);
|
|
|
|
|
|
- if (textRun.Text.IsEmpty)
|
|
|
+ if (start == -1)
|
|
|
{
|
|
|
- break;
|
|
|
+ start = textRun.Text.Start;
|
|
|
}
|
|
|
|
|
|
if (textRun is TextEndOfLine)
|
|
|
@@ -79,29 +72,33 @@ namespace Avalonia.Media.TextFormatting
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- if (!(textRun is TextCharacters))
|
|
|
+ switch (textRun)
|
|
|
{
|
|
|
- throw new NotSupportedException("Run type not supported by the formatter.");
|
|
|
- }
|
|
|
+ case TextCharacters textCharacters:
|
|
|
|
|
|
- var runText = textRun.Text;
|
|
|
+ var runText = textCharacters.Text;
|
|
|
|
|
|
- while (!runText.IsEmpty)
|
|
|
- {
|
|
|
- var shapableTextStyleRun = CreateShapableTextStyleRun(runText, textRun.Style);
|
|
|
+ while (!runText.IsEmpty)
|
|
|
+ {
|
|
|
+ var shapableTextStyleRun = CreateShapableTextStyleRun(runText, textRun.Style);
|
|
|
|
|
|
- var shapedRun = new ShapedTextRun(runText.Take(shapableTextStyleRun.TextPointer.Length),
|
|
|
- shapableTextStyleRun.Style);
|
|
|
+ var shapedRun = new ShapedTextRun(runText.Take(shapableTextStyleRun.TextPointer.Length),
|
|
|
+ shapableTextStyleRun.Style);
|
|
|
|
|
|
- textRuns.Add(shapedRun);
|
|
|
+ textRuns.Add(shapedRun);
|
|
|
|
|
|
- runText = runText.Skip(shapedRun.Text.Length);
|
|
|
+ runText = runText.Skip(shapedRun.Text.Length);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new NotSupportedException("Run type not supported by the formatter.");
|
|
|
}
|
|
|
|
|
|
- firstTextSourceIndex += textRun.Text.Length;
|
|
|
+ length += textRun.Text.Length;
|
|
|
}
|
|
|
|
|
|
- textPointer = new TextPointer(start, firstTextSourceIndex - start);
|
|
|
+ textPointer = new TextPointer(start, length);
|
|
|
|
|
|
return textRuns;
|
|
|
}
|
|
|
@@ -115,7 +112,7 @@ namespace Avalonia.Media.TextFormatting
|
|
|
/// <param name="textRuns">The text runs to perform the trimming on.</param>
|
|
|
/// <param name="text">The text that was used to construct the text runs.</param>
|
|
|
/// <returns></returns>
|
|
|
- private TextLine PerformTextTrimming(TextPointer text, IReadOnlyList<ShapedTextRun> textRuns,
|
|
|
+ private static TextLine PerformTextTrimming(TextPointer text, IReadOnlyList<ShapedTextRun> textRuns,
|
|
|
double paragraphWidth, TextParagraphProperties paragraphProperties)
|
|
|
{
|
|
|
var textTrimming = paragraphProperties.TextTrimming;
|
|
|
@@ -195,7 +192,7 @@ namespace Avalonia.Media.TextFormatting
|
|
|
/// <param name="text">The text to analyze for break opportunities.</param>
|
|
|
/// <param name="paragraphWidth"></param>
|
|
|
/// <returns></returns>
|
|
|
- private TextLine PerformTextWrapping(TextPointer text, IReadOnlyList<ShapedTextRun> textRuns,
|
|
|
+ private static TextLine PerformTextWrapping(TextPointer text, IReadOnlyList<ShapedTextRun> textRuns,
|
|
|
double paragraphWidth, TextParagraphProperties paragraphProperties)
|
|
|
{
|
|
|
var availableWidth = paragraphWidth;
|
|
|
@@ -267,41 +264,13 @@ namespace Avalonia.Media.TextFormatting
|
|
|
/// <param name="textRun">The text run.</param>
|
|
|
/// <param name="availableWidth">The available width.</param>
|
|
|
/// <returns></returns>
|
|
|
- private int MeasureText(ShapedTextRun textRun, double availableWidth)
|
|
|
+ private static int MeasureText(ShapedTextRun textRun, double availableWidth)
|
|
|
{
|
|
|
- if (textRun.GlyphRun.Bounds.Width < availableWidth)
|
|
|
- {
|
|
|
- return textRun.Text.Length;
|
|
|
- }
|
|
|
-
|
|
|
- var measuredWidth = 0.0;
|
|
|
-
|
|
|
- var index = 0;
|
|
|
-
|
|
|
- for (; index < textRun.GlyphRun.GlyphAdvances.Length; index++)
|
|
|
- {
|
|
|
- var advance = textRun.GlyphRun.GlyphAdvances[index];
|
|
|
-
|
|
|
- if (measuredWidth + advance > availableWidth)
|
|
|
- {
|
|
|
- index--;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- measuredWidth += advance;
|
|
|
- }
|
|
|
-
|
|
|
- if(index < 0)
|
|
|
- {
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- var cluster = textRun.GlyphRun.GlyphClusters[index];
|
|
|
+ var glyphRun = textRun.GlyphRun;
|
|
|
|
|
|
- var characterHit = textRun.GlyphRun.FindNearestCharacterHit(cluster, out _);
|
|
|
+ var characterHit = glyphRun.GetCharacterHitFromDistance(availableWidth, out _);
|
|
|
|
|
|
- return characterHit.FirstCharacterIndex - textRun.GlyphRun.Characters.Start +
|
|
|
- (textRun.GlyphRun.IsLeftToRight ? characterHit.TrailingLength : 0);
|
|
|
+ return characterHit.FirstCharacterIndex + characterHit.TrailingLength - textRun.Text.Start;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|