From c57ce7f5fc6345e7036525f2e5740f2ec20b915b Mon Sep 17 00:00:00 2001 From: Murdo R Ergeaux Date: Wed, 19 Aug 2020 17:22:34 +0100 Subject: [PATCH 1/3] Fix Issue 3825 --- src/Avalonia.Visuals/Media/GlyphRun.cs | 4 +--- src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Visuals/Media/GlyphRun.cs index a32a3e1b6c..da3a1f721c 100644 --- a/src/Avalonia.Visuals/Media/GlyphRun.cs +++ b/src/Avalonia.Visuals/Media/GlyphRun.cs @@ -555,7 +555,7 @@ namespace Avalonia.Media } } - return new Rect(0, 0, width, height); + return new Rect(0, GlyphTypeface.Ascent * Scale, width, height); } private void Set(ref T field, T value) @@ -595,8 +595,6 @@ namespace Avalonia.Media _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this, out var width); var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; - - _bounds = new Rect(0, 0, width, height); } void IDisposable.Dispose() diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs index eaf4effdbe..bdf05c4f86 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs @@ -25,7 +25,7 @@ namespace Avalonia.Rendering.SceneGraph GlyphRun glyphRun, Point baselineOrigin, IDictionary childScenes = null) - : base(glyphRun.Bounds, transform) + : base(glyphRun.Bounds.Translate(baselineOrigin), transform) { Transform = transform; Foreground = foreground?.ToImmutable(); From 8af00c9d10a19baa6d281e09b0bac97f1e291812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Thu, 20 Aug 2020 01:47:50 +0100 Subject: [PATCH 2/3] Relaxed conditions for data context type inference in compiled bindings. --- .../AvaloniaXamlIlDataContextTypeTransformer.cs | 6 +++--- .../Transformers/AvaloniaXamlIlWellKnownTypes.cs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index c4d67deb4c..349143253e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -24,7 +24,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { AvaloniaXamlIlDataContextTypeMetadataNode inferredDataContextTypeNode = null; AvaloniaXamlIlDataContextTypeMetadataNode directiveDataContextTypeNode = null; - bool isDataTemplate = on.Type.GetClrType().Equals(context.GetAvaloniaTypes().DataTemplate); for (int i = 0; i < on.Children.Count; ++i) { @@ -57,7 +56,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { inferredDataContextTypeNode = ParseDataContext(context, on, obj); } - else if(isDataTemplate + else if(context.GetAvaloniaTypes().DataTemplate.IsAssignableFrom(on.Type.GetClrType()) && pa.Property.Name == "DataType" && pa.Values[0] is XamlTypeExtensionNode dataTypeNode) { @@ -70,7 +69,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers // do more specialized inference if (directiveDataContextTypeNode is null) { - if (isDataTemplate && inferredDataContextTypeNode is null) + if (context.GetAvaloniaTypes().IDataTemplate.IsAssignableFrom(on.Type.GetClrType()) + && inferredDataContextTypeNode is null) { // Infer data type from collection binding on a control that displays items. var parentObject = context.ParentNodes().OfType().FirstOrDefault(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index f4ca76c21c..3dec96dc43 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -41,6 +41,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType ResolveByNameExtension { get; } public IXamlType DataTemplate { get; } + public IXamlType IDataTemplate { get; } public IXamlType IItemsPresenterHost { get; } public IXamlType ItemsRepeater { get; } public IXamlType ReflectionBindingExtension { get; } @@ -98,6 +99,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers CompiledBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension"); ResolveByNameExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ResolveByNameExtension"); DataTemplate = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Templates.DataTemplate"); + IDataTemplate = cfg.TypeSystem.GetType("Avalonia.Controls.Templates.IDataTemplate"); IItemsPresenterHost = cfg.TypeSystem.GetType("Avalonia.Controls.Presenters.IItemsPresenterHost"); ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater"); ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension"); From 7727722cb3902c6cf59940f5232377478cf74f1a Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 20 Aug 2020 11:21:37 +0200 Subject: [PATCH 3/3] Make sure to always return a valid CharacterHit for next / previous CharacterHit --- .../Media/TextFormatting/TextLineImpl.cs | 16 +++++ .../Media/TextFormatting/TextLineTests.cs | 60 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) 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]