From 0cb25c78c95dd382a8368bd422c8ec3222ceff3e Mon Sep 17 00:00:00 2001 From: Loris Bognanni Date: Thu, 26 Nov 2020 09:01:08 +0000 Subject: [PATCH 1/6] Only handle the Enter key when the DropDown is open Fixes #5106 --- src/Avalonia.Controls/AutoCompleteBox.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index bfd633c947..b59fd7abde 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -1405,8 +1405,11 @@ namespace Avalonia.Controls break; case Key.Enter: - OnAdapterSelectionComplete(this, new RoutedEventArgs()); - e.Handled = true; + if (IsDropDownOpen) + { + OnAdapterSelectionComplete(this, new RoutedEventArgs()); + e.Handled = true; + } break; default: From 0e7e861009721dd7f0c1c964d5732d4105dab8cf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 26 Nov 2020 12:13:58 +0100 Subject: [PATCH 2/6] Fix TransformToVisual not dealing with non-invertible transforms. --- src/Avalonia.Visuals/Matrix.cs | 25 ++++++++++++++++--- src/Avalonia.Visuals/VisualExtensions.cs | 10 +++++++- .../Avalonia.Visuals.UnitTests/VisualTests.cs | 19 ++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 2ccfd43f03..ea7d0d63af 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -282,16 +282,16 @@ namespace Avalonia } /// - /// Inverts the Matrix. + /// Attempts to invert the Matrix. /// - /// The inverted matrix. - public Matrix Invert() + /// The inverted matrix or when matrix is not invertible. + public Matrix? TryInvert() { double d = GetDeterminant(); if (MathUtilities.IsZero(d)) { - throw new InvalidOperationException("Transform is not invertible."); + return null; } return new Matrix( @@ -303,6 +303,23 @@ namespace Avalonia ((_m12 * _m31) - (_m11 * _m32)) / d); } + /// + /// Inverts the Matrix. + /// + /// Matrix is not invertible. + /// The inverted matrix. + public Matrix Invert() + { + Matrix? inverted = TryInvert(); + + if (!inverted.HasValue) + { + throw new InvalidOperationException("Transform is not invertible."); + } + + return inverted.Value; + } + /// /// Parses a string. /// diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 6079e5941f..6875e2c55a 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -50,7 +50,15 @@ namespace Avalonia { var thisOffset = GetOffsetFrom(common, from); var thatOffset = GetOffsetFrom(common, to); - return -thatOffset * thisOffset; + + var thatOffsetInverted = thatOffset.TryInvert(); + + if (!thatOffsetInverted.HasValue) + { + return null; + } + + return thatOffsetInverted.Value * thisOffset; } return null; diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs index 447a68aa69..38131fbfca 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs @@ -235,6 +235,25 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new Point(100, 100), point); } + [Fact] + public void TransformToVisual_With_NonInvertible_RenderTransform_Should_Work() + { + var child = new Decorator + { + Width = 100, + Height = 100, + RenderTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 0 } + }; + var root = new TestRoot() { Child = child, Width = 400, Height = 400 }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(new Point(), root.DesiredSize)); + + var tr = root.TransformToVisual(child); + + Assert.Null(tr); + } + [Fact] public void Should_Not_Log_Binding_Error_When_Not_Attached_To_Logical_Tree() { From 713d4698ee66fc524eb480e69e7f14778b682e56 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 26 Nov 2020 12:25:35 +0100 Subject: [PATCH 3/6] Use different Try convention. --- src/Avalonia.Visuals/Matrix.cs | 16 +++++++++------- src/Avalonia.Visuals/VisualExtensions.cs | 8 +++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index ea7d0d63af..8136f843df 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -285,22 +285,26 @@ namespace Avalonia /// Attempts to invert the Matrix. /// /// The inverted matrix or when matrix is not invertible. - public Matrix? TryInvert() + public bool TryInvert(out Matrix inverted) { double d = GetDeterminant(); if (MathUtilities.IsZero(d)) { - return null; + inverted = default; + + return false; } - return new Matrix( + inverted = new Matrix( _m22 / d, -_m12 / d, -_m21 / d, _m11 / d, ((_m21 * _m32) - (_m22 * _m31)) / d, ((_m12 * _m31) - (_m11 * _m32)) / d); + + return true; } /// @@ -310,14 +314,12 @@ namespace Avalonia /// The inverted matrix. public Matrix Invert() { - Matrix? inverted = TryInvert(); - - if (!inverted.HasValue) + if (!TryInvert(out Matrix inverted)) { throw new InvalidOperationException("Transform is not invertible."); } - return inverted.Value; + return inverted; } /// diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 6875e2c55a..e6523a1469 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -51,14 +51,12 @@ namespace Avalonia var thisOffset = GetOffsetFrom(common, from); var thatOffset = GetOffsetFrom(common, to); - var thatOffsetInverted = thatOffset.TryInvert(); - - if (!thatOffsetInverted.HasValue) + if (!thatOffset.TryInvert(out var thatOffsetInverted)) { return null; } - - return thatOffsetInverted.Value * thisOffset; + + return thatOffsetInverted * thisOffset; } return null; From 55967506345e289c5328e54e7991988f314e8af7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 26 Nov 2020 11:36:47 +0000 Subject: [PATCH 4/6] initial attempt at detecting chrome double click. --- src/Avalonia.Native/DoubleClickHelper.cs | 31 ++++++++++++++++++++++++ src/Avalonia.Native/WindowImpl.cs | 22 ++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Native/DoubleClickHelper.cs diff --git a/src/Avalonia.Native/DoubleClickHelper.cs b/src/Avalonia.Native/DoubleClickHelper.cs new file mode 100644 index 0000000000..7618d6976a --- /dev/null +++ b/src/Avalonia.Native/DoubleClickHelper.cs @@ -0,0 +1,31 @@ +using Avalonia.Platform; + +namespace Avalonia.Native +{ + internal class DoubleClickHelper + { + private int _clickCount; + private Rect _lastClickRect; + private ulong _lastClickTime; + + public bool IsDoubleClick( + ulong timestamp, + Point p) + { + var settings = AvaloniaLocator.Current.GetService(); + var doubleClickTime = settings.DoubleClickTime.TotalMilliseconds; + + if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) + { + _clickCount = 0; + } + + ++_clickCount; + _lastClickTime = timestamp; + _lastClickRect = new Rect(p, new Size()) + .Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2)); + + return _clickCount == 2; + } + } +} diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index f60e83efe3..f3b60f07be 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -1,4 +1,5 @@ using System; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Input; @@ -17,6 +18,8 @@ namespace Avalonia.Native private readonly AvaloniaNativePlatformOpenGlInterface _glFeature; IAvnWindow _native; private double _extendTitleBarHeight = -1; + private DoubleClickHelper _doubleClickHelper; + internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature) @@ -24,6 +27,8 @@ namespace Avalonia.Native _factory = factory; _opts = opts; _glFeature = glFeature; + _doubleClickHelper = new DoubleClickHelper(); + using (var e = new WindowEvents(this)) { var context = _opts.UseGpu ? glFeature?.MainContext : null; @@ -118,7 +123,22 @@ namespace Avalonia.Native if(visual == null) { - _native.BeginMoveDrag(); + if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position)) + { + // TOGGLE WINDOW STATE. + if (WindowState == WindowState.Maximized || WindowState == WindowState.FullScreen) + { + WindowState = WindowState.Normal; + } + else + { + WindowState = WindowState.Maximized; + } + } + else + { + _native.BeginMoveDrag(); + } } } } From aa8e0651ee995d61ccc18a932511414eef022adb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 27 Nov 2020 09:26:32 +0100 Subject: [PATCH 5/6] Make ItemContainerInfo.Index setter public. It's needed to implement custom `ItemContainerGenerator`s. --- src/Avalonia.Controls/Generators/ItemContainerInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs index 023b108061..31d9a5c02e 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs @@ -37,6 +37,6 @@ namespace Avalonia.Controls.Generators /// /// Gets the index of the item in the collection. /// - public int Index { get; internal set; } + public int Index { get; set; } } -} \ No newline at end of file +} From 7165fe6abb147e9d7dadb72a32dc7540497e0e45 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 27 Nov 2020 14:23:30 -0500 Subject: [PATCH 6/6] Thread safe SKTextBlobBuilder --- .../Avalonia.Skia/PlatformRenderInterface.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 72700fb8fd..6d0be9f64d 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; + using Avalonia.Controls.Platform.Surfaces; using Avalonia.Media; using Avalonia.OpenGL; @@ -166,12 +168,13 @@ namespace Avalonia.Skia LinearMetrics = true }; - private static readonly SKTextBlobBuilder s_textBlobBuilder = new SKTextBlobBuilder(); + private static readonly ThreadLocal s_textBlobBuilderThreadLocal = new ThreadLocal(() => new SKTextBlobBuilder()); /// public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width) { var count = glyphRun.GlyphIndices.Length; + var textBlobBuilder = s_textBlobBuilderThreadLocal.Value; var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl; @@ -191,15 +194,15 @@ namespace Avalonia.Skia { if (glyphTypeface.IsFixedPitch) { - s_textBlobBuilder.AddRun(glyphRun.GlyphIndices.Buffer.Span, s_font); + textBlobBuilder.AddRun(glyphRun.GlyphIndices.Buffer.Span, s_font); - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); width = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[0]) * scale * glyphRun.GlyphIndices.Length; } else { - var buffer = s_textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); + var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); var positions = buffer.GetPositionSpan(); @@ -219,12 +222,12 @@ namespace Avalonia.Skia buffer.SetGlyphs(glyphRun.GlyphIndices.Buffer.Span); - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); } } else { - var buffer = s_textBlobBuilder.AllocatePositionedRun(s_font, count); + var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count); var glyphPositions = buffer.GetPositionSpan(); @@ -250,7 +253,7 @@ namespace Avalonia.Skia width = currentX; - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); } return new GlyphRunImpl(textBlob);