diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index 5bb8ad5b958..f01de9cc9ef 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -98,7 +98,7 @@ internal void Reverse() /// /// true if characters fit into the available width; otherwise, false. /// - internal bool TryMeasureCharacters(double availableWidth, out int length) + public bool TryMeasureCharacters(double availableWidth, out int length) { length = 0; var currentWidth = 0.0; diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs index 7cdf81ecc9a..e1d92534157 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs @@ -27,5 +27,56 @@ public abstract class TextCollapsingProperties /// /// Text line to collapse. public abstract TextRun[]? Collapse(TextLine textLine); + + /// + /// Creates a list of runs for given collapsed length which includes specified symbol at the end. + /// + /// The text line. + /// The collapsed length. + /// The flow direction. + /// The symbol. + /// List of remaining runs. + public static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength, + FlowDirection flowDirection, TextRun shapedSymbol) + { + var textRuns = textLine.TextRuns; + + if (collapsedLength <= 0) + { + return new[] { shapedSymbol }; + } + + if (flowDirection == FlowDirection.RightToLeft) + { + collapsedLength = textLine.Length - collapsedLength; + } + + var objectPool = FormattingObjectPool.Instance; + + var (preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength, objectPool); + + try + { + if (flowDirection == FlowDirection.RightToLeft) + { + var collapsedRuns = new TextRun[postSplitRuns!.Count + 1]; + postSplitRuns.CopyTo(collapsedRuns, 1); + collapsedRuns[0] = shapedSymbol; + return collapsedRuns; + } + else + { + var collapsedRuns = new TextRun[preSplitRuns!.Count + 1]; + preSplitRuns.CopyTo(collapsedRuns); + collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol; + return collapsedRuns; + } + } + finally + { + objectPool.TextRunLists.Return(ref preSplitRuns); + objectPool.TextRunLists.Return(ref postSplitRuns); + } + } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs index 8b6d576c6ec..fb01afa33d0 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Media.TextFormatting.Unicode; +using Avalonia.Media.TextFormatting.Unicode; namespace Avalonia.Media.TextFormatting { @@ -17,12 +16,12 @@ internal static class TextEllipsisHelper var runIndex = 0; var currentWidth = 0.0; var collapsedLength = 0; - var shapedSymbol = TextFormatterImpl.CreateSymbol(properties.Symbol, FlowDirection.LeftToRight); + var shapedSymbol = TextFormatter.CreateSymbol(properties.Symbol, FlowDirection.LeftToRight); if (properties.Width < shapedSymbol.GlyphRun.Bounds.Width) { //Not enough space to fit in the symbol - return Array.Empty(); + return []; } var availableWidth = properties.Width - shapedSymbol.Size.Width; @@ -72,7 +71,7 @@ internal static class TextEllipsisHelper collapsedLength += measuredLength; - return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol); + return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol); } availableWidth -= shapedRun.Size.Width; @@ -85,7 +84,7 @@ internal static class TextEllipsisHelper //The whole run needs to fit into available space if (currentWidth + drawableRun.Size.Width > availableWidth) { - return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol); + return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol); } availableWidth -= drawableRun.Size.Width; @@ -146,7 +145,7 @@ internal static class TextEllipsisHelper collapsedLength += measuredLength; - return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol); + return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol); } availableWidth -= shapedRun.Size.Width; @@ -159,7 +158,7 @@ internal static class TextEllipsisHelper //The whole run needs to fit into available space if (currentWidth + drawableRun.Size.Width > availableWidth) { - return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol); + return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol); } availableWidth -= drawableRun.Size.Width; @@ -176,48 +175,5 @@ internal static class TextEllipsisHelper return null; } - - private static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength, - FlowDirection flowDirection, TextRun shapedSymbol) - { - var textRuns = textLine.TextRuns; - - if (collapsedLength <= 0) - { - return new[] { shapedSymbol }; - } - - if(flowDirection == FlowDirection.RightToLeft) - { - collapsedLength = textLine.Length - collapsedLength; - } - - var objectPool = FormattingObjectPool.Instance; - - var (preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength, objectPool); - - try - { - if (flowDirection == FlowDirection.RightToLeft) - { - var collapsedRuns = new TextRun[postSplitRuns!.Count + 1]; - postSplitRuns.CopyTo(collapsedRuns, 1); - collapsedRuns[0] = shapedSymbol; - return collapsedRuns; - } - else - { - var collapsedRuns = new TextRun[preSplitRuns!.Count + 1]; - preSplitRuns.CopyTo(collapsedRuns); - collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol; - return collapsedRuns; - } - } - finally - { - objectPool.TextRunLists.Return(ref preSplitRuns); - objectPool.TextRunLists.Return(ref postSplitRuns); - } - } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs index ff8c1c48609..1dbf55fb97a 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs @@ -40,5 +40,31 @@ public static TextFormatter Current /// The formatted line. public abstract TextLine? FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null); + + /// + /// Creates a shaped symbol. + /// + /// The symbol run to shape. + /// The flow direction. + /// + /// The shaped symbol. + /// + public static ShapedTextRun CreateSymbol(TextRun textRun, FlowDirection flowDirection) + { + var textShaper = TextShaper.Current; + + var glyphTypeface = textRun.Properties!.CachedGlyphTypeface; + + var fontRenderingEmSize = textRun.Properties.FontRenderingEmSize; + + var cultureInfo = textRun.Properties.CultureInfo; + + var shaperOptions = new TextShaperOptions(glyphTypeface, textRun.Properties.FontFeatures, + fontRenderingEmSize, (sbyte)flowDirection, cultureInfo); + + var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions); + + return new ShapedTextRun(shapedBuffer, textRun.Properties); + } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 5aeff0fba24..8e2325fb145 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -957,31 +957,5 @@ public bool MoveNext() return true; } } - - /// - /// Creates a shaped symbol. - /// - /// The symbol run to shape. - /// The flow direction. - /// - /// The shaped symbol. - /// - internal static ShapedTextRun CreateSymbol(TextRun textRun, FlowDirection flowDirection) - { - var textShaper = TextShaper.Current; - - var glyphTypeface = textRun.Properties!.CachedGlyphTypeface; - - var fontRenderingEmSize = textRun.Properties.FontRenderingEmSize; - - var cultureInfo = textRun.Properties.CultureInfo; - - var shaperOptions = new TextShaperOptions(glyphTypeface, textRun.Properties.FontFeatures, - fontRenderingEmSize, (sbyte)flowDirection, cultureInfo); - - var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions); - - return new ShapedTextRun(shapedBuffer, textRun.Properties); - } } }