@ -706,6 +706,64 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Assert . NotNull ( textLine . TextLineBreak . TextEndOfLine ) ;
}
}
[Fact]
public void Should_HitTestStringWithInvisibleRuns ( )
{
var defaultRunProperties = new GenericTextRunProperties ( Typeface . Default , foregroundBrush : Brushes . Black ) ;
var paragraphProperties = new GenericTextParagraphProperties ( defaultRunProperties ) ;
//var textSource = new ListTextSource(
using ( Start ( ) )
{
var hello = new TextCharacters ( "Hello" ,
new GenericTextRunProperties ( Typeface . Default , foregroundBrush : Brushes . Black ) ) ;
var world = new TextCharacters ( "world" ,
new GenericTextRunProperties ( Typeface . Default , foregroundBrush : Brushes . Red ) ) ;
var source = new ListTextSource ( new InvisibleRun ( 1 ) , hello , new InvisibleRun ( 1 ) , world ) ;
var textLine =
TextFormatter . Current . FormatLine ( source , 0 , double . PositiveInfinity , paragraphProperties ) ;
void VerifyHit ( int offset )
{
var glyphCenter = textLine . GetTextBounds ( offset , 1 ) [ 0 ] . Rectangle . Center ;
var hit = textLine . GetCharacterHitFromDistance ( glyphCenter . X ) ;
Assert . Equal ( offset , hit . FirstCharacterIndex ) ;
}
VerifyHit ( 3 ) ;
VerifyHit ( 8 ) ;
}
}
[Fact]
public void GetTextBounds_For_TextLine_With_ZeroWidthSpaces_Does_Not_Freeze ( )
{
var defaultRunProperties = new GenericTextRunProperties ( Typeface . Default , foregroundBrush : Brushes . Black ) ;
var paragraphProperties = new GenericTextParagraphProperties ( defaultRunProperties ) ;
using ( Start ( ) )
{
var text = new TextCharacters ( "\u200B\u200B" ,
new GenericTextRunProperties ( Typeface . Default , foregroundBrush : Brushes . Black ) ) ;
var source = new ListTextSource ( text , new InvisibleRun ( 1 ) , new TextEndOfParagraph ( ) ) ;
var textLine =
TextFormatter . Current . FormatLine ( source , 0 , double . PositiveInfinity , paragraphProperties ) ;
var bounds = textLine . GetTextBounds ( 0 , 3 ) ;
Assert . Equal ( 1 , bounds . Count ) ;
var runBounds = bounds [ 0 ] . TextRunBounds ;
Assert . Equal ( 2 , runBounds . Count ) ;
}
}
protected readonly record struct SimpleTextSource : ITextSource
{
@ -776,6 +834,32 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
return new TextCharacters ( _ text , new GenericTextRunProperties ( Typeface . Default , foregroundBrush : Brushes . Black ) ) ;
}
}
private class ListTextSource : ITextSource
{
private Dictionary < int , TextRun > _ runs = new ( ) ;
public ListTextSource ( params TextRun [ ] runs ) : this ( ( IEnumerable < TextRun > ) runs )
{
}
public ListTextSource ( IEnumerable < TextRun > runs )
{
var off = 0 ;
foreach ( var r in runs )
{
_ runs [ off ] = r ;
off + = r . Length ;
}
}
public TextRun GetTextRun ( int textSourceIndex )
{
_ runs . TryGetValue ( textSourceIndex , out var rv ) ;
return rv ;
}
}
private class RectangleRun : DrawableTextRun
{
@ -798,6 +882,15 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
}
private class InvisibleRun : TextRun
{
public InvisibleRun ( int length )
{
Length = length ;
}
public override int Length { get ; }
}
public static IDisposable Start ( )
{