diff --git a/src/Avalonia.Base/Media/TextFormatting/SplitResult.cs b/src/Avalonia.Base/Media/TextFormatting/SplitResult.cs
index 53021c4656..c1ac57ce46 100644
--- a/src/Avalonia.Base/Media/TextFormatting/SplitResult.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/SplitResult.cs
@@ -26,5 +26,16 @@
/// The second part.
///
public T? Second { get; }
+
+ ///
+ /// Deconstructs the split results into its components.
+ ///
+ /// On return, contains the first part.
+ /// On return, contains the second part.
+ public void Deconstruct(out T first, out T? second)
+ {
+ first = First;
+ second = Second;
+ }
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
index 2525f0dbf9..6454f9bfa3 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
@@ -46,24 +46,21 @@ namespace Avalonia.Media.TextFormatting
/// Gets a list of .
///
/// The shapeable text characters.
- internal IReadOnlyList GetShapeableCharacters(ReadOnlyMemory text, sbyte biDiLevel,
- ref TextRunProperties? previousProperties)
+ internal void GetShapeableCharacters(ReadOnlyMemory text, sbyte biDiLevel,
+ ref TextRunProperties? previousProperties, List results)
{
- var shapeableCharacters = new List(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;
}
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
index 01804e1ce3..72882df0b5 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
-
-namespace Avalonia.Media.TextFormatting
+namespace Avalonia.Media.TextFormatting
{
///
/// Properties of text collapsing.
@@ -21,6 +19,6 @@ namespace Avalonia.Media.TextFormatting
/// Collapses given text line.
///
/// Text line to collapse.
- public abstract List? Collapse(TextLine textLine);
+ public abstract TextRun[]? Collapse(TextLine textLine);
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
index 528cd45581..97f8b2483b 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
+++ b/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? 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(0);
+ return Array.Empty();
}
var availableWidth = properties.Width - shapedSymbol.Size.Width;
@@ -70,18 +73,7 @@ namespace Avalonia.Media.TextFormatting
collapsedLength += measuredLength;
- var collapsedRuns = new List(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(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 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;
+ }
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
index 8afecb09e2..5c073452f4 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
+++ b/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();
+
+ 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 ShapeTogether(
- IReadOnlyList textRuns, ReadOnlyMemory text, TextShaperOptions options)
+ private static void ShapeTogether(IReadOnlyList textRuns, ReadOnlyMemory text,
+ TextShaperOptions options, List results)
{
- var shapedRuns = new List(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;
}
///
@@ -335,7 +340,7 @@ namespace Avalonia.Media.TextFormatting
///
/// The text characters to form from.
/// The bidi levels.
- ///
+ /// A list that will be filled with the processed runs.
///
private static void CoalesceLevels(IReadOnlyList textCharacters, ArraySlice levels,
List 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);
}
///
@@ -423,8 +430,8 @@ namespace Avalonia.Media.TextFormatting
///
/// The text source.
/// The first text source index.
- ///
- ///
+ /// On return, the end of line, if any.
+ /// On return, the processed text source length.
///
/// The formatted text runs.
///
@@ -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 { 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);
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
index 468623b356..55b6f14267 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
+++ b/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();
}
///
@@ -109,7 +109,7 @@ namespace Avalonia.Media.TextFormatting
MaxLines = maxLines;
- TextLines = CreateTextLines();
+ _textLines = CreateTextLines();
}
///
@@ -147,7 +147,8 @@ namespace Avalonia.Media.TextFormatting
///
/// The text lines.
///
- public IReadOnlyList TextLines { get; private set; }
+ public IReadOnlyList TextLines
+ => _textLines;
///
/// Gets the bounds of the layout.
@@ -164,14 +165,14 @@ namespace Avalonia.Media.TextFormatting
/// The origin.
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
///
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();
}
- var result = new List(TextLines.Count);
+ var result = new List(_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 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 };
+ return new TextLine[] { textLine };
}
var textLines = new List();
@@ -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();
}
///
@@ -569,7 +572,7 @@ namespace Avalonia.Media.TextFormatting
public void Dispose()
{
- foreach (var line in TextLines)
+ foreach (var line in _textLines)
{
line.Dispose();
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
index e30a0fe9f4..672a15b398 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
+++ b/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
///
public override TextRun Symbol { get; }
- public override List? Collapse(TextLine textLine)
+ ///
+ 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(0);
+ return Array.Empty();
}
// Overview of ellipsis structure
@@ -75,41 +78,48 @@ namespace Avalonia.Media.TextFormatting
{
shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength);
- var collapsedRuns = new List(textRuns.Count);
-
if (measuredLength > 0)
{
- IReadOnlyList? preSplitRuns = null;
+ var collapsedRuns = new List(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? preSplitRuns;
IReadOnlyList? 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;
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index ab9686a34a..ae6df3a232 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/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 _textRuns;
+ private TextRun[] _textRuns;
private readonly double _paragraphWidth;
private readonly TextParagraphProperties _paragraphProperties;
private TextLineMetrics _textLineMetrics;
private readonly FlowDirection _resolvedFlowDirection;
- public TextLineImpl(IReadOnlyList 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
///
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
///
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(_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])
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
index deecbbe476..ccae99cc75 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
-
-namespace Avalonia.Media.TextFormatting
+namespace Avalonia.Media.TextFormatting
{
///
/// A collapsing properties to collapse whole line toward the end
@@ -26,7 +24,8 @@ namespace Avalonia.Media.TextFormatting
///
public override TextRun Symbol { get; }
- public override List? Collapse(TextLine textLine)
+ ///
+ public override TextRun[]? Collapse(TextLine textLine)
{
return TextEllipsisHelper.Collapse(textLine, this, false);
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs
index c291e1dfb9..c622c76a60 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs
+++ b/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
{
///
/// a collapsing properties to collapse whole line toward the end
@@ -31,7 +28,8 @@ namespace Avalonia.Media.TextFormatting
///
public override TextRun Symbol { get; }
- public override List? Collapse(TextLine textLine)
+ ///
+ public override TextRun[]? Collapse(TextLine textLine)
{
return TextEllipsisHelper.Collapse(textLine, this, true);
}