Browse Source

Removed some temporary List<T> from text layout

pull/10013/head
Julien Lebosquain 3 years ago
parent
commit
658fd804af
  1. 11
      src/Avalonia.Base/Media/TextFormatting/SplitResult.cs
  2. 9
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  3. 6
      src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
  4. 62
      src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
  5. 53
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  6. 51
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  7. 50
      src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
  8. 56
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  9. 7
      src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
  10. 8
      src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs

11
src/Avalonia.Base/Media/TextFormatting/SplitResult.cs

@ -26,5 +26,16 @@
/// The second part.
/// </value>
public T? Second { get; }
/// <summary>
/// Deconstructs the split results into its components.
/// </summary>
/// <param name="first">On return, contains the first part.</param>
/// <param name="second">On return, contains the second part.</param>
public void Deconstruct(out T first, out T? second)
{
first = First;
second = Second;
}
}
}

9
src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs

@ -46,24 +46,21 @@ namespace Avalonia.Media.TextFormatting
/// Gets a list of <see cref="UnshapedTextRun"/>.
/// </summary>
/// <returns>The shapeable text characters.</returns>
internal IReadOnlyList<UnshapedTextRun> GetShapeableCharacters(ReadOnlyMemory<char> text, sbyte biDiLevel,
ref TextRunProperties? previousProperties)
internal void GetShapeableCharacters(ReadOnlyMemory<char> text, sbyte biDiLevel,
ref TextRunProperties? previousProperties, List<TextRun> results)
{
var shapeableCharacters = new List<UnshapedTextRun>(2);
var properties = Properties;
while (!text.IsEmpty)
{
var shapeableRun = CreateShapeableRun(text, properties, biDiLevel, ref previousProperties);
shapeableCharacters.Add(shapeableRun);
results.Add(shapeableRun);
text = text.Slice(shapeableRun.Length);
previousProperties = shapeableRun.Properties;
}
return shapeableCharacters;
}
/// <summary>

6
src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs

@ -1,6 +1,4 @@
using System.Collections.Generic;
namespace Avalonia.Media.TextFormatting
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// Properties of text collapsing.
@ -21,6 +19,6 @@ namespace Avalonia.Media.TextFormatting
/// Collapses given text line.
/// </summary>
/// <param name="textLine">Text line to collapse.</param>
public abstract List<TextRun>? Collapse(TextLine textLine);
public abstract TextRun[]? Collapse(TextLine textLine);
}
}

62
src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs

@ -1,15 +1,18 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
internal static class TextEllipsisHelper
{
public static List<TextRun>? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis)
public static TextRun[]? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis)
{
var textRuns = textLine.TextRuns;
if (textRuns == null || textRuns.Count == 0)
if (textRuns.Count == 0)
{
return null;
}
@ -22,7 +25,7 @@ namespace Avalonia.Media.TextFormatting
if (properties.Width < shapedSymbol.GlyphRun.Size.Width)
{
//Not enough space to fit in the symbol
return new List<TextRun>(0);
return Array.Empty<TextRun>();
}
var availableWidth = properties.Width - shapedSymbol.Size.Width;
@ -70,18 +73,7 @@ namespace Avalonia.Media.TextFormatting
collapsedLength += measuredLength;
var collapsedRuns = new List<TextRun>(textRuns.Count);
if (collapsedLength > 0)
{
var splitResult = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength);
collapsedRuns.AddRange(splitResult.First);
}
collapsedRuns.Add(shapedSymbol);
return collapsedRuns;
return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol);
}
availableWidth -= shapedRun.Size.Width;
@ -94,18 +86,7 @@ namespace Avalonia.Media.TextFormatting
//The whole run needs to fit into available space
if (currentWidth + drawableRun.Size.Width > availableWidth)
{
var collapsedRuns = new List<TextRun>(textRuns.Count);
if (collapsedLength > 0)
{
var splitResult = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength);
collapsedRuns.AddRange(splitResult.First);
}
collapsedRuns.Add(shapedSymbol);
return collapsedRuns;
return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol);
}
availableWidth -= drawableRun.Size.Width;
@ -121,5 +102,30 @@ namespace Avalonia.Media.TextFormatting
return null;
}
private static TextRun[] CreateCollapsedRuns(IReadOnlyList<TextRun> textRuns, int collapsedLength,
TextRun shapedSymbol)
{
if (collapsedLength <= 0)
{
return new[] { shapedSymbol };
}
// perf note: the runs are very likely to come from TextLineImpl
// which already uses an array: ToArray() won't ever be called in this case
var textRunArray = textRuns as TextRun[] ?? textRuns.ToArray();
var (preSplitRuns, _) = TextFormatterImpl.SplitTextRuns(textRunArray, collapsedLength);
var collapsedRuns = new TextRun[preSplitRuns.Count + 1];
for (var i = 0; i < preSplitRuns.Count; ++i)
{
collapsedRuns[i] = preSplitRuns[i];
}
collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
return collapsedRuns;
}
}
}

53
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -1,6 +1,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
@ -23,10 +24,10 @@ namespace Avalonia.Media.TextFormatting
var fetchedRuns = FetchTextRuns(textSource, firstTextSourceIndex,
out var textEndOfLine, out var textSourceLength);
if (previousLineBreak?.RemainingRuns != null)
if (previousLineBreak?.RemainingRuns is { } remainingRuns)
{
resolvedFlowDirection = previousLineBreak.FlowDirection;
textRuns = previousLineBreak.RemainingRuns;
textRuns = remainingRuns;
nextLineBreak = previousLineBreak;
}
else
@ -45,7 +46,7 @@ namespace Avalonia.Media.TextFormatting
{
case TextWrapping.NoWrap:
{
textLine = new TextLineImpl(textRuns, firstTextSourceIndex, textSourceLength,
textLine = new TextLineImpl(textRuns.ToArray(), firstTextSourceIndex, textSourceLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine();
@ -160,6 +161,14 @@ namespace Avalonia.Media.TextFormatting
{
var flowDirection = paragraphProperties.FlowDirection;
var shapedRuns = new List<TextRun>();
if (textRuns.Count == 0)
{
resolvedFlowDirection = flowDirection;
return shapedRuns;
}
using var biDiData = new BidiData((sbyte)flowDirection);
foreach (var textRun in textRuns)
@ -224,7 +233,7 @@ namespace Avalonia.Media.TextFormatting
shapeableRun.BidiLevel, currentRun.Properties.CultureInfo,
paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing);
shapedRuns.AddRange(ShapeTogether(groupedRuns, text, shaperOptions));
ShapeTogether(groupedRuns, text, shaperOptions, shapedRuns);
break;
}
@ -309,11 +318,9 @@ namespace Avalonia.Media.TextFormatting
&& x.Typeface == y.Typeface
&& x.BaselineAlignment == y.BaselineAlignment;
private static IReadOnlyList<ShapedTextRun> ShapeTogether(
IReadOnlyList<UnshapedTextRun> textRuns, ReadOnlyMemory<char> text, TextShaperOptions options)
private static void ShapeTogether(IReadOnlyList<UnshapedTextRun> textRuns, ReadOnlyMemory<char> text,
TextShaperOptions options, List<TextRun> results)
{
var shapedRuns = new List<ShapedTextRun>(textRuns.Count);
var shapedBuffer = TextShaper.Current.ShapeText(text, options);
for (var i = 0; i < textRuns.Count; i++)
@ -322,12 +329,10 @@ namespace Avalonia.Media.TextFormatting
var splitResult = shapedBuffer.Split(currentRun.Length);
shapedRuns.Add(new ShapedTextRun(splitResult.First, currentRun.Properties));
results.Add(new ShapedTextRun(splitResult.First, currentRun.Properties));
shapedBuffer = splitResult.Second!;
}
return shapedRuns;
}
/// <summary>
@ -335,7 +340,7 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
/// <param name="textCharacters">The text characters to form <see cref="UnshapedTextRun"/> from.</param>
/// <param name="levels">The bidi levels.</param>
/// <param name="processedRuns"></param>
/// <param name="processedRuns">A list that will be filled with the processed runs.</param>
/// <returns></returns>
private static void CoalesceLevels(IReadOnlyList<TextRun> textCharacters, ArraySlice<sbyte> levels,
List<TextRun> processedRuns)
@ -385,7 +390,8 @@ namespace Avalonia.Media.TextFormatting
if (j == runTextSpan.Length)
{
processedRuns.AddRange(currentRun.GetShapeableCharacters(runText.Slice(0, j), runLevel, ref previousProperties));
currentRun.GetShapeableCharacters(runText.Slice(0, j), runLevel, ref previousProperties,
processedRuns);
runLevel = levels[levelIndex];
@ -398,7 +404,8 @@ namespace Avalonia.Media.TextFormatting
}
// End of this run
processedRuns.AddRange(currentRun.GetShapeableCharacters(runText.Slice(0, j), runLevel, ref previousProperties));
currentRun.GetShapeableCharacters(runText.Slice(0, j), runLevel, ref previousProperties,
processedRuns);
runText = runText.Slice(j);
runTextSpan = runText.Span;
@ -415,7 +422,7 @@ namespace Avalonia.Media.TextFormatting
return;
}
processedRuns.AddRange(currentRun.GetShapeableCharacters(runText, runLevel, ref previousProperties));
currentRun.GetShapeableCharacters(runText, runLevel, ref previousProperties, processedRuns);
}
/// <summary>
@ -423,8 +430,8 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
/// <param name="textSource">The text source.</param>
/// <param name="firstTextSourceIndex">The first text source index.</param>
/// <param name="endOfLine"></param>
/// <param name="textSourceLength"></param>
/// <param name="endOfLine">On return, the end of line, if any.</param>
/// <param name="textSourceLength">On return, the processed text source length.</param>
/// <returns>
/// The formatted text runs.
/// </returns>
@ -602,7 +609,7 @@ namespace Avalonia.Media.TextFormatting
var shapedBuffer = new ShapedBuffer(s_empty.AsMemory(), glyphInfos, glyphTypeface, properties.FontRenderingEmSize,
(sbyte)flowDirection);
var textRuns = new List<DrawableTextRun> { new ShapedTextRun(shapedBuffer, properties) };
var textRuns = new TextRun[] { new ShapedTextRun(shapedBuffer, properties) };
return new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, flowDirection).FinalizeLine();
}
@ -749,12 +756,10 @@ namespace Avalonia.Media.TextFormatting
break;
}
var splitResult = SplitTextRuns(textRuns, measuredLength);
var remainingCharacters = splitResult.Second;
var (preSplitRuns, postSplitRuns) = SplitTextRuns(textRuns, measuredLength);
var lineBreak = remainingCharacters?.Count > 0 ?
new TextLineBreak(null, resolvedFlowDirection, remainingCharacters) :
var lineBreak = postSplitRuns?.Count > 0 ?
new TextLineBreak(null, resolvedFlowDirection, postSplitRuns) :
null;
if (lineBreak is null && currentLineBreak?.TextEndOfLine != null)
@ -762,7 +767,7 @@ namespace Avalonia.Media.TextFormatting
lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, resolvedFlowDirection);
}
var textLine = new TextLineImpl(splitResult.First, firstTextSourceIndex, measuredLength,
var textLine = new TextLineImpl(preSplitRuns.ToArray(), firstTextSourceIndex, measuredLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection,
lineBreak);

51
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
@ -13,6 +12,7 @@ namespace Avalonia.Media.TextFormatting
private readonly ITextSource _textSource;
private readonly TextParagraphProperties _paragraphProperties;
private readonly TextTrimming _textTrimming;
private readonly TextLine[] _textLines;
private int _textSourceLength;
@ -69,7 +69,7 @@ namespace Avalonia.Media.TextFormatting
MaxLines = maxLines;
TextLines = CreateTextLines();
_textLines = CreateTextLines();
}
/// <summary>
@ -109,7 +109,7 @@ namespace Avalonia.Media.TextFormatting
MaxLines = maxLines;
TextLines = CreateTextLines();
_textLines = CreateTextLines();
}
/// <summary>
@ -147,7 +147,8 @@ namespace Avalonia.Media.TextFormatting
/// <value>
/// The text lines.
/// </value>
public IReadOnlyList<TextLine> TextLines { get; private set; }
public IReadOnlyList<TextLine> TextLines
=> _textLines;
/// <summary>
/// Gets the bounds of the layout.
@ -164,14 +165,14 @@ namespace Avalonia.Media.TextFormatting
/// <param name="origin">The origin.</param>
public void Draw(DrawingContext context, Point origin)
{
if (!TextLines.Any())
if (_textLines.Length == 0)
{
return;
}
var (currentX, currentY) = origin;
foreach (var textLine in TextLines)
foreach (var textLine in _textLines)
{
textLine.Draw(context, new Point(currentX + textLine.Start, currentY));
@ -186,7 +187,7 @@ namespace Avalonia.Media.TextFormatting
/// <returns></returns>
public Rect HitTestTextPosition(int textPosition)
{
if (TextLines.Count == 0)
if (_textLines.Length == 0)
{
return new Rect();
}
@ -198,7 +199,7 @@ namespace Avalonia.Media.TextFormatting
var currentY = 0.0;
foreach (var textLine in TextLines)
foreach (var textLine in _textLines)
{
var end = textLine.FirstTextSourceIndex + textLine.Length;
@ -230,11 +231,11 @@ namespace Avalonia.Media.TextFormatting
return Array.Empty<Rect>();
}
var result = new List<Rect>(TextLines.Count);
var result = new List<Rect>(_textLines.Length);
var currentY = 0d;
foreach (var textLine in TextLines)
foreach (var textLine in _textLines)
{
//Current line isn't covered.
if (textLine.FirstTextSourceIndex + textLine.Length < start)
@ -284,13 +285,12 @@ namespace Avalonia.Media.TextFormatting
{
var currentY = 0d;
var lineIndex = 0;
TextLine? currentLine = null;
CharacterHit characterHit;
for (; lineIndex < TextLines.Count; lineIndex++)
for (var lineIndex = 0; lineIndex < _textLines.Length; lineIndex++)
{
currentLine = TextLines[lineIndex];
currentLine = _textLines[lineIndex];
if (currentY + currentLine.Height > point.Y)
{
@ -322,12 +322,12 @@ namespace Avalonia.Media.TextFormatting
if (charIndex > _textSourceLength)
{
return TextLines.Count - 1;
return _textLines.Length - 1;
}
for (var index = 0; index < TextLines.Count; index++)
for (var index = 0; index < _textLines.Length; index++)
{
var textLine = TextLines[index];
var textLine = _textLines[index];
if (textLine.FirstTextSourceIndex + textLine.Length < charIndex)
{
@ -341,7 +341,7 @@ namespace Avalonia.Media.TextFormatting
}
}
return TextLines.Count - 1;
return _textLines.Length - 1;
}
private TextHitTestResult GetHitTestResult(TextLine textLine, CharacterHit characterHit, Point point)
@ -424,7 +424,7 @@ namespace Avalonia.Media.TextFormatting
height += textLine.Height;
}
private IReadOnlyList<TextLine> CreateTextLines()
private TextLine[] CreateTextLines()
{
if (MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight))
{
@ -432,7 +432,7 @@ namespace Avalonia.Media.TextFormatting
Bounds = new Rect(0, 0, 0, textLine.Height);
return new List<TextLine> { textLine };
return new TextLine[] { textLine };
}
var textLines = new List<TextLine>();
@ -443,12 +443,14 @@ namespace Avalonia.Media.TextFormatting
TextLine? previousLine = null;
var textFormatter = TextFormatter.Current;
while (true)
{
var textLine = TextFormatter.Current.FormatLine(_textSource, _textSourceLength, MaxWidth,
var textLine = textFormatter.FormatLine(_textSource, _textSourceLength, MaxWidth,
_paragraphProperties, previousLine?.TextLineBreak);
if(textLine == null || textLine.Length == 0)
if (textLine.Length == 0)
{
if (previousLine != null && previousLine.NewLineLength > 0)
{
@ -524,8 +526,9 @@ namespace Avalonia.Media.TextFormatting
{
var whitespaceWidth = 0d;
foreach (var line in textLines)
for (var i = 0; i < textLines.Count; i++)
{
var line = textLines[i];
var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
if (lineWhitespaceWidth > whitespaceWidth)
@ -549,7 +552,7 @@ namespace Avalonia.Media.TextFormatting
}
}
return textLines;
return textLines.ToArray();
}
/// <summary>
@ -569,7 +572,7 @@ namespace Avalonia.Media.TextFormatting
public void Dispose()
{
foreach (var line in TextLines)
foreach (var line in _textLines)
{
line.Dispose();
}

50
src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs

@ -1,5 +1,7 @@
using System;
// ReSharper disable ForCanBeConvertedToForeach
using System;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Media.TextFormatting
{
@ -39,11 +41,12 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override TextRun Symbol { get; }
public override List<TextRun>? Collapse(TextLine textLine)
/// <inheritdoc />
public override TextRun[]? Collapse(TextLine textLine)
{
var textRuns = textLine.TextRuns;
if (textRuns == null || textRuns.Count == 0)
if (textRuns.Count == 0)
{
return null;
}
@ -54,7 +57,7 @@ namespace Avalonia.Media.TextFormatting
if (Width < shapedSymbol.GlyphRun.Size.Width)
{
return new List<TextRun>(0);
return Array.Empty<TextRun>();
}
// Overview of ellipsis structure
@ -75,41 +78,48 @@ namespace Avalonia.Media.TextFormatting
{
shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength);
var collapsedRuns = new List<TextRun>(textRuns.Count);
if (measuredLength > 0)
{
IReadOnlyList<TextRun>? preSplitRuns = null;
var collapsedRuns = new List<TextRun>(textRuns.Count + 1);
// perf note: the runs are very likely to come from TextLineImpl,
// which already uses an array: ToArray() won't ever be called in this case
var textRunArray = textRuns as TextRun[] ?? textRuns.ToArray();
IReadOnlyList<TextRun>? preSplitRuns;
IReadOnlyList<TextRun>? postSplitRuns;
if (_prefixLength > 0)
{
var splitResult = TextFormatterImpl.SplitTextRuns(textRuns,
Math.Min(_prefixLength, measuredLength));
(preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(
textRunArray, Math.Min(_prefixLength, measuredLength));
collapsedRuns.AddRange(splitResult.First);
preSplitRuns = splitResult.First;
postSplitRuns = splitResult.Second;
for (var i = 0; i < preSplitRuns.Count; i++)
{
var preSplitRun = preSplitRuns[i];
collapsedRuns.Add(preSplitRun);
}
}
else
{
postSplitRuns = textRuns;
preSplitRuns = null;
postSplitRuns = textRunArray;
}
collapsedRuns.Add(shapedSymbol);
if (measuredLength <= _prefixLength || postSplitRuns is null)
{
return collapsedRuns;
return collapsedRuns.ToArray();
}
var availableSuffixWidth = availableWidth;
if (preSplitRuns is not null)
{
foreach (var run in preSplitRuns)
for (var i = 0; i < preSplitRuns.Count; i++)
{
var run = preSplitRuns[i];
if (run is DrawableTextRun drawableTextRun)
{
availableSuffixWidth -= drawableTextRun.Size.Width;
@ -143,13 +153,11 @@ namespace Avalonia.Media.TextFormatting
}
}
}
}
else
{
collapsedRuns.Add(shapedSymbol);
return collapsedRuns.ToArray();
}
return collapsedRuns;
return new TextRun[] { shapedSymbol };
}
availableWidth -= shapedRun.Size.Width;

56
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -1,19 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
{
internal class TextLineImpl : TextLine
internal sealed class TextLineImpl : TextLine
{
private IReadOnlyList<TextRun> _textRuns;
private TextRun[] _textRuns;
private readonly double _paragraphWidth;
private readonly TextParagraphProperties _paragraphProperties;
private TextLineMetrics _textLineMetrics;
private readonly FlowDirection _resolvedFlowDirection;
public TextLineImpl(IReadOnlyList<TextRun> textRuns, int firstTextSourceIndex, int length, double paragraphWidth,
public TextLineImpl(TextRun[] textRuns, int firstTextSourceIndex, int length, double paragraphWidth,
TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection = FlowDirection.LeftToRight,
TextLineBreak? lineBreak = null, bool hasCollapsed = false)
{
@ -147,7 +146,7 @@ namespace Avalonia.Media.TextFormatting
var collapsedLine = new TextLineImpl(collapsedRuns, FirstTextSourceIndex, Length, _paragraphWidth, _paragraphProperties,
_resolvedFlowDirection, TextLineBreak, true);
if (collapsedRuns.Count > 0)
if (collapsedRuns.Length > 0)
{
collapsedLine.FinalizeLine();
}
@ -166,7 +165,7 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override CharacterHit GetCharacterHitFromDistance(double distance)
{
if (_textRuns.Count == 0)
if (_textRuns.Length == 0)
{
return new CharacterHit();
}
@ -182,7 +181,7 @@ namespace Avalonia.Media.TextFormatting
if (distance >= WidthIncludingTrailingWhitespace)
{
var lastRun = _textRuns[_textRuns.Count - 1];
var lastRun = _textRuns[_textRuns.Length - 1];
var size = 0.0;
@ -199,7 +198,7 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = FirstTextSourceIndex;
var currentDistance = 0.0;
for (var i = 0; i < _textRuns.Count; i++)
for (var i = 0; i < _textRuns.Length; i++)
{
var currentRun = _textRuns[i];
@ -208,7 +207,7 @@ namespace Avalonia.Media.TextFormatting
var rightToLeftIndex = i;
currentPosition += currentRun.Length;
while (rightToLeftIndex + 1 <= _textRuns.Count - 1)
while (rightToLeftIndex + 1 <= _textRuns.Length - 1)
{
var nextShaped = _textRuns[++rightToLeftIndex] as ShapedTextRun;
@ -224,7 +223,7 @@ namespace Avalonia.Media.TextFormatting
for (var j = i; i <= rightToLeftIndex; j++)
{
if (j > _textRuns.Count - 1)
if (j > _textRuns.Length - 1)
{
break;
}
@ -254,7 +253,7 @@ namespace Avalonia.Media.TextFormatting
if (currentRun is DrawableTextRun drawableTextRun)
{
if (i < _textRuns.Count - 1 && currentDistance + drawableTextRun.Size.Width < distance)
if (i < _textRuns.Length - 1 && currentDistance + drawableTextRun.Size.Width < distance)
{
currentDistance += drawableTextRun.Size.Width;
@ -328,7 +327,7 @@ namespace Avalonia.Media.TextFormatting
if (flowDirection == FlowDirection.LeftToRight)
{
for (var index = 0; index < _textRuns.Count; index++)
for (var index = 0; index < _textRuns.Length; index++)
{
var currentRun = _textRuns[index];
@ -338,7 +337,7 @@ namespace Avalonia.Media.TextFormatting
var rightToLeftWidth = shapedRun.Size.Width;
while (i + 1 <= _textRuns.Count - 1)
while (i + 1 <= _textRuns.Length - 1)
{
var nextRun = _textRuns[i + 1];
@ -402,7 +401,7 @@ namespace Avalonia.Media.TextFormatting
{
currentDistance += WidthIncludingTrailingWhitespace;
for (var index = _textRuns.Count - 1; index >= 0; index--)
for (var index = _textRuns.Length - 1; index >= 0; index--)
{
var currentRun = _textRuns[index];
@ -502,7 +501,7 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit)
{
if (_textRuns.Count == 0)
if (_textRuns.Length == 0)
{
return new CharacterHit();
}
@ -637,7 +636,7 @@ namespace Avalonia.Media.TextFormatting
var rightToLeftIndex = index;
var rightToLeftWidth = currentShapedRun.Size.Width;
while (rightToLeftIndex + 1 <= _textRuns.Count - 1 && _textRuns[rightToLeftIndex + 1] is ShapedTextRun nextShapedRun)
while (rightToLeftIndex + 1 <= _textRuns.Length - 1 && _textRuns[rightToLeftIndex + 1] is ShapedTextRun nextShapedRun)
{
if (nextShapedRun == null || nextShapedRun.ShapedBuffer.IsLeftToRight)
{
@ -981,7 +980,7 @@ namespace Avalonia.Media.TextFormatting
public override void Dispose()
{
for (int i = 0; i < _textRuns.Count; i++)
for (int i = 0; i < _textRuns.Length; i++)
{
if (_textRuns[i] is ShapedTextRun shapedTextRun)
{
@ -1013,7 +1012,7 @@ namespace Avalonia.Media.TextFormatting
private void BidiReorder()
{
if (_textRuns.Count == 0)
if (_textRuns.Length == 0)
{
return;
}
@ -1025,7 +1024,7 @@ namespace Avalonia.Media.TextFormatting
var current = orderedRun;
for (var i = 1; i < _textRuns.Count; i++)
for (var i = 1; i < _textRuns.Length; i++)
{
run = _textRuns[i];
@ -1044,7 +1043,7 @@ namespace Avalonia.Media.TextFormatting
sbyte max = 0;
var min = sbyte.MaxValue;
for (var i = 0; i < _textRuns.Count; i++)
for (var i = 0; i < _textRuns.Length; i++)
{
var currentRun = _textRuns[i];
@ -1095,13 +1094,14 @@ namespace Avalonia.Media.TextFormatting
minLevelToReverse--;
}
var textRuns = new List<TextRun>(_textRuns.Count);
var textRuns = new TextRun[_textRuns.Length];
var index = 0;
current = orderedRun;
while (current != null)
{
textRuns.Add(current.Run);
textRuns[index++] = current.Run;
current = current.Next;
}
@ -1197,7 +1197,7 @@ namespace Avalonia.Media.TextFormatting
var runIndex = GetRunIndexAtCharacterIndex(codepointIndex, LogicalDirection.Forward, out var currentPosition);
while (runIndex < _textRuns.Count)
while (runIndex < _textRuns.Length)
{
var currentRun = _textRuns[runIndex];
@ -1346,7 +1346,7 @@ namespace Avalonia.Media.TextFormatting
textPosition = FirstTextSourceIndex;
TextRun? previousRun = null;
while (runIndex < _textRuns.Count)
while (runIndex < _textRuns.Length)
{
var currentRun = _textRuns[runIndex];
@ -1395,7 +1395,7 @@ namespace Avalonia.Media.TextFormatting
}
}
if (runIndex + 1 >= _textRuns.Count)
if (runIndex + 1 >= _textRuns.Length)
{
return runIndex;
}
@ -1411,7 +1411,7 @@ namespace Avalonia.Media.TextFormatting
return runIndex;
}
if (runIndex + 1 >= _textRuns.Count)
if (runIndex + 1 >= _textRuns.Length)
{
return runIndex;
}
@ -1448,14 +1448,14 @@ namespace Avalonia.Media.TextFormatting
var lineHeight = _paragraphProperties.LineHeight;
var lastRunIndex = _textRuns.Count - 1;
var lastRunIndex = _textRuns.Length - 1;
if (lastRunIndex > 0 && _textRuns[lastRunIndex] is TextEndOfLine)
{
lastRunIndex--;
}
for (var index = 0; index < _textRuns.Count; index++)
for (var index = 0; index < _textRuns.Length; index++)
{
switch (_textRuns[index])
{

7
src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs

@ -1,6 +1,4 @@
using System.Collections.Generic;
namespace Avalonia.Media.TextFormatting
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// A collapsing properties to collapse whole line toward the end
@ -26,7 +24,8 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override TextRun Symbol { get; }
public override List<TextRun>? Collapse(TextLine textLine)
/// <inheritdoc />
public override TextRun[]? Collapse(TextLine textLine)
{
return TextEllipsisHelper.Collapse(textLine, this, false);
}

8
src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs

@ -1,7 +1,4 @@
using System.Collections.Generic;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// a collapsing properties to collapse whole line toward the end
@ -31,7 +28,8 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override TextRun Symbol { get; }
public override List<TextRun>? Collapse(TextLine textLine)
/// <inheritdoc />
public override TextRun[]? Collapse(TextLine textLine)
{
return TextEllipsisHelper.Collapse(textLine, this, true);
}

Loading…
Cancel
Save