Browse Source

Text hit testing fixes

pull/8094/head
Benedikt Stebner 4 years ago
parent
commit
17b9f246f3
  1. 15
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  2. 4
      src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
  3. 5
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  4. 6
      src/Avalonia.Controls/TextBlock.cs
  5. 2
      src/Avalonia.Headless/HeadlessPlatformStubs.cs
  6. 2
      src/Skia/Avalonia.Skia/TextShaperImpl.cs
  7. 2
      src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
  8. 50
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
  9. 2
      tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
  10. 2
      tests/Avalonia.UnitTests/MockTextShaperImpl.cs

15
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -407,6 +407,7 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = FirstTextSourceIndex; var currentPosition = FirstTextSourceIndex;
var currentRect = Rect.Empty; var currentRect = Rect.Empty;
var startX = Start; var startX = Start;
var runStart = startX;
//A portion of the line is covered. //A portion of the line is covered.
for (var index = 0; index < TextRuns.Count; index++) for (var index = 0; index < TextRuns.Count; index++)
@ -431,7 +432,7 @@ namespace Avalonia.Media.TextFormatting
{ {
case ShapedTextCharacters when currentRun is ShapedTextCharacters: case ShapedTextCharacters when currentRun is ShapedTextCharacters:
{ {
if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End && _flowDirection == FlowDirection.LeftToRight)
{ {
goto skip; goto skip;
} }
@ -480,7 +481,7 @@ namespace Avalonia.Media.TextFormatting
case ShapedTextCharacters shapedRun: case ShapedTextCharacters shapedRun:
{ {
endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit(
shapedRun.ShapedBuffer.IsLeftToRight ? shapedRun.ShapedBuffer.IsLeftToRight ?
new CharacterHit(firstTextSourceCharacterIndex + textLength) : new CharacterHit(firstTextSourceCharacterIndex + textLength) :
new CharacterHit(firstTextSourceCharacterIndex)); new CharacterHit(firstTextSourceCharacterIndex));
@ -493,7 +494,7 @@ namespace Avalonia.Media.TextFormatting
startX += startOffset; startX += startOffset;
var characterHit = shapedRun.GlyphRun.IsLeftToRight ? var characterHit = _flowDirection == FlowDirection.LeftToRight ?
shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) : shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) :
shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
@ -580,6 +581,11 @@ namespace Avalonia.Media.TextFormatting
{ {
break; break;
} }
if (_flowDirection == FlowDirection.RightToLeft)
{
endX += currentRun.Size.Width - endOffset;
}
} }
else else
{ {
@ -591,8 +597,9 @@ namespace Avalonia.Media.TextFormatting
endX += currentRun.Size.Width - endOffset; endX += currentRun.Size.Width - endOffset;
} }
lastDirection = currentDirection;
startX = endX; startX = endX;
lastDirection = currentDirection;
runStart += currentRun.Size.Width;
} }
return result; return result;

4
src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs

@ -16,7 +16,7 @@ namespace Avalonia.Media.TextFormatting
{ {
Typeface = typeface; Typeface = typeface;
FontRenderingEmSize = fontRenderingEmSize; FontRenderingEmSize = fontRenderingEmSize;
BidLevel = bidiLevel; BidiLevel = bidiLevel;
Culture = culture; Culture = culture;
IncrementalTabWidth = incrementalTabWidth; IncrementalTabWidth = incrementalTabWidth;
} }
@ -33,7 +33,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary> /// <summary>
/// Get the bidi level of the text. /// Get the bidi level of the text.
/// </summary> /// </summary>
public sbyte BidLevel { get; } public sbyte BidiLevel { get; }
/// <summary> /// <summary>
/// Get the culture. /// Get the culture.

5
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -515,11 +515,6 @@ namespace Avalonia.Controls.Presenters
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
if (string.IsNullOrEmpty(Text))
{
return new Size();
}
_constraint = availableSize; _constraint = availableSize;
_textLayout = null; _textLayout = null;

6
src/Avalonia.Controls/TextBlock.cs

@ -631,7 +631,11 @@ namespace Avalonia.Controls
return finalSize; return finalSize;
} }
_constraint = new Size(finalSize.Width, double.PositiveInfinity); var scale = LayoutHelper.GetLayoutScale(this);
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
_constraint = new Size(finalSize.Deflate(padding).Width, double.PositiveInfinity);
_textLayout = null; _textLayout = null;

2
src/Avalonia.Headless/HeadlessPlatformStubs.cs

@ -137,7 +137,7 @@ namespace Avalonia.Headless
{ {
var typeface = options.Typeface; var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize; var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel; var bidiLevel = options.BidiLevel;
return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
} }

2
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -16,7 +16,7 @@ namespace Avalonia.Skia
{ {
var typeface = options.Typeface; var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize; var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel; var bidiLevel = options.BidiLevel;
var culture = options.Culture; var culture = options.Culture;
using (var buffer = new Buffer()) using (var buffer = new Buffer())

2
src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs

@ -16,7 +16,7 @@ namespace Avalonia.Direct2D1.Media
{ {
var typeface = options.Typeface; var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize; var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel; var bidiLevel = options.BidiLevel;
var culture = options.Culture; var culture = options.Culture;
using (var buffer = new Buffer()) using (var buffer = new Buffer())

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

@ -718,31 +718,45 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
using (Start()) using (Start())
{ {
var defaultProperties = new GenericTextRunProperties(Typeface.Default); var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var text = "0123".AsMemory(); var text = "אאא AAA";
var ltrOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); var textSource = new SingleBufferTextSource(text, defaultProperties);
var rtlOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 1, CultureInfo.CurrentCulture);
var textRuns = new List<TextRun>
{
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text), ltrOptions), defaultProperties),
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text, text.Length, text.Length), ltrOptions), defaultProperties),
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text, text.Length * 2, text.Length), rtlOptions), defaultProperties),
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text, text.Length * 3, text.Length), ltrOptions), defaultProperties)
};
var textSource = new FixedRunsTextSource(textRuns);
var formatter = new TextFormatterImpl(); var formatter = new TextFormatterImpl();
var textLine = var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, 200,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left, true, true, defaultProperties, TextWrapping.NoWrap, 0, 0));
var textBounds = textLine.GetTextBounds(0, text.Length * 4); var textBounds = textLine.GetTextBounds(0, text.Length);
Assert.Equal(3, textBounds.Count); Assert.Equal(2, textBounds.Count);
Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width));
textBounds = textLine.GetTextBounds(0, 4);
var secondRun = textLine.TextRuns[1] as ShapedTextCharacters;
Assert.Equal(1, textBounds.Count);
Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
textBounds = textLine.GetTextBounds(4, 3);
var firstRun = textLine.TextRuns[0] as ShapedTextCharacters;
Assert.Equal(1, textBounds.Count);
Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
textBounds = textLine.GetTextBounds(0, 5);
Assert.Equal(2, textBounds.Count);
Assert.Equal(7.201171875, textBounds[0].Rectangle.Width);
Assert.Equal(textLine.Start, textBounds[0].Rectangle.Left);
Assert.Equal(secondRun.Size.Width, textBounds[1].Rectangle.Width);
Assert.Equal(textLine.Start + firstRun.Size.Width, textBounds[1].Rectangle.Left);
} }
} }

2
tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs

@ -15,7 +15,7 @@ namespace Avalonia.UnitTests
{ {
var typeface = options.Typeface; var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize; var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel; var bidiLevel = options.BidiLevel;
var culture = options.Culture; var culture = options.Culture;
using (var buffer = new Buffer()) using (var buffer = new Buffer())

2
tests/Avalonia.UnitTests/MockTextShaperImpl.cs

@ -11,7 +11,7 @@ namespace Avalonia.UnitTests
{ {
var typeface = options.Typeface; var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize; var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel; var bidiLevel = options.BidiLevel;
var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);

Loading…
Cancel
Save