diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index 8b44e32c48..08d9107bb1 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -181,6 +181,17 @@ namespace Avalonia.Media.TextFormatting return nextCharacterHit; } + if (characterHit.FirstCharacterIndex + characterHit.TrailingLength <= TextRange.Start + TextRange.Length) + { + return characterHit; // Can't move, we're after the last character + } + + var runIndex = GetRunIndexAtCodepointIndex(TextRange.End); + + var textRun = _textRuns[runIndex]; + + characterHit = textRun.GlyphRun.GetNextCaretCharacterHit(characterHit); + return characterHit; // Can't move, we're after the last character } @@ -192,6 +203,11 @@ namespace Avalonia.Media.TextFormatting return previousCharacterHit; } + if (characterHit.FirstCharacterIndex < TextRange.Start) + { + characterHit = new CharacterHit(TextRange.Start); + } + return characterHit; // Can't move, we're before the first character } diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 3655d78c9d..7abfe29f11 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Avalonia.Media; using Avalonia.Media.TextFormatting; @@ -10,6 +9,65 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { public class TextLineTests { + private static readonly string s_multiLineText = "012345678\r\r0123456789"; + + [Fact] + public void Should_Get_First_CharacterHit() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + + var textSource = new SingleBufferTextSource(s_multiLineText, defaultProperties); + + var formatter = new TextFormatterImpl(); + + var currentIndex = 0; + + while (currentIndex < s_multiLineText.Length) + { + var textLine = + formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var firstCharacterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(int.MinValue)); + + Assert.Equal(textLine.TextRange.Start, firstCharacterHit.FirstCharacterIndex); + + currentIndex += textLine.TextRange.Length; + } + } + } + + [Fact] + public void Should_Get_Last_CharacterHit() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + + var textSource = new SingleBufferTextSource(s_multiLineText, defaultProperties); + + var formatter = new TextFormatterImpl(); + + var currentIndex = 0; + + while (currentIndex < s_multiLineText.Length) + { + var textLine = + formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var lastCharacterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(int.MaxValue)); + + Assert.Equal(textLine.TextRange.Start + textLine.TextRange.Length, + lastCharacterHit.FirstCharacterIndex + lastCharacterHit.TrailingLength); + + currentIndex += textLine.TextRange.Length; + } + } + } + [InlineData("𐐷𐐷𐐷𐐷𐐷")] [InlineData("𐐷1234")] [Theory]