Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve TextTrimming customization experience #16521

Merged
merged 3 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ internal void Reverse()
/// <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;
Expand Down
51 changes: 51 additions & 0 deletions src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,56 @@ public abstract class TextCollapsingProperties
/// </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);
}
}
}
}
58 changes: 7 additions & 51 deletions src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Media.TextFormatting.Unicode;

namespace Avalonia.Media.TextFormatting
{
Expand All @@ -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<TextRun>();
return [];
}

var availableWidth = properties.Width - shapedSymbol.Size.Width;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
}
}
}
}
26 changes: 26 additions & 0 deletions src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,31 @@ public static TextFormatter Current
/// <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);
}
}
}
26 changes: 0 additions & 26 deletions src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -957,31 +957,5 @@ public bool MoveNext()
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);
}
}
}
Loading