Browse Source

Improve TextTrimming customization experience (#16521)

* Add some hack

* Make some helper APIs public for easier TextTrimming customization
Benedikt Stebner 1 year ago
parent
commit
a562c4e4e5

+ 1 - 1
src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs

@@ -98,7 +98,7 @@ namespace Avalonia.Media.TextFormatting
         /// <returns>
         /// <c>true</c> if characters fit into the available width; otherwise, <c>false</c>.
         /// </returns>
-        internal bool TryMeasureCharacters(double availableWidth, out int length)
+        public bool TryMeasureCharacters(double availableWidth, out int length)
         {
             length = 0;
             var currentWidth = 0.0;

+ 51 - 0
src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs

@@ -27,5 +27,56 @@ namespace Avalonia.Media.TextFormatting
         /// </summary>
         /// <param name="textLine">Text line to collapse.</param>
         public abstract TextRun[]? Collapse(TextLine textLine);
+
+        /// <summary>
+        /// Creates a list of runs for given collapsed length which includes specified symbol at the end.
+        /// </summary>
+        /// <param name="textLine">The text line.</param>
+        /// <param name="collapsedLength">The collapsed length.</param>
+        /// <param name="flowDirection">The flow direction.</param>
+        /// <param name="shapedSymbol">The symbol.</param>
+        /// <returns>List of remaining runs.</returns>
+        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);
+            }
+        }
     }
 }

+ 7 - 51
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 @@ namespace Avalonia.Media.TextFormatting
             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<TextRun>();
+                return [];
             }
 
             var availableWidth = properties.Width - shapedSymbol.Size.Width;
@@ -72,7 +71,7 @@ namespace Avalonia.Media.TextFormatting
 
                                     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 @@ namespace Avalonia.Media.TextFormatting
                                 //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 @@ namespace Avalonia.Media.TextFormatting
 
                                     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 @@ namespace Avalonia.Media.TextFormatting
                                 //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 @@ namespace Avalonia.Media.TextFormatting
       
             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);
-            }
-        }
     }
 }

+ 26 - 0
src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs

@@ -40,5 +40,31 @@
         /// <returns>The formatted line.</returns>
         public abstract TextLine? FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
             TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null);
+
+        /// <summary>
+        /// Creates a shaped symbol.
+        /// </summary>
+        /// <param name="textRun">The symbol run to shape.</param>
+        /// <param name="flowDirection">The flow direction.</param>
+        /// <returns>
+        /// The shaped symbol.
+        /// </returns>
+        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);
+        }
     }
 }

+ 0 - 26
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@@ -957,31 +957,5 @@ namespace Avalonia.Media.TextFormatting
                 return true;
             }
         }
-
-        /// <summary>
-        /// Creates a shaped symbol.
-        /// </summary>
-        /// <param name="textRun">The symbol run to shape.</param>
-        /// <param name="flowDirection">The flow direction.</param>
-        /// <returns>
-        /// The shaped symbol.
-        /// </returns>
-        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);
-        }
     }
 }