Browse Source

Make sure we always hit the outer sides at the start/end

pull/4339/head
Benedikt Schroeder 6 years ago
parent
commit
c3f01b696b
  1. 2
      src/Avalonia.Visuals/Media/CharacterHit.cs
  2. 18
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  3. 75
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

2
src/Avalonia.Visuals/Media/CharacterHit.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
namespace Avalonia.Media
{
@ -9,6 +10,7 @@ namespace Avalonia.Media
/// The CharacterHit structure provides information about the index of the first
/// character that got hit as well as information about leading or trailing edge.
/// </remarks>
[DebuggerDisplay("CharacterHit({FirstCharacterIndex}, {TrailingLength})")]
public readonly struct CharacterHit : IEquatable<CharacterHit>
{
/// <summary>

18
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

@ -236,7 +236,7 @@ namespace Avalonia.Media.TextFormatting
var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
if (codepointIndex >= TextRange.Start + TextRange.Length)
if (codepointIndex > TextRange.End)
{
return false; // Cannot go forward anymore
}
@ -249,11 +249,14 @@ namespace Avalonia.Media.TextFormatting
var foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _);
nextCharacterHit = characterHit.TrailingLength != 0 ?
var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength ==
TextRange.Length;
nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ?
foundCharacterHit :
new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength);
if (nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex)
if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex)
{
return true;
}
@ -272,6 +275,13 @@ namespace Avalonia.Media.TextFormatting
/// <returns></returns>
private bool TryFindPreviousCharacterHit(CharacterHit characterHit, out CharacterHit previousCharacterHit)
{
if (characterHit.FirstCharacterIndex == TextRange.Start)
{
previousCharacterHit = new CharacterHit(TextRange.Start);
return true;
}
previousCharacterHit = characterHit;
var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
@ -354,7 +364,7 @@ namespace Avalonia.Media.TextFormatting
return new ShapedTextCharacters(glyphRun, textRun.Properties);
}
/// <summary>
/// Gets the shaped width of specified shaped text characters.
/// </summary>

75
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
@ -101,7 +102,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex);
Assert.Equal(firstCharacterHit.TrailingLength, previousCharacterHit.TrailingLength);
Assert.Equal(0, previousCharacterHit.TrailingLength);
previousCharacterHit = new CharacterHit(clusters[^1], text.Length - clusters[^1]);
@ -119,7 +120,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex);
Assert.Equal(firstCharacterHit.TrailingLength, previousCharacterHit.TrailingLength);
Assert.Equal(0, previousCharacterHit.TrailingLength);
}
}
@ -272,6 +273,76 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void TestNext()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new SingleBufferTextSource("Text from memory", defaultProperties);
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
var characterHits = new List<CharacterHit>();
var currentCharacterHit = new CharacterHit(0);
characterHits.Add(currentCharacterHit);
var nextCharacterHit = textLine.GetNextCaretCharacterHit(currentCharacterHit);
while (nextCharacterHit != currentCharacterHit)
{
currentCharacterHit = nextCharacterHit;
characterHits.Add(currentCharacterHit);
nextCharacterHit = textLine.GetNextCaretCharacterHit(currentCharacterHit);
}
}
}
[Fact]
public void TestPrevious()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var text = "Text from memory";
var textSource = new SingleBufferTextSource(text, defaultProperties);
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
var characterHits = new List<CharacterHit>();
var currentCharacterHit = new CharacterHit(text.Length);
characterHits.Add(currentCharacterHit);
var nextCharacterHit = textLine.GetPreviousCaretCharacterHit(currentCharacterHit);
while (nextCharacterHit != currentCharacterHit)
{
currentCharacterHit = nextCharacterHit;
characterHits.Add(currentCharacterHit);
nextCharacterHit = textLine.GetPreviousCaretCharacterHit(currentCharacterHit);
}
}
}
private static IDisposable Start()
{
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface

Loading…
Cancel
Save