From cb9f1bc9bd2b709e080f72fcd3517ae51799270b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 15:26:40 +0200 Subject: [PATCH 01/77] Add Transform property for Brush class --- src/Avalonia.Visuals/Media/Brush.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index cf7f5f531c..fe0dc63297 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -18,13 +18,19 @@ namespace Avalonia.Media public static readonly StyledProperty OpacityProperty = AvaloniaProperty.Register(nameof(Opacity), 1.0); + /// + /// Defines the property. + /// + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); + /// public event EventHandler Invalidated; static Brush() { Animation.Animation.RegisterAnimator(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType)); - AffectsRender(OpacityProperty); + AffectsRender(OpacityProperty, TransformProperty); } /// @@ -36,6 +42,15 @@ namespace Avalonia.Media set { SetValue(OpacityProperty, value); } } + /// + /// Gets or sets the transform of the brush. + /// + public Transform Transform + { + get { return GetValue(TransformProperty); } + set { SetValue(TransformProperty, value); } + } + /// /// Parses a brush string. /// From 4cea7a703fec2ed746e0007cadf0aa4ebdb73fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 18:11:48 +0200 Subject: [PATCH 02/77] Add Transform property to IBrush interface --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 4 +- src/Avalonia.Visuals/Media/IBrush.cs | 5 ++ .../Media/Immutable/ImmutableGradientBrush.cs | 5 ++ .../Immutable/ImmutableSolidColorBrush.cs | 9 ++- .../Media/Immutable/ImmutableTileBrush.cs | 5 ++ src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 67 ++++++++++++++++--- 6 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 39a4c3004c..7d2151a485 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -5,6 +5,8 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.Transform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.Transform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. @@ -74,4 +76,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWr InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. -Total Issues: 75 +Total Issues: 77 diff --git a/src/Avalonia.Visuals/Media/IBrush.cs b/src/Avalonia.Visuals/Media/IBrush.cs index 15b7681be4..f481bbff54 100644 --- a/src/Avalonia.Visuals/Media/IBrush.cs +++ b/src/Avalonia.Visuals/Media/IBrush.cs @@ -12,5 +12,10 @@ namespace Avalonia.Media /// Gets the opacity of the brush. /// double Opacity { get; } + + /// + /// Gets the transform of the brush. + /// + Transform Transform { get; } } } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs index 1f6e3bbcfd..6ae1bfe88a 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs @@ -39,6 +39,11 @@ namespace Avalonia.Media.Immutable /// public double Opacity { get; } + /// + /// Gets the transform of the brush. + /// + public Transform Transform { get; } + /// public GradientSpreadMethod SpreadMethod { get; } } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs index 8e93ac580e..1e48d91f05 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs @@ -46,11 +46,16 @@ namespace Avalonia.Media.Immutable /// public double Opacity { get; } + /// + /// Gets the transform of the brush. + /// + public Transform Transform { get; } + public bool Equals(ImmutableSolidColorBrush other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Color.Equals(other.Color) && Opacity.Equals(other.Opacity); + return Color.Equals(other.Color) && Opacity.Equals(other.Opacity) && (Transform == null && other.Transform == null ? true : Transform.Equals(other.Transform)); } public override bool Equals(object obj) @@ -62,7 +67,7 @@ namespace Avalonia.Media.Immutable { unchecked { - return (Color.GetHashCode() * 397) ^ Opacity.GetHashCode(); + return (Color.GetHashCode() * 397) ^ Opacity.GetHashCode() ^ (Transform is null ? 0 : Transform.GetHashCode()); } } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs index fd4d921516..0557f5c1d6 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs @@ -69,6 +69,11 @@ namespace Avalonia.Media.Immutable /// public double Opacity { get; } + /// + /// Gets the transform of the brush. + /// + public Transform Transform { get; } + /// public RelativeRect SourceRect { get; } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 2352b8b076..5bade6c5e5 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -608,10 +608,21 @@ namespace Avalonia.Skia var end = position + linearGradient.EndPoint.ToPixels(targetRect.Size).ToSKPoint(); // would be nice to cache these shaders possibly? - using (var shader = - SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode)) + if (linearGradient.Transform is null) { - paintWrapper.Paint.Shader = shader; + using (var shader = + SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode)) + { + paintWrapper.Paint.Shader = shader; + } + } + else + { + using (var shader = + SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, linearGradient.Transform.Value.ToSKMatrix())) + { + paintWrapper.Paint.Shader = shader; + } } break; @@ -626,10 +637,21 @@ namespace Avalonia.Skia if (origin.Equals(center)) { // when the origin is the same as the center the Skia RadialGradient acts the same as D2D - using (var shader = - SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) + if (radialGradient.Transform is null) { - paintWrapper.Paint.Shader = shader; + using (var shader = + SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) + { + paintWrapper.Paint.Shader = shader; + } + } + else + { + using (var shader = + SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode, radialGradient.Transform.Value.ToSKMatrix())) + { + paintWrapper.Paint.Shader = shader; + } } } else @@ -653,12 +675,25 @@ namespace Avalonia.Skia } // compose with a background colour of the final stop to match D2D's behaviour of filling with the final color - using (var shader = SKShader.CreateCompose( - SKShader.CreateColor(reversedColors[0]), - SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode) - )) + if (radialGradient.Transform is null) { - paintWrapper.Paint.Shader = shader; + using (var shader = SKShader.CreateCompose( + SKShader.CreateColor(reversedColors[0]), + SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode) + )) + { + paintWrapper.Paint.Shader = shader; + } + } + else + { + using (var shader = SKShader.CreateCompose( + SKShader.CreateColor(reversedColors[0]), + SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode, radialGradient.Transform.Value.ToSKMatrix()) + )) + { + paintWrapper.Paint.Shader = shader; + } } } @@ -673,6 +708,11 @@ namespace Avalonia.Skia var angle = (float)(conicGradient.Angle - 90); var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y); + if (conicGradient.Transform is not null) + { + rotation = rotation.PreConcat(conicGradient.Transform.Value.ToSKMatrix()); + } + using (var shader = SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation)) { @@ -740,6 +780,11 @@ namespace Avalonia.Skia var paintTransform = default(SKMatrix); + if (tileBrush.Transform is not null) + { + paintTransform = paintTransform.PreConcat(tileBrush.Transform.Value.ToSKMatrix()); + } + SKMatrix.Concat( ref paintTransform, tileTransform, From 4a443f691675bd82a50a768c8d21102c2a2f6884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 19:10:26 +0200 Subject: [PATCH 03/77] Move transform --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 5bade6c5e5..c282b4c24d 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -780,16 +780,16 @@ namespace Avalonia.Skia var paintTransform = default(SKMatrix); - if (tileBrush.Transform is not null) - { - paintTransform = paintTransform.PreConcat(tileBrush.Transform.Value.ToSKMatrix()); - } - SKMatrix.Concat( ref paintTransform, tileTransform, SKMatrix.CreateScale((float)(96.0 / _dpi.X), (float)(96.0 / _dpi.Y))); + if (tileBrush.Transform is not null) + { + paintTransform = paintTransform.PreConcat(tileBrush.Transform.Value.ToSKMatrix()); + } + using (var shader = image.ToShader(tileX, tileY, paintTransform)) { paintWrapper.Paint.Shader = shader; From d47c47935e354fc24a076b0c3383d233b3b84792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 19:26:27 +0200 Subject: [PATCH 04/77] Do not use not pattern --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index c282b4c24d..4e47953863 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -708,7 +708,7 @@ namespace Avalonia.Skia var angle = (float)(conicGradient.Angle - 90); var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y); - if (conicGradient.Transform is not null) + if (conicGradient.Transform is { }) { rotation = rotation.PreConcat(conicGradient.Transform.Value.ToSKMatrix()); } @@ -785,7 +785,7 @@ namespace Avalonia.Skia tileTransform, SKMatrix.CreateScale((float)(96.0 / _dpi.X), (float)(96.0 / _dpi.Y))); - if (tileBrush.Transform is not null) + if (tileBrush.Transform is { }) { paintTransform = paintTransform.PreConcat(tileBrush.Transform.Value.ToSKMatrix()); } From 13f137c7abbbbb0fec8eec756c170b734e8e97d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 20:36:48 +0200 Subject: [PATCH 05/77] Fix deferred rendering by using immutable transform --- .../Animators/GradientBrushAnimator.cs | 6 +++++ src/Avalonia.Visuals/ApiCompatBaseline.txt | 12 ++++++--- src/Avalonia.Visuals/Media/Brush.cs | 6 ++--- src/Avalonia.Visuals/Media/IBrush.cs | 2 +- .../Media/IMutableTransform.cs | 6 +++++ .../Media/Immutable/IImmutableTransform.cs | 21 ++++++++++++++++ .../Immutable/ImmutableConicGradientBrush.cs | 4 ++- .../Media/Immutable/ImmutableGradientBrush.cs | 7 ++++-- .../Media/Immutable/ImmutableImageBrush.cs | 3 +++ .../Immutable/ImmutableLinearGradientBrush.cs | 4 ++- .../Immutable/ImmutableRadialGradientBrush.cs | 4 ++- .../Immutable/ImmutableSolidColorBrush.cs | 2 +- .../Media/Immutable/ImmutableTileBrush.cs | 6 ++++- .../Media/Immutable/ImmutableVisualBrush.cs | 3 +++ src/Avalonia.Visuals/Media/Transform.cs | 7 ++++++ .../Media/TransformExtensions.cs | 25 +++++++++++++++++++ 16 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/Immutable/IImmutableTransform.cs create mode 100644 src/Avalonia.Visuals/Media/TransformExtensions.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index 864e12413f..bb8457de9a 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -30,6 +30,7 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), + oldValue.Transform, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -39,6 +40,7 @@ namespace Avalonia.Animation.Animators return new ImmutableConicGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), + oldValue.Transform, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -47,6 +49,7 @@ namespace Avalonia.Animation.Animators return new ImmutableLinearGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), + oldValue.Transform, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -93,16 +96,19 @@ namespace Avalonia.Animation.Animators case IRadialGradientBrush oldRadial: return new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, + oldRadial.Transform, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, + oldConic.Transform, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, + oldLinear.Transform, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 7d2151a485..41985dc078 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -5,9 +5,15 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.Transform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.Transform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IMutableTransform.ToImmutable()' is present in the implementation but not in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableConicGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableLinearGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. +MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableTileBrush..ctor(Avalonia.Media.AlignmentX, Avalonia.Media.AlignmentY, Avalonia.RelativeRect, System.Double, Avalonia.RelativeRect, Avalonia.Media.Stretch, Avalonia.Media.TileMode, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. @@ -76,4 +82,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWr InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. -Total Issues: 77 +Total Issues: 83 diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index fe0dc63297..101b28a376 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -21,8 +21,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty TransformProperty = - AvaloniaProperty.Register(nameof(Transform)); + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); /// public event EventHandler Invalidated; @@ -45,7 +45,7 @@ namespace Avalonia.Media /// /// Gets or sets the transform of the brush. /// - public Transform Transform + public ITransform Transform { get { return GetValue(TransformProperty); } set { SetValue(TransformProperty, value); } diff --git a/src/Avalonia.Visuals/Media/IBrush.cs b/src/Avalonia.Visuals/Media/IBrush.cs index f481bbff54..c3d03fb35b 100644 --- a/src/Avalonia.Visuals/Media/IBrush.cs +++ b/src/Avalonia.Visuals/Media/IBrush.cs @@ -16,6 +16,6 @@ namespace Avalonia.Media /// /// Gets the transform of the brush. /// - Transform Transform { get; } + ITransform Transform { get; } } } diff --git a/src/Avalonia.Visuals/Media/IMutableTransform.cs b/src/Avalonia.Visuals/Media/IMutableTransform.cs index 2033c434c0..d7a106b22b 100644 --- a/src/Avalonia.Visuals/Media/IMutableTransform.cs +++ b/src/Avalonia.Visuals/Media/IMutableTransform.cs @@ -8,5 +8,11 @@ namespace Avalonia.Media /// Raised when the transform changes. /// event EventHandler Changed; + + /// + /// Converts a transform to an immutable transform. + /// + /// The immutable transform + ITransform ToImmutable(); } } diff --git a/src/Avalonia.Visuals/Media/Immutable/IImmutableTransform.cs b/src/Avalonia.Visuals/Media/Immutable/IImmutableTransform.cs new file mode 100644 index 0000000000..d5ff2b8317 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Immutable/IImmutableTransform.cs @@ -0,0 +1,21 @@ +using Avalonia.VisualTree; + +namespace Avalonia.Media.Immutable +{ + /// + /// Represents a transform on an . + /// + public class ImmutableTransform : ITransform + { + public Matrix Value { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix. + public ImmutableTransform(Matrix matrix) + { + Value = matrix; + } + } +} diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs index d3c80dfcad..dc9360d162 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs @@ -12,16 +12,18 @@ namespace Avalonia.Media.Immutable /// /// The gradient stops. /// The opacity of the brush. + /// The transform of the brush. /// The spread method. /// The center point for the gradient. /// The starting angle for the gradient. public ImmutableConicGradientBrush( IReadOnlyList gradientStops, double opacity = 1, + ITransform transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, double angle = 0) - : base(gradientStops, opacity, spreadMethod) + : base(gradientStops, opacity, transform, spreadMethod) { Center = center ?? RelativePoint.Center; Angle = angle; diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs index 6ae1bfe88a..12e1a39635 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs @@ -12,14 +12,17 @@ namespace Avalonia.Media.Immutable /// /// The gradient stops. /// The opacity of the brush. + /// The transform of the brush. /// The spread method. protected ImmutableGradientBrush( IReadOnlyList gradientStops, double opacity, + ITransform transform, GradientSpreadMethod spreadMethod) { GradientStops = gradientStops; Opacity = opacity; + Transform = transform; SpreadMethod = spreadMethod; } @@ -28,7 +31,7 @@ namespace Avalonia.Media.Immutable /// /// The brush from which this brush's properties should be copied. protected ImmutableGradientBrush(GradientBrush source) - : this(source.GradientStops.ToImmutable(), source.Opacity, source.SpreadMethod) + : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform.ToImmutable(), source.SpreadMethod) { } @@ -42,7 +45,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the transform of the brush. /// - public Transform Transform { get; } + public ITransform Transform { get; } /// public GradientSpreadMethod SpreadMethod { get; } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs index 15da8f8b43..43b157faa9 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs @@ -16,6 +16,7 @@ namespace Avalonia.Media.Immutable /// The vertical alignment of a tile in the destination. /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. + /// The transform of the brush. /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -28,6 +29,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, double opacity = 1, + ITransform transform = null, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -37,6 +39,7 @@ namespace Avalonia.Media.Immutable alignmentY, destinationRect ?? RelativeRect.Fill, opacity, + transform, sourceRect ?? RelativeRect.Fill, stretch, tileMode, diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs index 912d77d763..2270a5e855 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -12,16 +12,18 @@ namespace Avalonia.Media.Immutable /// /// The gradient stops. /// The opacity of the brush. + /// The transform of the brush. /// The spread method. /// The start point for the gradient. /// The end point for the gradient. public ImmutableLinearGradientBrush( IReadOnlyList gradientStops, double opacity = 1, + ITransform transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? startPoint = null, RelativePoint? endPoint = null) - : base(gradientStops, opacity, spreadMethod) + : base(gradientStops, opacity, transform, spreadMethod) { StartPoint = startPoint ?? RelativePoint.TopLeft; EndPoint = endPoint ?? RelativePoint.BottomRight; diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs index e26fbab5f5..f621d83213 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -12,6 +12,7 @@ namespace Avalonia.Media.Immutable /// /// The gradient stops. /// The opacity of the brush. + /// The transform of the brush. /// The spread method. /// The start point for the gradient. /// @@ -23,11 +24,12 @@ namespace Avalonia.Media.Immutable public ImmutableRadialGradientBrush( IReadOnlyList gradientStops, double opacity = 1, + ITransform transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, RelativePoint? gradientOrigin = null, double radius = 0.5) - : base(gradientStops, opacity, spreadMethod) + : base(gradientStops, opacity, transform, spreadMethod) { Center = center ?? RelativePoint.Center; GradientOrigin = gradientOrigin ?? RelativePoint.Center; diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs index 1e48d91f05..daf73f8974 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs @@ -49,7 +49,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the transform of the brush. /// - public Transform Transform { get; } + public ITransform Transform { get; } public bool Equals(ImmutableSolidColorBrush other) { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs index 0557f5c1d6..e96ef026af 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs @@ -14,6 +14,7 @@ namespace Avalonia.Media.Immutable /// The vertical alignment of a tile in the destination. /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. + /// The transform of the brush. /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -25,6 +26,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY, RelativeRect destinationRect, double opacity, + ITransform transform, RelativeRect sourceRect, Stretch stretch, TileMode tileMode, @@ -34,6 +36,7 @@ namespace Avalonia.Media.Immutable AlignmentY = alignmentY; DestinationRect = destinationRect; Opacity = opacity; + Transform = transform; SourceRect = sourceRect; Stretch = stretch; TileMode = tileMode; @@ -50,6 +53,7 @@ namespace Avalonia.Media.Immutable source.AlignmentY, source.DestinationRect, source.Opacity, + source.Transform.ToImmutable(), source.SourceRect, source.Stretch, source.TileMode, @@ -72,7 +76,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the transform of the brush. /// - public Transform Transform { get; } + public ITransform Transform { get; } /// public RelativeRect SourceRect { get; } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs index 4c6aae08ab..ccc0b64f98 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs @@ -16,6 +16,7 @@ namespace Avalonia.Media.Immutable /// The vertical alignment of a tile in the destination. /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. + /// The transform of the brush. /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -28,6 +29,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, double opacity = 1, + Transform transform = null, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -37,6 +39,7 @@ namespace Avalonia.Media.Immutable alignmentY, destinationRect ?? RelativeRect.Fill, opacity, + transform, sourceRect ?? RelativeRect.Fill, stretch, tileMode, diff --git a/src/Avalonia.Visuals/Media/Transform.cs b/src/Avalonia.Visuals/Media/Transform.cs index 7cf1b35ada..2df0c7cc27 100644 --- a/src/Avalonia.Visuals/Media/Transform.cs +++ b/src/Avalonia.Visuals/Media/Transform.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Animation; using Avalonia.Animation.Animators; +using Avalonia.Media.Immutable; using Avalonia.VisualTree; namespace Avalonia.Media @@ -44,6 +45,12 @@ namespace Avalonia.Media Changed?.Invoke(this, EventArgs.Empty); } + /// + public ITransform ToImmutable() + { + return new ImmutableTransform(this.Value); + } + /// /// Returns a String representing this transform matrix instance. /// diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Visuals/Media/TransformExtensions.cs new file mode 100644 index 0000000000..2f473fcadb --- /dev/null +++ b/src/Avalonia.Visuals/Media/TransformExtensions.cs @@ -0,0 +1,25 @@ +using System; + +namespace Avalonia.Media +{ + /// + /// Extension methods for transform classes. + /// + public static class TransformExtensions + { + /// + /// Converts a transform to an immutable transform. + /// + /// The transform. + /// + /// The result of calling if the transform is mutable, + /// otherwise . + /// + public static ITransform ToImmutable(this ITransform transform) + { + Contract.Requires(transform != null); + + return (transform as IMutableTransform)?.ToImmutable() ?? transform; + } + } +} From 90a027aef82e1fcddfbc4a8dcb6f40009328acf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 20:39:22 +0200 Subject: [PATCH 06/77] Rename --- .../Immutable/{IImmutableTransform.cs => ImmutableTransform.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Visuals/Media/Immutable/{IImmutableTransform.cs => ImmutableTransform.cs} (100%) diff --git a/src/Avalonia.Visuals/Media/Immutable/IImmutableTransform.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/IImmutableTransform.cs rename to src/Avalonia.Visuals/Media/Immutable/ImmutableTransform.cs From e3bfa12909d1f9d8840a0114512bd4d587a8942f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 21:40:19 +0200 Subject: [PATCH 07/77] Use ImmutableTransform --- .../Animation/Animators/GradientBrushAnimator.cs | 13 +++++++------ .../Animation/Animators/TransformAnimator.cs | 7 +++++++ src/Avalonia.Visuals/Media/IMutableTransform.cs | 6 ------ .../Media/Immutable/ImmutableConicGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableImageBrush.cs | 2 +- .../Media/Immutable/ImmutableLinearGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableRadialGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableVisualBrush.cs | 2 +- src/Avalonia.Visuals/Media/Transform.cs | 7 +++++-- src/Avalonia.Visuals/Media/TransformExtensions.cs | 7 ++++--- 11 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index bb8457de9a..5d206ad9e1 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -16,6 +16,7 @@ namespace Avalonia.Animation.Animators { private static readonly RelativePointAnimator s_relativePointAnimator = new RelativePointAnimator(); private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator(); + private static readonly TransformAnimator s_transformAnimator = new TransformAnimator(); public override IGradientBrush? Interpolate(double progress, IGradientBrush? oldValue, IGradientBrush? newValue) { @@ -30,7 +31,7 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform, + s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -40,7 +41,7 @@ namespace Avalonia.Animation.Animators return new ImmutableConicGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform, + s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -49,7 +50,7 @@ namespace Avalonia.Animation.Animators return new ImmutableLinearGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform, + s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -96,19 +97,19 @@ namespace Avalonia.Animation.Animators case IRadialGradientBrush oldRadial: return new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, - oldRadial.Transform, + new ImmutableTransform(oldRadial.Transform.Value), oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, - oldConic.Transform, + new ImmutableTransform(oldConic.Transform.Value), oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, - oldLinear.Transform, + new ImmutableTransform(oldLinear.Transform.Value), oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 1b2142f6c9..0299cba0e5 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -2,6 +2,7 @@ using System.Reactive.Disposables; using Avalonia.Logging; using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.Media.Transformation; namespace Avalonia.Animation.Animators @@ -86,6 +87,12 @@ namespace Avalonia.Animation.Animators return null; } + internal ImmutableTransform InterpolateTransform(double progress, ITransform oldValue, ITransform newValue) + { + // TODO: + return new ImmutableTransform(newValue.Value); + } + /// public override double Interpolate(double p, double o, double n) => 0; } diff --git a/src/Avalonia.Visuals/Media/IMutableTransform.cs b/src/Avalonia.Visuals/Media/IMutableTransform.cs index d7a106b22b..2033c434c0 100644 --- a/src/Avalonia.Visuals/Media/IMutableTransform.cs +++ b/src/Avalonia.Visuals/Media/IMutableTransform.cs @@ -8,11 +8,5 @@ namespace Avalonia.Media /// Raised when the transform changes. /// event EventHandler Changed; - - /// - /// Converts a transform to an immutable transform. - /// - /// The immutable transform - ITransform ToImmutable(); } } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs index dc9360d162..869262c7a4 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media.Immutable public ImmutableConicGradientBrush( IReadOnlyList gradientStops, double opacity = 1, - ITransform transform = null, + ImmutableTransform transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, double angle = 0) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs index 12e1a39635..87ada91cbd 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs @@ -17,7 +17,7 @@ namespace Avalonia.Media.Immutable protected ImmutableGradientBrush( IReadOnlyList gradientStops, double opacity, - ITransform transform, + ImmutableTransform transform, GradientSpreadMethod spreadMethod) { GradientStops = gradientStops; diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs index 43b157faa9..41456b7f92 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs @@ -29,7 +29,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, double opacity = 1, - ITransform transform = null, + ImmutableTransform transform = null, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs index 2270a5e855..1c905c166e 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media.Immutable public ImmutableLinearGradientBrush( IReadOnlyList gradientStops, double opacity = 1, - ITransform transform = null, + ImmutableTransform transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? startPoint = null, RelativePoint? endPoint = null) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs index f621d83213..910e14faf7 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -24,7 +24,7 @@ namespace Avalonia.Media.Immutable public ImmutableRadialGradientBrush( IReadOnlyList gradientStops, double opacity = 1, - ITransform transform = null, + ImmutableTransform transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, RelativePoint? gradientOrigin = null, diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs index ccc0b64f98..abec170419 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs @@ -29,7 +29,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, double opacity = 1, - Transform transform = null, + ImmutableTransform transform = null, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, diff --git a/src/Avalonia.Visuals/Media/Transform.cs b/src/Avalonia.Visuals/Media/Transform.cs index 2df0c7cc27..5a757b44c6 100644 --- a/src/Avalonia.Visuals/Media/Transform.cs +++ b/src/Avalonia.Visuals/Media/Transform.cs @@ -45,8 +45,11 @@ namespace Avalonia.Media Changed?.Invoke(this, EventArgs.Empty); } - /// - public ITransform ToImmutable() + /// + /// Converts a transform to an immutable transform. + /// + /// The immutable transform + public ImmutableTransform ToImmutable() { return new ImmutableTransform(this.Value); } diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Visuals/Media/TransformExtensions.cs index 2f473fcadb..c264d483cd 100644 --- a/src/Avalonia.Visuals/Media/TransformExtensions.cs +++ b/src/Avalonia.Visuals/Media/TransformExtensions.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Media.Immutable; namespace Avalonia.Media { @@ -12,14 +13,14 @@ namespace Avalonia.Media /// /// The transform. /// - /// The result of calling if the transform is mutable, + /// The result of calling if the transform is mutable, /// otherwise . /// - public static ITransform ToImmutable(this ITransform transform) + public static ImmutableTransform ToImmutable(this ITransform transform) { Contract.Requires(transform != null); - return (transform as IMutableTransform)?.ToImmutable() ?? transform; + return (transform as Transform)?.ToImmutable() ?? new ImmutableTransform(transform.Value); } } } From 4f90ab46f0a7869d0f6294a93c61d7dad748f423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 21:44:54 +0200 Subject: [PATCH 08/77] Check for null --- .../Animation/Animators/GradientBrushAnimator.cs | 12 ++++++------ .../Media/Immutable/ImmutableGradientBrush.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index 5d206ad9e1..817c53698e 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -31,7 +31,7 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform), + oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -41,7 +41,7 @@ namespace Avalonia.Animation.Animators return new ImmutableConicGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform), + oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -50,7 +50,7 @@ namespace Avalonia.Animation.Animators return new ImmutableLinearGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform), + oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -97,19 +97,19 @@ namespace Avalonia.Animation.Animators case IRadialGradientBrush oldRadial: return new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, - new ImmutableTransform(oldRadial.Transform.Value), + oldRadial.Transform is { } ? new ImmutableTransform(oldRadial.Transform.Value) : null, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, - new ImmutableTransform(oldConic.Transform.Value), + oldConic.Transform is { } ? new ImmutableTransform(oldConic.Transform.Value) : null, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, - new ImmutableTransform(oldLinear.Transform.Value), + oldLinear.Transform is { } ? new ImmutableTransform(oldLinear.Transform.Value) : null, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs index 87ada91cbd..78e46f52d0 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs @@ -31,7 +31,7 @@ namespace Avalonia.Media.Immutable /// /// The brush from which this brush's properties should be copied. protected ImmutableGradientBrush(GradientBrush source) - : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform.ToImmutable(), source.SpreadMethod) + : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform?.ToImmutable(), source.SpreadMethod) { } From e361b827eb6ad7edf0cf08831f8de8002a25a72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 21:51:01 +0200 Subject: [PATCH 09/77] Add transform --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 2 +- .../Media/Immutable/ImmutableSolidColorBrush.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 41985dc078..18b8f4c5a8 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -7,12 +7,12 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IMutableTransform.ToImmutable()' is present in the implementation but not in the contract. MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableConicGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableLinearGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableSolidColorBrush..ctor(Avalonia.Media.Color, System.Double)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableTileBrush..ctor(Avalonia.Media.AlignmentX, Avalonia.Media.AlignmentY, Avalonia.RelativeRect, System.Double, Avalonia.RelativeRect, Avalonia.Media.Stretch, Avalonia.Media.TileMode, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs index daf73f8974..ac7d336f30 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs @@ -12,10 +12,12 @@ namespace Avalonia.Media.Immutable /// /// The color to use. /// The opacity of the brush. - public ImmutableSolidColorBrush(Color color, double opacity = 1) + /// The transform of the brush. + public ImmutableSolidColorBrush(Color color, double opacity = 1, ImmutableTransform transform = null) { Color = color; Opacity = opacity; + Transform = null; } /// @@ -32,7 +34,7 @@ namespace Avalonia.Media.Immutable /// /// The brush from which this brush's properties should be copied. public ImmutableSolidColorBrush(ISolidColorBrush source) - : this(source.Color, source.Opacity) + : this(source.Color, source.Opacity, source.Transform?.ToImmutable()) { } From 167ad6c003ad58ea5739a8574ba25102b19c1d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 21:51:08 +0200 Subject: [PATCH 10/77] Use ImmutableTransform --- src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs index e96ef026af..1eaf3bce67 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs @@ -26,7 +26,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY, RelativeRect destinationRect, double opacity, - ITransform transform, + ImmutableTransform transform, RelativeRect sourceRect, Stretch stretch, TileMode tileMode, From 81daa4f03b50c53cae15f2a770daba79eb613d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 22:10:51 +0200 Subject: [PATCH 11/77] Add Matrix interpolation --- .../Animators/GradientBrushAnimator.cs | 6 +++--- .../Animation/Animators/TransformAnimator.cs | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index 817c53698e..ce0cfa2ce3 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -31,7 +31,7 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateMatrix(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -41,7 +41,7 @@ namespace Avalonia.Animation.Animators return new ImmutableConicGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateMatrix(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -50,7 +50,7 @@ namespace Avalonia.Animation.Animators return new ImmutableLinearGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateMatrix(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 0299cba0e5..102e0f83a8 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -87,10 +87,19 @@ namespace Avalonia.Animation.Animators return null; } - internal ImmutableTransform InterpolateTransform(double progress, ITransform oldValue, ITransform newValue) - { - // TODO: - return new ImmutableTransform(newValue.Value); + internal ImmutableTransform InterpolateMatrix(double progress, ITransform oldValue, ITransform newValue) + { + var from = oldValue.Value; + var to = newValue.Value; + var matrix = new Matrix( + ((to.M11 - from.M11) * progress) + from.M11, + ((to.M12 - from.M12) * progress) + from.M12, + ((to.M21 - from.M21) * progress) + from.M21, + ((to.M22 - from.M22) * progress) + from.M22, + ((to.M31 - from.M31) * progress) + from.M31, + ((to.M32 - from.M32) * progress) + from.M32); + + return new ImmutableTransform(matrix); } /// From cca7b962287be0b209de79181e0ef6a8375d822f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 22:57:27 +0200 Subject: [PATCH 12/77] Mark field as private --- src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 102e0f83a8..271430ee42 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -12,7 +12,7 @@ namespace Avalonia.Animation.Animators /// public class TransformAnimator : Animator { - DoubleAnimator _doubleAnimator; + private DoubleAnimator _doubleAnimator; /// public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable obsMatch, Action onComplete) From 97332822c368ae1250b854c8370337a52566d459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sun, 1 Aug 2021 22:57:40 +0200 Subject: [PATCH 13/77] Add MatrixAnimator --- .../Animators/GradientBrushAnimator.cs | 12 ++++++++--- .../Animation/Animators/MatrixAnimator.cs | 20 +++++++++++++++++++ .../Animation/Animators/TransformAnimator.cs | 15 -------------- .../Animation/Transitions/MatrixTransition.cs | 11 ++++++++++ src/Avalonia.Visuals/Matrix.cs | 10 ++++++++++ 5 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs create mode 100644 src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index ce0cfa2ce3..3c13752992 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -17,6 +17,7 @@ namespace Avalonia.Animation.Animators private static readonly RelativePointAnimator s_relativePointAnimator = new RelativePointAnimator(); private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator(); private static readonly TransformAnimator s_transformAnimator = new TransformAnimator(); + private static readonly MatrixAnimator _matrixAnimator = new MatrixAnimator(); public override IGradientBrush? Interpolate(double progress, IGradientBrush? oldValue, IGradientBrush? newValue) { @@ -31,7 +32,7 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateMatrix(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } && newValue.Transform is { } ? InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -41,7 +42,7 @@ namespace Avalonia.Animation.Animators return new ImmutableConicGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateMatrix(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } && newValue.Transform is { } ? InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -50,7 +51,7 @@ namespace Avalonia.Animation.Animators return new ImmutableLinearGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? s_transformAnimator.InterpolateMatrix(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } && newValue.Transform is { } ? InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -90,6 +91,11 @@ namespace Avalonia.Animation.Animators return stops; } + private ImmutableTransform InterpolateTransform(double progress, ITransform oldValue, ITransform newValue) + { + return new ImmutableTransform(_matrixAnimator.Interpolate(progress, oldValue.Value, newValue.Value)); + } + internal static IGradientBrush ConvertSolidColorBrushToGradient(IGradientBrush gradientBrush, ISolidColorBrush solidColorBrush) { switch (gradientBrush) diff --git a/src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs new file mode 100644 index 0000000000..d5b6267a3c --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs @@ -0,0 +1,20 @@ +namespace Avalonia.Animation.Animators +{ + /// + /// Animator that handles properties. + /// + public class MatrixAnimator : Animator + { + /// + public override Matrix Interpolate(double progress, Matrix oldValue, Matrix newValue) + { + return new Matrix( + ((newValue.M11 - oldValue.M11) * progress) + oldValue.M11, + ((newValue.M12 - oldValue.M12) * progress) + oldValue.M12, + ((newValue.M21 - oldValue.M21) * progress) + oldValue.M21, + ((newValue.M22 - oldValue.M22) * progress) + oldValue.M22, + ((newValue.M31 - oldValue.M31) * progress) + oldValue.M31, + ((newValue.M32 - oldValue.M32) * progress) + oldValue.M32); + } + } +} diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 271430ee42..1c5f6fa786 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -87,21 +87,6 @@ namespace Avalonia.Animation.Animators return null; } - internal ImmutableTransform InterpolateMatrix(double progress, ITransform oldValue, ITransform newValue) - { - var from = oldValue.Value; - var to = newValue.Value; - var matrix = new Matrix( - ((to.M11 - from.M11) * progress) + from.M11, - ((to.M12 - from.M12) * progress) + from.M12, - ((to.M21 - from.M21) * progress) + from.M21, - ((to.M22 - from.M22) * progress) + from.M22, - ((to.M31 - from.M31) * progress) + from.M31, - ((to.M32 - from.M32) * progress) + from.M32); - - return new ImmutableTransform(matrix); - } - /// public override double Interpolate(double p, double o, double n) => 0; } diff --git a/src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs new file mode 100644 index 0000000000..59bf06ca26 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs @@ -0,0 +1,11 @@ +using Avalonia.Animation.Animators; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with type. + /// + public class MatrixTransition : AnimatorDrivenTransition + { + } +} diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 8136f843df..7a56aa6fe2 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -1,5 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK +using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -12,6 +15,13 @@ namespace Avalonia #endif readonly struct Matrix : IEquatable { + static Matrix() + { +#if !BUILDTASK + Animation.Animation.RegisterAnimator(prop => typeof(Matrix).IsAssignableFrom(prop.PropertyType)); +#endif + } + private readonly double _m11; private readonly double _m12; private readonly double _m21; From 0beaa1c642a71fa40b3e03264834b47c6a4548ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 2 Aug 2021 08:34:10 +0200 Subject: [PATCH 14/77] Fix tests --- src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs index 1eaf3bce67..ed722275f5 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs @@ -53,7 +53,7 @@ namespace Avalonia.Media.Immutable source.AlignmentY, source.DestinationRect, source.Opacity, - source.Transform.ToImmutable(), + source.Transform?.ToImmutable(), source.SourceRect, source.Stretch, source.TileMode, From b7b9b8a35c52abf70fda96780dff0acfc5e652ed Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 22:16:59 -0500 Subject: [PATCH 15/77] Add new HsvColor struct --- src/Avalonia.Visuals/Media/HsvColor.cs | 493 +++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 src/Avalonia.Visuals/Media/HsvColor.cs diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs new file mode 100644 index 0000000000..605d89346e --- /dev/null +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -0,0 +1,493 @@ +using System; + +namespace Avalonia.Media +{ + /// + /// Defines a color using the hue/saturation/value (HSV) model. + /// + public struct HsvColor : IEquatable + { + /// + /// Represents a null with zero set for all channels. + /// + public static readonly HsvColor Empty = new HsvColor(); + + //////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new instance of the struct. + /// + /// The Hue channel value in the range from 0..360. + /// The Saturation channel value in the range from 0..1. + /// The Value channel value in the range from 0..1. + public HsvColor( + double hue, + double saturation, + double value) + { + A = 1.0; + H = Math.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); + S = Math.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); + V = Math.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The Hue channel value in the range from 0..360. + /// The Saturation channel value in the range from 0..1. + /// The Value channel value in the range from 0..1. + /// The Alpha (transparency) channel value in the range from 0..1. + public HsvColor( + double hue, + double saturation, + double value, + double alpha) + { + A = Math.Clamp((double.IsNaN(alpha) ? 1 : alpha), 0, 1); // Default to no transparency + H = Math.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); + S = Math.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); + V = Math.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The RGB color to convert to HSV. + public HsvColor(Color color) + { + var hsv = HsvColor.FromRgb(color); + + A = hsv.A; + H = hsv.H; + S = hsv.S; + V = hsv.V; + } + + //////////////////////////////////////////////////////////////////////// + // Properties + //////////////////////////////////////////////////////////////////////// + + /// + /// Gets the Alpha (transparency) channel value in the range from 0..1. + /// + public double A { get; } + + /// + /// Gets the Hue channel value in the range from 0..360. + /// + public double H { get; } + + /// + /// Gets the Saturation channel value in the range from 0..1. + /// + public double S { get; } + + /// + /// Gets the Value channel value in the range from 0..1. + /// + public double V { get; } + + /// + /// Gets a value indicating whether this is uninitialized and + /// considered null. + /// + public bool IsEmpty + { + get => A == 0 && + H == 0 && + S == 0 && + V == 0; + } + + //////////////////////////////////////////////////////////////////////// + // Methods + //////////////////////////////////////////////////////////////////////// + + /// + public bool Equals(HsvColor other) + { + if (object.Equals(other.A, A) == false) { return false; } + if (object.Equals(other.H, H) == false) { return false; } + if (object.Equals(other.S, S) == false) { return false; } + if (object.Equals(other.V, V) == false) { return false; } + + return true; + } + + /// + public override bool Equals(object? obj) + { + if (obj is HsvColor hsvColor) + { + return Equals(hsvColor); + } + else + { + return false; + } + } + + /// + /// Gets a hashcode for this object. + /// Hashcode is not guaranteed to be unique. + /// + /// The hashcode for this object. + public override int GetHashCode() + { + return HashCode.Combine(A, H, S, V); + } + + /// + /// Gets the RGB color model equivalent of this HSV color. + /// + /// The RGB equivalent color. + public Color ToRgb() + { + return HsvColor.ToRgb(this); + } + + //////////////////////////////////////////////////////////////////////// + // Static Methods + //////////////////////////////////////////////////////////////////////// + + /// + /// Creates a new from individual color channel values. + /// + /// + /// This exists for symmetry with the struct; however, the + /// appropriate constructor should commonly be used instead. + /// + /// The Alpha (transparency) channel value in the range from 0..1. + /// The Hue channel value in the range from 0..360. + /// The Saturation channel value in the range from 0..1. + /// The Value channel value in the range from 0..1. + /// A new built from the individual color channel values. + public static HsvColor FromAhsv(double a, double h, double s, double v) + { + return new HsvColor(h, s, v, a); + } + + /// + /// Creates a new from individual color channel values. + /// + /// + /// This exists for symmetry with the struct; however, the + /// appropriate constructor should commonly be used instead. + /// + /// The Hue channel value in the range from 0..360. + /// The Saturation channel value in the range from 0..1. + /// The Value channel value in the range from 0..1. + /// A new built from the individual color channel values. + public static HsvColor FromHsv(double h, double s, double v) + { + return new HsvColor(h, s, v, 1.0); + } + + /// + /// Converts the given HSV color to it's RGB color equivalent. + /// + /// The color in the HSV color model. + /// A new RGB equivalent to the given HSVA values. + public static Color ToRgb(HsvColor hsvColor) + { + return HsvColor.ToRgb(hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A); + } + + /// + /// Converts the given HSVA color channel values to it's RGB color equivalent. + /// + /// The hue channel value in the HSV color model in the range from 0..360. + /// The saturation channel value in the HSV color model in the range from 0..1. + /// The value channel value in the HSV color model in the range from 0..1. + /// The alpha channel value in the range from 0..1. + /// A new RGB equivalent to the given HSVA values. + public static Color ToRgb( + double hue, + double saturation, + double value, + double alpha = 1.0) + { + // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) + // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp + // This was used because it is the best documented and likely most optimized for performance + // Alpha channel support was added + + // We want the hue to be between 0 and 359, + // so we first ensure that that's the case. + while (hue >= 360.0) + { + hue -= 360.0; + } + + while (hue < 0.0) + { + hue += 360.0; + } + + // We similarly clamp saturation and value between 0 and 1. + saturation = saturation < 0.0 ? 0.0 : saturation; + saturation = saturation > 1.0 ? 1.0 : saturation; + + value = value < 0.0 ? 0.0 : value; + value = value > 1.0 ? 1.0 : value; + + // The first thing that we need to do is to determine the chroma (see above for its definition). + // Remember from above that: + // + // 1. The chroma is the difference between the maximum and the minimum of the RGB channels, + // 2. The value is the maximum of the RGB channels, and + // 3. The saturation comes from dividing the chroma by the maximum of the RGB channels (i.e., the value). + // + // From these facts, you can see that we can retrieve the chroma by simply multiplying the saturation and the value, + // and we can retrieve the minimum of the RGB channels by subtracting the chroma from the value. + double chroma = saturation * value; + double min = value - chroma; + + // If the chroma is zero, then we have a greyscale color. In that case, the maximum and the minimum RGB channels + // have the same value (and, indeed, all of the RGB channels are the same), so we can just immediately return + // the minimum value as the value of all the channels. + if (chroma == 0) + { + return Color.FromArgb( + (byte)Math.Round(alpha * 255), + (byte)Math.Round(min * 255), + (byte)Math.Round(min * 255), + (byte)Math.Round(min * 255)); + } + + // If the chroma is not zero, then we need to continue. The first step is to figure out + // what section of the color wheel we're located in. In order to do that, we'll divide the hue by 60. + // The resulting value means we're in one of the following locations: + // + // 0 - Between red and yellow. + // 1 - Between yellow and green. + // 2 - Between green and cyan. + // 3 - Between cyan and blue. + // 4 - Between blue and purple. + // 5 - Between purple and red. + // + // In each of these sextants, one of the RGB channels is completely present, one is partially present, and one is not present. + // For example, as we transition between red and yellow, red is completely present, green is becoming increasingly present, and blue is not present. + // Then, as we transition from yellow and green, green is now completely present, red is becoming decreasingly present, and blue is still not present. + // As we transition from green to cyan, green is still completely present, blue is becoming increasingly present, and red is no longer present. And so on. + // + // To convert from hue to RGB value, we first need to figure out which of the three channels is in which configuration + // in the sextant that we're located in. Next, we figure out what value the completely-present color should have. + // We know that chroma = (max - min), and we know that this color is the max color, so to find its value we simply add + // min to chroma to retrieve max. Finally, we consider how far we've transitioned from the pure form of that color + // to the next color (e.g., how far we are from pure red towards yellow), and give a value to the partially present channel + // equal to the minimum plus the chroma (i.e., the max minus the min), multiplied by the percentage towards the new color. + // This gets us a value between the maximum and the minimum representing the partially present channel. + // Finally, the not-present color must be equal to the minimum value, since it is the one least participating in the overall color. + int sextant = (int)(hue / 60); + double intermediateColorPercentage = (hue / 60) - sextant; + double max = chroma + min; + + double r = 0; + double g = 0; + double b = 0; + + switch (sextant) + { + case 0: + r = max; + g = min + (chroma * intermediateColorPercentage); + b = min; + break; + case 1: + r = min + (chroma * (1 - intermediateColorPercentage)); + g = max; + b = min; + break; + case 2: + r = min; + g = max; + b = min + (chroma * intermediateColorPercentage); + break; + case 3: + r = min; + g = min + (chroma * (1 - intermediateColorPercentage)); + b = max; + break; + case 4: + r = min + (chroma * intermediateColorPercentage); + g = min; + b = max; + break; + case 5: + r = max; + g = min; + b = min + (chroma * (1 - intermediateColorPercentage)); + break; + } + + return Color.FromArgb( + (byte)Math.Round(alpha * 255), + (byte)Math.Round(r * 255), + (byte)Math.Round(g * 255), + (byte)Math.Round(b * 255)); + } + + /// + /// Converts the given RGB color to it's HSV color equivalent. + /// + /// The color in the RGB color model. + /// A new equivalent to the given RGBA values. + public static HsvColor FromRgb(Color color) + { + return HsvColor.FromRgb(color.R, color.G, color.B, color.A); + } + + /// + /// Converts the given RGBA color channel values to it's HSV color equivalent. + /// + /// The red channel value in the RGB color model. + /// The green channel value in the RGB color model. + /// The blue channel value in the RGB color model. + /// The alpha channel value. + /// A new equivalent to the given RGBA values. + public static HsvColor FromRgb( + byte red, + byte green, + byte blue, + byte alpha = 0xFF) + { + // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) + // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp + // This was used because it is the best documented and likely most optimized for performance + // Alpha channel support was added + + // Normalize RGBA channel values into the 0..1 range used by this algorithm + double r = red / 255.0; + double g = green / 255.0; + double b = blue / 255.0; + double a = alpha / 255.0; + + double hue; + double saturation; + double value; + + double max = r >= g ? (r >= b ? r : b) : (g >= b ? g : b); + double min = r <= g ? (r <= b ? r : b) : (g <= b ? g : b); + + // The value, a number between 0 and 1, is the largest of R, G, and B (divided by 255). + // Conceptually speaking, it represents how much color is present. + // If at least one of R, G, B is 255, then there exists as much color as there can be. + // If RGB = (0, 0, 0), then there exists no color at all - a value of zero corresponds + // to black (i.e., the absence of any color). + value = max; + + // The "chroma" of the color is a value directly proportional to the extent to which + // the color diverges from greyscale. If, for example, we have RGB = (255, 255, 0), + // then the chroma is maximized - this is a pure yellow, no gray of any kind. + // On the other hand, if we have RGB = (128, 128, 128), then the chroma being zero + // implies that this color is pure greyscale, with no actual hue to be found. + double chroma = max - min; + + // If the chrome is zero, then hue is technically undefined - a greyscale color + // has no hue. For the sake of convenience, we'll just set hue to zero, since + // it will be unused in this circumstance. Since the color is purely gray, + // saturation is also equal to zero - you can think of saturation as basically + // a measure of hue intensity, such that no hue at all corresponds to a + // nonexistent intensity. + if (chroma == 0) + { + hue = 0.0; + saturation = 0.0; + } + else + { + // In this block, hue is properly defined, so we'll extract both hue + // and saturation information from the RGB color. + + // Hue can be thought of as a cyclical thing, between 0 degrees and 360 degrees. + // A hue of 0 degrees is red; 120 degrees is green; 240 degrees is blue; and 360 is back to red. + // Every other hue is somewhere between either red and green, green and blue, and blue and red, + // so every other hue can be thought of as an angle on this color wheel. + // These if/else statements determines where on this color wheel our color lies. + if (r == max) + { + // If the red channel is the most pronounced channel, then we exist + // somewhere between (-60, 60) on the color wheel - i.e., the section around 0 degrees + // where red dominates. We figure out where in that section we are exactly + // by considering whether the green or the blue channel is greater - by subtracting green from blue, + // then if green is greater, we'll nudge ourselves closer to 60, whereas if blue is greater, then + // we'll nudge ourselves closer to -60. We then divide by chroma (which will actually make the result larger, + // since chroma is a value between 0 and 1) to normalize the value to ensure that we get the right hue + // even if we're very close to greyscale. + hue = 60 * (g - b) / chroma; + } + else if (g == max) + { + // We do the exact same for the case where the green channel is the most pronounced channel, + // only this time we want to see if we should tilt towards the blue direction or the red direction. + // We add 120 to center our value in the green third of the color wheel. + hue = 120 + (60 * (b - r) / chroma); + } + else // blue == max + { + // And we also do the exact same for the case where the blue channel is the most pronounced channel, + // only this time we want to see if we should tilt towards the red direction or the green direction. + // We add 240 to center our value in the blue third of the color wheel. + hue = 240 + (60 * (r - g) / chroma); + } + + // Since we want to work within the range [0, 360), we'll add 360 to any value less than zero - + // this will bump red values from within -60 to -1 to 300 to 359. The hue is the same at both values. + if (hue < 0.0) + { + hue += 360.0; + } + + // The saturation, our final HSV axis, can be thought of as a value between 0 and 1 indicating how intense our color is. + // To find it, we divide the chroma - the distance between the minimum and the maximum RGB channels - by the maximum channel (i.e., the value). + // This effectively normalizes the chroma - if the maximum is 0.5 and the minimum is 0, the saturation will be (0.5 - 0) / 0.5 = 1, + // meaning that although this color is not as bright as it can be, the dark color is as intense as it possibly could be. + // If, on the other hand, the maximum is 0.5 and the minimum is 0.25, then the saturation will be (0.5 - 0.25) / 0.5 = 0.5, + // meaning that this color is partially washed out. + // A saturation value of 0 corresponds to a greyscale color, one in which the color is *completely* washed out and there is no actual hue. + saturation = chroma / value; + } + + return new HsvColor(hue, saturation, value, a); + } + + //////////////////////////////////////////////////////////////////////// + // Operators + //////////////////////////////////////////////////////////////////////// + + /// + /// Indicates whether the values of two specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left and right are equal; otherwise, false. + public static bool operator ==(HsvColor left, HsvColor right) + { + return left.Equals(right); + } + + /// + /// Indicates whether the values of two specified objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left and right are not equal; otherwise, false. + public static bool operator !=(HsvColor left, HsvColor right) + { + return !(left == right); + } + + /// + /// Explicit conversion from an to a . + /// + /// The to convert. + public static explicit operator Color(HsvColor hsvColor) + { + return hsvColor.ToRgb(); + } + } +} From 6dd502b0bd2c65e8e6b9e298ec881f79a0225931 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 22:17:14 -0500 Subject: [PATCH 16/77] Add Empty support in Color --- src/Avalonia.Visuals/Media/Color.cs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 083c15cd13..074d98122a 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -14,6 +14,11 @@ namespace Avalonia.Media #endif readonly struct Color : IEquatable { + /// + /// Represents a null with zero set for all channels. + /// + public static readonly Color Empty = new Color(); + static Color() { #if !BUILDTASK @@ -41,6 +46,18 @@ namespace Avalonia.Media /// public byte B { get; } + /// + /// Gets a value indicating whether this is uninitialized and + /// considered null. + /// + public bool IsEmpty + { + get => A == 0 && + R == 0 && + G == 0 && + B == 0; + } + public Color(byte a, byte r, byte g, byte b) { A = a; @@ -272,9 +289,7 @@ namespace Avalonia.Media return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B; } - /// - /// Check if two colors are equal. - /// + /// public bool Equals(Color other) { return A == other.A && R == other.R && G == other.G && B == other.B; From c3d7577ef7ec6954817ef7108c9c78509761c66a Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 22:54:11 -0500 Subject: [PATCH 17/77] Add additional license comments for color conversion code --- src/Avalonia.Visuals/Media/HsvColor.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 605d89346e..284dbc8d68 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -1,4 +1,9 @@ -using System; +// Color conversion portions of this source file are adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. + +using System; namespace Avalonia.Media { From 50e0547622515c197d10a953328ebf3215166f9f Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 23:35:03 -0500 Subject: [PATCH 18/77] Use var per maintainer request Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com> --- src/Avalonia.Visuals/Media/HsvColor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 284dbc8d68..3e4e694426 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -248,8 +248,8 @@ namespace Avalonia.Media // // From these facts, you can see that we can retrieve the chroma by simply multiplying the saturation and the value, // and we can retrieve the minimum of the RGB channels by subtracting the chroma from the value. - double chroma = saturation * value; - double min = value - chroma; + var chroma = saturation * value; + var min = value - chroma; // If the chroma is zero, then we have a greyscale color. In that case, the maximum and the minimum RGB channels // have the same value (and, indeed, all of the RGB channels are the same), so we can just immediately return From f221afda13b648376d386bdb954872068d36ae71 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 23:35:16 -0500 Subject: [PATCH 19/77] Use var per maintainer request Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com> --- src/Avalonia.Visuals/Media/HsvColor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 3e4e694426..fadd1b20c7 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -390,7 +390,7 @@ namespace Avalonia.Media // then the chroma is maximized - this is a pure yellow, no gray of any kind. // On the other hand, if we have RGB = (128, 128, 128), then the chroma being zero // implies that this color is pure greyscale, with no actual hue to be found. - double chroma = max - min; + var chroma = max - min; // If the chrome is zero, then hue is technically undefined - a greyscale color // has no hue. For the sake of convenience, we'll just set hue to zero, since From 6f779152d3303656c214b8b06f0281666e6c44fd Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 23:55:05 -0500 Subject: [PATCH 20/77] Use MathUtilities.Clamp() for .NET Standard 2.0 support --- src/Avalonia.Visuals/Media/HsvColor.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index fadd1b20c7..39ddcf7de6 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -4,6 +4,7 @@ // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; +using Avalonia.Utilities; namespace Avalonia.Media { @@ -33,9 +34,9 @@ namespace Avalonia.Media double value) { A = 1.0; - H = Math.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); - S = Math.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); - V = Math.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); + H = MathUtilities.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); + S = MathUtilities.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); + V = MathUtilities.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); } /// @@ -51,10 +52,10 @@ namespace Avalonia.Media double value, double alpha) { - A = Math.Clamp((double.IsNaN(alpha) ? 1 : alpha), 0, 1); // Default to no transparency - H = Math.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); - S = Math.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); - V = Math.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); + A = MathUtilities.Clamp((double.IsNaN(alpha) ? 1 : alpha), 0, 1); // Default to no transparency + H = MathUtilities.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); + S = MathUtilities.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); + V = MathUtilities.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); } /// From 53ca7f89225f4b28c1b66ce41b8eb8536f33b33c Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 23:56:03 -0500 Subject: [PATCH 21/77] Use custom hash algo for .NET Standard 2.0 support --- src/Avalonia.Visuals/Media/HsvColor.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 39ddcf7de6..c46d3bf4b8 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -143,7 +143,16 @@ namespace Avalonia.Media /// The hashcode for this object. public override int GetHashCode() { - return HashCode.Combine(A, H, S, V); + // Same algorithm as Color + // This is used instead of HashCode.Combine() due to .NET Standard 2.0 requirements + unchecked + { + int hashCode = A.GetHashCode(); + hashCode = (hashCode * 397) ^ H.GetHashCode(); + hashCode = (hashCode * 397) ^ S.GetHashCode(); + hashCode = (hashCode * 397) ^ V.GetHashCode(); + return hashCode; + } } /// From fd2635aae43500ca898ba6fe562192091ef68f78 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Feb 2022 23:56:21 -0500 Subject: [PATCH 22/77] Readonly struct --- src/Avalonia.Visuals/Media/HsvColor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index c46d3bf4b8..fb431ae7d2 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -11,7 +11,7 @@ namespace Avalonia.Media /// /// Defines a color using the hue/saturation/value (HSV) model. /// - public struct HsvColor : IEquatable + public readonly struct HsvColor : IEquatable { /// /// Represents a null with zero set for all channels. From 4f11cffa4a24716425e14cb3f6ef9a3c98feb570 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Feb 2022 13:04:18 -0500 Subject: [PATCH 23/77] Add Color.ToHsv() helper method --- src/Avalonia.Visuals/Media/Color.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 074d98122a..8421b952ac 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -289,6 +289,15 @@ namespace Avalonia.Media return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B; } + /// + /// Gets the HSV color model equivalent of this RGB color. + /// + /// The HSV equivalent color. + public HsvColor ToHsv() + { + return new HsvColor(this); + } + /// public bool Equals(Color other) { From dbd75d65719592a917bcaf6f3a411b37f4010fc3 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Feb 2022 13:05:31 -0500 Subject: [PATCH 24/77] Standardize comments --- src/Avalonia.Visuals/Media/Color.cs | 2 +- src/Avalonia.Visuals/Media/HsvColor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 8421b952ac..d59e2bf645 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -290,7 +290,7 @@ namespace Avalonia.Media } /// - /// Gets the HSV color model equivalent of this RGB color. + /// Returns the HSV color model equivalent of this RGB color. /// /// The HSV equivalent color. public HsvColor ToHsv() diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index fb431ae7d2..8d1a6e6d59 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -156,7 +156,7 @@ namespace Avalonia.Media } /// - /// Gets the RGB color model equivalent of this HSV color. + /// Returns the RGB color model equivalent of this HSV color. /// /// The RGB equivalent color. public Color ToRgb() From 2102e83a835cbf2c49523ea5fba2b5b60c35315a Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Feb 2022 16:17:57 -0500 Subject: [PATCH 25/77] Switch public/internal conditionally to match Color --- src/Avalonia.Visuals/Media/HsvColor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 8d1a6e6d59..36f8483230 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -11,7 +11,10 @@ namespace Avalonia.Media /// /// Defines a color using the hue/saturation/value (HSV) model. /// - public readonly struct HsvColor : IEquatable +#if !BUILDTASK + public +#endif + readonly struct HsvColor : IEquatable { /// /// Represents a null with zero set for all channels. From 2ba39784f1983bc5dd9d1144442fe2705c3d2876 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Feb 2022 16:37:29 -0500 Subject: [PATCH 26/77] Add HsvColor to Avalonia.Build.Tasks.csproj --- src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index e864ea2007..5a7daa6d12 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -83,6 +83,9 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + Markup/%(RecursiveDir)%(FileName)%(Extension) From 0f0b8aaf865bd6d74adc39a7c087a4fe879ace35 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Feb 2022 20:03:17 -0500 Subject: [PATCH 27/77] Add missing alpha clamping caught in review --- src/Avalonia.Visuals/Media/HsvColor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 36f8483230..708c77ec41 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -245,13 +245,16 @@ namespace Avalonia.Media hue += 360.0; } - // We similarly clamp saturation and value between 0 and 1. + // We similarly clamp saturation, value and alpha between 0 and 1. saturation = saturation < 0.0 ? 0.0 : saturation; saturation = saturation > 1.0 ? 1.0 : saturation; value = value < 0.0 ? 0.0 : value; value = value > 1.0 ? 1.0 : value; + alpha = alpha < 0.0 ? 0.0 : alpha; + alpha = alpha > 1.0 ? 1.0 : alpha; + // The first thing that we need to do is to determine the chroma (see above for its definition). // Remember from above that: // From 086d0c2b4cec7bedf85537331fac02ff6b45ddfd Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 1 Mar 2022 15:24:39 -0500 Subject: [PATCH 28/77] Optimizations --- src/Avalonia.Visuals/Media/Color.cs | 4 +++- src/Avalonia.Visuals/Media/HsvColor.cs | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index d59e2bf645..16bcf127d5 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -295,7 +295,9 @@ namespace Avalonia.Media /// The HSV equivalent color. public HsvColor ToHsv() { - return new HsvColor(this); + // Use the by-channel conversion method directly for performance + // Don't use the HsvColor(Color) constructor to avoid an extra HsvColor + return HsvColor.FromRgb(R, G, B, A); } /// diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 708c77ec41..8e93f950fc 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -164,7 +164,8 @@ namespace Avalonia.Media /// The RGB equivalent color. public Color ToRgb() { - return HsvColor.ToRgb(this); + // Use the by-channel conversion method directly for performance + return HsvColor.ToRgb(H, S, V, A); } //////////////////////////////////////////////////////////////////////// From c82a01090de4008ef3436a9ad564fd9d4816e073 Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Tue, 1 Mar 2022 21:57:51 -0500 Subject: [PATCH 29/77] Update Viewbox to use internal visual and no layout rounding --- src/Avalonia.Controls/ApiCompatBaseline.txt | 3 +- src/Avalonia.Controls/Viewbox.cs | 72 ++++++++++++++++----- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 55372a7f1a..d4db1573ad 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -30,6 +30,7 @@ MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownV MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. +CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Window' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.WindowBase' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. @@ -68,4 +69,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 69 +Total Issues: 70 diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 624c61bb82..79e9684c67 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -1,13 +1,15 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Viewbox is used to scale single child to fit in the available space. /// - /// - public class Viewbox : Decorator + public class Viewbox : Control { + private Decorator _containerVisual; + /// /// Defines the property. /// @@ -20,12 +22,27 @@ namespace Avalonia.Controls public static readonly StyledProperty StretchDirectionProperty = AvaloniaProperty.Register(nameof(StretchDirection), StretchDirection.Both); + /// + /// Defines the property + /// + public static readonly StyledProperty ChildProperty = + Decorator.ChildProperty.AddOwner(); + static Viewbox() { ClipToBoundsProperty.OverrideDefaultValue(true); + UseLayoutRoundingProperty.OverrideDefaultValue(true); AffectsMeasure(StretchProperty, StretchDirectionProperty); } + public Viewbox() + { + _containerVisual = new Decorator(); + _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft; + LogicalChildren.Add(_containerVisual); + VisualChildren.Add(_containerVisual); + } + /// /// Gets or sets the stretch mode, /// which determines how child fits into the available space. @@ -45,9 +62,40 @@ namespace Avalonia.Controls set => SetValue(StretchDirectionProperty, value); } + /// + /// Gets or sets the child of the Viewbox + /// + [Content] + public IControl? Child + { + get => GetValue(ChildProperty); + set => SetValue(ChildProperty, value); + } + + /// + /// Gets or sets the transform applied to the container visual that + /// hosts the child of the Viewbox + /// + protected ITransform? InternalTransform + { + get => _containerVisual.RenderTransform; + set => _containerVisual.RenderTransform = value; + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ChildProperty) + { + _containerVisual.Child = change.NewValue.GetValueOrDefault(); + InvalidateMeasure(); + } + } + protected override Size MeasureOverride(Size availableSize) { - var child = Child; + var child = _containerVisual; if (child != null) { @@ -57,7 +105,7 @@ namespace Avalonia.Controls var size = Stretch.CalculateSize(availableSize, childSize, StretchDirection); - return size.Constrain(availableSize); + return size; } return new Size(); @@ -65,31 +113,21 @@ namespace Avalonia.Controls protected override Size ArrangeOverride(Size finalSize) { - var child = Child; + var child = _containerVisual; if (child != null) { var childSize = child.DesiredSize; var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection); - // TODO: Viewbox should have another decorator as a child so we won't affect other render transforms. - var scaleTransform = child.RenderTransform as ScaleTransform; - - if (scaleTransform == null) - { - child.RenderTransform = scaleTransform = new ScaleTransform(scale.X, scale.Y); - child.RenderTransformOrigin = RelativePoint.TopLeft; - } - - scaleTransform.ScaleX = scale.X; - scaleTransform.ScaleY = scale.Y; + InternalTransform = new ScaleTransform(scale.X, scale.Y); child.Arrange(new Rect(childSize)); return childSize * scale; } - return new Size(); + return finalSize; } } } From 759227799bb2e6f38dc498ad687282bb0f4d15fd Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:51:17 -0500 Subject: [PATCH 30/77] Fix Unit Tests --- src/Avalonia.Controls/Viewbox.cs | 2 +- .../Avalonia.Controls.UnitTests/ViewboxTests.cs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 79e9684c67..50b9560cac 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls /// Gets or sets the transform applied to the container visual that /// hosts the child of the Viewbox /// - protected ITransform? InternalTransform + protected internal ITransform? InternalTransform { get => _containerVisual.RenderTransform; set => _containerVisual.RenderTransform = value; diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index 7eaec35506..d33e55341b 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls.UnitTests target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); Assert.Equal(new Size(200, 100), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(2.0, scaleTransform.ScaleX); @@ -36,7 +36,7 @@ namespace Avalonia.Controls.UnitTests target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); Assert.Equal(new Size(100, 50), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(1.0, scaleTransform.ScaleX); @@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); Assert.Equal(new Size(200, 200), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(2.0, scaleTransform.ScaleX); @@ -72,7 +72,7 @@ namespace Avalonia.Controls.UnitTests target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); Assert.Equal(new Size(200, 200), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(4.0, scaleTransform.ScaleX); @@ -90,7 +90,7 @@ namespace Avalonia.Controls.UnitTests target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); Assert.Equal(new Size(400, 200), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(4.0, scaleTransform.ScaleX); @@ -108,7 +108,7 @@ namespace Avalonia.Controls.UnitTests target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); Assert.Equal(new Size(200, 100), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(2.0, scaleTransform.ScaleX); @@ -136,7 +136,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(expectedScale, scaleTransform.ScaleX); @@ -164,7 +164,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); - var scaleTransform = target.Child.RenderTransform as ScaleTransform; + var scaleTransform = target.InternalTransform as ScaleTransform; Assert.NotNull(scaleTransform); Assert.Equal(expectedScale, scaleTransform.ScaleX); From 836483d53c1b409d2154f765222022707a763a44 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 11:18:07 -0500 Subject: [PATCH 31/77] Optimization from review --- src/Avalonia.Visuals/Media/HsvColor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 8e93f950fc..7c06a08369 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -118,10 +118,10 @@ namespace Avalonia.Media /// public bool Equals(HsvColor other) { - if (object.Equals(other.A, A) == false) { return false; } - if (object.Equals(other.H, H) == false) { return false; } - if (object.Equals(other.S, S) == false) { return false; } - if (object.Equals(other.V, V) == false) { return false; } + if (other.A != A) { return false; } + if (other.H != H) { return false; } + if (other.S != S) { return false; } + if (other.V != V) { return false; } return true; } From 8cab8af039b46c387bfb59a5b592450bda379fe9 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 16:19:11 -0500 Subject: [PATCH 32/77] Remove Empty field and IsEmpty property from Color/HsvColor --- src/Avalonia.Visuals/Media/Color.cs | 17 ----------------- src/Avalonia.Visuals/Media/HsvColor.cs | 17 ----------------- 2 files changed, 34 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 16bcf127d5..e974bbb100 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -14,11 +14,6 @@ namespace Avalonia.Media #endif readonly struct Color : IEquatable { - /// - /// Represents a null with zero set for all channels. - /// - public static readonly Color Empty = new Color(); - static Color() { #if !BUILDTASK @@ -46,18 +41,6 @@ namespace Avalonia.Media /// public byte B { get; } - /// - /// Gets a value indicating whether this is uninitialized and - /// considered null. - /// - public bool IsEmpty - { - get => A == 0 && - R == 0 && - G == 0 && - B == 0; - } - public Color(byte a, byte r, byte g, byte b) { A = a; diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 7c06a08369..dc92639ee9 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -16,11 +16,6 @@ namespace Avalonia.Media #endif readonly struct HsvColor : IEquatable { - /// - /// Represents a null with zero set for all channels. - /// - public static readonly HsvColor Empty = new HsvColor(); - //////////////////////////////////////////////////////////////////////// // Constructors //////////////////////////////////////////////////////////////////////// @@ -99,18 +94,6 @@ namespace Avalonia.Media /// public double V { get; } - /// - /// Gets a value indicating whether this is uninitialized and - /// considered null. - /// - public bool IsEmpty - { - get => A == 0 && - H == 0 && - S == 0 && - V == 0; - } - //////////////////////////////////////////////////////////////////////// // Methods //////////////////////////////////////////////////////////////////////// From 3e5acdc2c66d2f60e140f8029d1d01d37cc7d08f Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 16:21:31 -0500 Subject: [PATCH 33/77] Remove section headings --- src/Avalonia.Visuals/Media/HsvColor.cs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index dc92639ee9..1e0af9f7dd 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -16,10 +16,6 @@ namespace Avalonia.Media #endif readonly struct HsvColor : IEquatable { - //////////////////////////////////////////////////////////////////////// - // Constructors - //////////////////////////////////////////////////////////////////////// - /// /// Initializes a new instance of the struct. /// @@ -70,10 +66,6 @@ namespace Avalonia.Media V = hsv.V; } - //////////////////////////////////////////////////////////////////////// - // Properties - //////////////////////////////////////////////////////////////////////// - /// /// Gets the Alpha (transparency) channel value in the range from 0..1. /// @@ -94,10 +86,6 @@ namespace Avalonia.Media /// public double V { get; } - //////////////////////////////////////////////////////////////////////// - // Methods - //////////////////////////////////////////////////////////////////////// - /// public bool Equals(HsvColor other) { @@ -151,10 +139,6 @@ namespace Avalonia.Media return HsvColor.ToRgb(H, S, V, A); } - //////////////////////////////////////////////////////////////////////// - // Static Methods - //////////////////////////////////////////////////////////////////////// - /// /// Creates a new from individual color channel values. /// @@ -460,10 +444,6 @@ namespace Avalonia.Media return new HsvColor(hue, saturation, value, a); } - //////////////////////////////////////////////////////////////////////// - // Operators - //////////////////////////////////////////////////////////////////////// - /// /// Indicates whether the values of two specified objects are equal. /// From b67c086f71bceded485b8051f852764110f3ab4a Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 16:25:37 -0500 Subject: [PATCH 34/77] Make alpha the first parameter in the constructor matching Color --- src/Avalonia.Visuals/Media/HsvColor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 1e0af9f7dd..f99abdc78d 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -36,15 +36,15 @@ namespace Avalonia.Media /// /// Initializes a new instance of the struct. /// + /// The Alpha (transparency) channel value in the range from 0..1. /// The Hue channel value in the range from 0..360. /// The Saturation channel value in the range from 0..1. /// The Value channel value in the range from 0..1. - /// The Alpha (transparency) channel value in the range from 0..1. public HsvColor( + double alpha, double hue, double saturation, - double value, - double alpha) + double value) { A = MathUtilities.Clamp((double.IsNaN(alpha) ? 1 : alpha), 0, 1); // Default to no transparency H = MathUtilities.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); From bdac3d08521f6a1a07ad25177b707e8d767f9d62 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 16:32:44 -0500 Subject: [PATCH 35/77] Fix parameter ordering --- src/Avalonia.Visuals/Media/HsvColor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index f99abdc78d..b7b98e4227 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -153,7 +153,7 @@ namespace Avalonia.Media /// A new built from the individual color channel values. public static HsvColor FromAhsv(double a, double h, double s, double v) { - return new HsvColor(h, s, v, a); + return new HsvColor(a, h, s, v); } /// @@ -169,7 +169,7 @@ namespace Avalonia.Media /// A new built from the individual color channel values. public static HsvColor FromHsv(double h, double s, double v) { - return new HsvColor(h, s, v, 1.0); + return new HsvColor(1.0, h, s, v); } /// @@ -441,7 +441,7 @@ namespace Avalonia.Media saturation = chroma / value; } - return new HsvColor(hue, saturation, value, a); + return new HsvColor(a, hue, saturation, value); } /// From 53237d2ef2f69c57bc4f32943066412c218c4e06 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 16:39:03 -0500 Subject: [PATCH 36/77] Remove IsNaN checks from constructor In XAML NaN is used in some cases so I wanted to cover this case. However, it isn't worth the performance cost in a constructor like this. NaN, Infinity, etc. will cause exceptions. --- src/Avalonia.Visuals/Media/HsvColor.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index b7b98e4227..0a2eb3a45c 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -27,10 +27,10 @@ namespace Avalonia.Media double saturation, double value) { - A = 1.0; - H = MathUtilities.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); - S = MathUtilities.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); - V = MathUtilities.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); + A = 1.0; // Default to no transparency + H = MathUtilities.Clamp(hue, 0.0, 360.0); + S = MathUtilities.Clamp(saturation, 0.0, 1.0); + V = MathUtilities.Clamp(value, 0.0, 1.0); } /// @@ -46,10 +46,10 @@ namespace Avalonia.Media double saturation, double value) { - A = MathUtilities.Clamp((double.IsNaN(alpha) ? 1 : alpha), 0, 1); // Default to no transparency - H = MathUtilities.Clamp((double.IsNaN(hue) ? 0 : hue), 0, 360); - S = MathUtilities.Clamp((double.IsNaN(saturation) ? 0 : saturation), 0, 1); - V = MathUtilities.Clamp((double.IsNaN(value) ? 0 : value), 0, 1); + A = MathUtilities.Clamp(alpha, 0.0, 1.0); + H = MathUtilities.Clamp(hue, 0.0, 360.0); + S = MathUtilities.Clamp(saturation, 0.0, 1.0); + V = MathUtilities.Clamp(value, 0.0, 1.0); } /// From 2c59e1583982325ae82d37b83651c0ad5fd10aa7 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 2 Mar 2022 16:47:30 -0500 Subject: [PATCH 37/77] Add internal constructor that can skip parameter clamping --- src/Avalonia.Visuals/Media/HsvColor.cs | 37 +++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 0a2eb3a45c..6c80fbd1c1 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -52,6 +52,41 @@ namespace Avalonia.Media V = MathUtilities.Clamp(value, 0.0, 1.0); } + /// + /// Initializes a new instance of the struct. + /// + /// + /// This constructor exists only for internal use where performance is critical. + /// Whether or not the channel values are in the correct ranges must be known. + /// + /// The Alpha (transparency) channel value in the range from 0..1. + /// The Hue channel value in the range from 0..360. + /// The Saturation channel value in the range from 0..1. + /// The Value channel value in the range from 0..1. + /// Whether to clamp channel values to their required ranges. + internal HsvColor( + double alpha, + double hue, + double saturation, + double value, + bool clampValues) + { + if (clampValues) + { + A = MathUtilities.Clamp(alpha, 0.0, 1.0); + H = MathUtilities.Clamp(hue, 0.0, 360.0); + S = MathUtilities.Clamp(saturation, 0.0, 1.0); + V = MathUtilities.Clamp(value, 0.0, 1.0); + } + else + { + A = alpha; + H = hue; + S = saturation; + V = value; + } + } + /// /// Initializes a new instance of the struct. /// @@ -441,7 +476,7 @@ namespace Avalonia.Media saturation = chroma / value; } - return new HsvColor(a, hue, saturation, value); + return new HsvColor(a, hue, saturation, value, false); } /// From 58afdf7666983a544e819a25ddd143c6293396fe Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 7 Mar 2022 22:02:16 -0500 Subject: [PATCH 38/77] Remove constructor and method with only HSV channels Only variants with an alpha channel (AHSV) are supported. This is to avoid concerns about accidental parameter ordering and the API matches with Color. --- src/Avalonia.Visuals/Media/HsvColor.cs | 33 -------------------------- 1 file changed, 33 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 6c80fbd1c1..7ba4c54080 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -16,23 +16,6 @@ namespace Avalonia.Media #endif readonly struct HsvColor : IEquatable { - /// - /// Initializes a new instance of the struct. - /// - /// The Hue channel value in the range from 0..360. - /// The Saturation channel value in the range from 0..1. - /// The Value channel value in the range from 0..1. - public HsvColor( - double hue, - double saturation, - double value) - { - A = 1.0; // Default to no transparency - H = MathUtilities.Clamp(hue, 0.0, 360.0); - S = MathUtilities.Clamp(saturation, 0.0, 1.0); - V = MathUtilities.Clamp(value, 0.0, 1.0); - } - /// /// Initializes a new instance of the struct. /// @@ -191,22 +174,6 @@ namespace Avalonia.Media return new HsvColor(a, h, s, v); } - /// - /// Creates a new from individual color channel values. - /// - /// - /// This exists for symmetry with the struct; however, the - /// appropriate constructor should commonly be used instead. - /// - /// The Hue channel value in the range from 0..360. - /// The Saturation channel value in the range from 0..1. - /// The Value channel value in the range from 0..1. - /// A new built from the individual color channel values. - public static HsvColor FromHsv(double h, double s, double v) - { - return new HsvColor(1.0, h, s, v); - } - /// /// Converts the given HSV color to it's RGB color equivalent. /// From 5436786ad5c29372511fa3ad89dbd071cf5608cb Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 7 Mar 2022 22:03:24 -0500 Subject: [PATCH 39/77] Simplify --- src/Avalonia.Visuals/Media/HsvColor.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 7ba4c54080..8b2f10d088 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -107,12 +107,10 @@ namespace Avalonia.Media /// public bool Equals(HsvColor other) { - if (other.A != A) { return false; } - if (other.H != H) { return false; } - if (other.S != S) { return false; } - if (other.V != V) { return false; } - - return true; + return other.A == A && + other.H == H && + other.S == S && + other.V == V; } /// From b902067196fea78365ec147078879fb42d4f0d97 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 9 Mar 2022 09:02:02 +0000 Subject: [PATCH 40/77] fix pinvoke signature for smc and ice error handlers for 32 bit --- src/Avalonia.X11/ICELib.cs | 2 +- src/Avalonia.X11/SMLib.cs | 2 +- src/Avalonia.X11/X11PlatformLifetimeEvents.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.X11/ICELib.cs b/src/Avalonia.X11/ICELib.cs index 8ef21dd000..19c973a5e0 100644 --- a/src/Avalonia.X11/ICELib.cs +++ b/src/Avalonia.X11/ICELib.cs @@ -46,7 +46,7 @@ namespace Avalonia.X11 IntPtr iceConn, bool swap, int offendingMinorOpcode, - ulong offendingSequence, + nuint offendingSequence, int errorClass, int severity, IntPtr values diff --git a/src/Avalonia.X11/SMLib.cs b/src/Avalonia.X11/SMLib.cs index e2b39cfcff..f8f13e32f8 100644 --- a/src/Avalonia.X11/SMLib.cs +++ b/src/Avalonia.X11/SMLib.cs @@ -124,7 +124,7 @@ namespace Avalonia.X11 IntPtr smcConn, bool swap, int offendingMinorOpcode, - ulong offendingSequence, + nuint offendingSequence, int errorClass, int severity, IntPtr values diff --git a/src/Avalonia.X11/X11PlatformLifetimeEvents.cs b/src/Avalonia.X11/X11PlatformLifetimeEvents.cs index 1a17a018e8..8412bd0730 100644 --- a/src/Avalonia.X11/X11PlatformLifetimeEvents.cs +++ b/src/Avalonia.X11/X11PlatformLifetimeEvents.cs @@ -153,14 +153,14 @@ namespace Avalonia.X11 } private static void StaticErrorHandler(IntPtr smcConn, bool swap, int offendingMinorOpcode, - ulong offendingSequence, int errorClass, int severity, IntPtr values) + nuint offendingSequence, int errorClass, int severity, IntPtr values) { GetInstance(smcConn) ?.ErrorHandler(swap, offendingMinorOpcode, offendingSequence, errorClass, severity, values); } // ReSharper disable UnusedParameter.Local - private void ErrorHandler(bool swap, int offendingMinorOpcode, ulong offendingSequence, int errorClass, + private void ErrorHandler(bool swap, int offendingMinorOpcode, nuint offendingSequence, int errorClass, int severity, IntPtr values) { Logger.TryGet(LogEventLevel.Warning, LogArea.X11Platform)?.Log(this, From 27a74bb12c7ddb4c8a8d3a84fb2ba9722ff2d5b5 Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 9 Mar 2022 10:52:48 +0100 Subject: [PATCH 41/77] fix formatting --- src/Windows/Avalonia.Win32/WindowImpl.cs | 68 ++++++++++++------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index aae65a792b..8d3a891cc7 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -86,7 +86,7 @@ namespace Avalonia.Win32 private Size _minSize; private Size _maxSize; private POINT _maxTrackSize; - private WindowImpl _parent; + private WindowImpl _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; private bool _isCloseRequested; private bool _shown; @@ -174,7 +174,7 @@ namespace Avalonia.Win32 public Action PositionChanged { get; set; } public Action WindowStateChanged { get; set; } - + public Action LostFocus { get; set; } public Action TransparencyLevelChanged { get; set; } @@ -247,7 +247,7 @@ namespace Avalonia.Win32 { get { - if(_isFullScreenActive) + if (_isFullScreenActive) { return WindowState.FullScreen; } @@ -270,7 +270,7 @@ namespace Avalonia.Win32 ShowWindow(value, value != WindowState.Minimized); // If the window is minimized, it shouldn't be activated } - _showWindowState = value; + _showWindowState = value; } } @@ -278,7 +278,7 @@ namespace Avalonia.Win32 protected IntPtr Hwnd => _hwnd; - public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel) + public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { TransparencyLevel = EnableBlur(transparencyLevel); } @@ -318,12 +318,12 @@ namespace Avalonia.Win32 } var blurInfo = new DWM_BLURBEHIND(false); - + if (transparencyLevel == WindowTransparencyLevel.Blur) { blurInfo = new DWM_BLURBEHIND(true); } - + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); if (transparencyLevel == WindowTransparencyLevel.Transparent) @@ -483,12 +483,12 @@ namespace Avalonia.Win32 if (customRendererFactory != null) return customRendererFactory.Create(root, loop); - return Win32Platform.UseDeferredRendering - ? _isUsingComposition + return Win32Platform.UseDeferredRendering + ? _isUsingComposition ? new DeferredRenderer(root, loop) { RenderOnlyOnRenderThread = true - } + } : (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) : new ImmediateRenderer(root); } @@ -543,7 +543,7 @@ namespace Avalonia.Win32 { BeforeCloseCleanup(true); } - + DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } @@ -609,7 +609,7 @@ namespace Avalonia.Win32 public void SetParent(IWindowImpl parent) { _parent = (WindowImpl)parent; - + var parentHwnd = _parent?._hwnd ?? IntPtr.Zero; if (parentHwnd == IntPtr.Zero && !_windowProperties.ShowInTaskbar) @@ -723,7 +723,7 @@ namespace Avalonia.Win32 _isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0, atom, null, - (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN, + (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int)WindowStyles.WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, @@ -779,7 +779,7 @@ namespace Avalonia.Win32 } if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8) - { + { var monitor = MonitorFromWindow( _hwnd, MONITOR.MONITOR_DEFAULTTONEAREST); @@ -862,14 +862,14 @@ namespace Avalonia.Win32 } TaskBarList.MarkFullscreen(_hwnd, fullscreen); - + ExtendClientArea(); } private MARGINS UpdateExtendMargins() { RECT borderThickness = new RECT(); - RECT borderCaptionThickness = new RECT(); + RECT borderCaptionThickness = new RECT(); AdjustWindowRectEx(ref borderCaptionThickness, (uint)(GetStyle()), false, 0); AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle() & ~WindowStyles.WS_CAPTION), false, 0); @@ -892,7 +892,7 @@ namespace Avalonia.Win32 if (_extendTitleBarHint != -1) { - borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling); + borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling); } margins.cyTopHeight = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1; @@ -917,7 +917,7 @@ namespace Avalonia.Win32 { return; } - + if (DwmIsCompositionEnabled(out bool compositionEnabled) < 0 || !compositionEnabled) { _isClientAreaExtended = false; @@ -945,11 +945,11 @@ namespace Avalonia.Win32 _offScreenMargin = new Thickness(); _extendedMargins = new Thickness(); - - Resize(new Size(rcWindow.Width/ RenderScaling, rcWindow.Height / RenderScaling), PlatformResizeReason.Layout); + + Resize(new Size(rcWindow.Width / RenderScaling, rcWindow.Height / RenderScaling), PlatformResizeReason.Layout); } - if(!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && + if (!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome))) { EnableCloseButton(_hwnd); @@ -965,12 +965,12 @@ namespace Avalonia.Win32 private void ShowWindow(WindowState state, bool activate) { _shown = true; - + if (_isClientAreaExtended) { ExtendClientArea(); } - + ShowWindowCommand? command; var newWindowProperties = _windowProperties; @@ -988,7 +988,7 @@ namespace Avalonia.Win32 case WindowState.Normal: newWindowProperties.IsFullScreen = false; - command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : + command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : activate ? ShowWindowCommand.Normal : ShowWindowCommand.ShowNoActivate; break; @@ -1019,7 +1019,7 @@ namespace Avalonia.Win32 SetForegroundWindow(_hwnd); } } - + private void BeforeCloseCleanup(bool isDisposing) { // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 @@ -1037,7 +1037,7 @@ namespace Avalonia.Win32 // Our window closed callback will set enabled state to a correct value after child window gets destroyed. _parent.SetEnabled(true); } - + // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. if (wasActive) { @@ -1064,7 +1064,7 @@ namespace Avalonia.Win32 SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW); } } - } + } private WindowStyles GetWindowStateStyles() { @@ -1241,7 +1241,7 @@ namespace Avalonia.Win32 SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); } - } + } } private const int MF_BYCOMMAND = 0x0; @@ -1291,9 +1291,9 @@ namespace Avalonia.Win32 public void SetExtendClientAreaToDecorationsHint(bool hint) { _isClientAreaExtended = hint; - - ExtendClientArea(); - } + + ExtendClientArea(); + } public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) { @@ -1301,7 +1301,7 @@ namespace Avalonia.Win32 ExtendClientArea(); } - + /// public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) { @@ -1315,7 +1315,7 @@ namespace Avalonia.Win32 /// public Action ExtendClientAreaToDecorationsChanged { get; set; } - + /// public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome); @@ -1354,7 +1354,7 @@ namespace Avalonia.Win32 { private readonly WindowImpl _owner; private readonly PlatformResizeReason _restore; - + public ResizeReasonScope(WindowImpl owner, PlatformResizeReason restore) { _owner = owner; From 24a18be7c261bdf36de3c5b357d95e089272c7a2 Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 9 Mar 2022 11:22:26 +0100 Subject: [PATCH 42/77] Use HostBackdropBrush for winver >= 10.0.22000 --- .../Composition/WinUICompositorConnection.cs | 60 ++++++++++++------- src/Windows/Avalonia.Win32/WinRT/winrt.idl | 6 ++ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index 57b0f71306..76af12e8ca 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -17,11 +17,11 @@ namespace Avalonia.Win32.WinRT.Composition { class WinUICompositorConnection : IRenderTimer { + public static readonly Version MinHostBackdropVersion = new Version(10, 0, 22000); private readonly float? _backdropCornerRadius; private readonly EglContext _syncContext; private readonly ICompositionBrush _micaBrush; private ICompositor _compositor; - private ICompositor2 _compositor2; private ICompositor5 _compositor5; private ICompositorInterop _compositorInterop; private AngleWin32EglDisplay _angle; @@ -39,12 +39,11 @@ namespace Avalonia.Win32.WinRT.Composition _syncContext = _gl.PrimaryEglContext; _angle = (AngleWin32EglDisplay)_gl.Display; _compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor"); - _compositor2 = _compositor.QueryInterface(); _compositor5 = _compositor.QueryInterface(); _compositorInterop = _compositor.QueryInterface(); _compositorDesktopInterop = _compositor.QueryInterface(); using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true); - + _device = _compositorInterop.CreateGraphicsDevice(device); _blurBrush = CreateAcrylicBlurBackdropBrush(); _micaBrush = CreateMicaBackdropBrush(); @@ -71,7 +70,7 @@ namespace Avalonia.Win32.WinRT.Composition AvaloniaLocator.CurrentMutable.BindToSelf(connect); AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect); tcs.SetResult(true); - + } catch (Exception e) { @@ -99,7 +98,7 @@ namespace Avalonia.Win32.WinRT.Composition } public void Dispose() { - + } public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus) @@ -118,12 +117,12 @@ namespace Avalonia.Win32.WinRT.Composition { } } - + private void RunLoop() { { var st = Stopwatch.StartNew(); - using (var act = _compositor5.RequestCommitAsync()) + using (var act = _compositor5.RequestCommitAsync()) act.SetCompleted(new RunLoopHandler(this)); while (true) { @@ -172,12 +171,12 @@ namespace Avalonia.Win32.WinRT.Composition using var sc = _syncContext.EnsureLocked(); using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0); using var target = desktopTarget.QueryInterface(); - + using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); using var surface = drawingSurface.QueryInterface(); using var surfaceInterop = drawingSurface.QueryInterface(); - + using var surfaceBrush = _compositor.CreateSurfaceBrushWithSurface(surface); using var brush = surfaceBrush.QueryInterface(); @@ -190,7 +189,7 @@ namespace Avalonia.Win32.WinRT.Composition using var containerVisual2 = container.QueryInterface(); containerVisual2.SetRelativeSizeAdjustment(new Vector2(1, 1)); using var containerChildren = container.Children; - + target.SetRoot(containerVisual); using var blur = CreateBlurVisual(_blurBrush); @@ -202,10 +201,10 @@ namespace Avalonia.Win32.WinRT.Composition } var compositionRoundedRectangleGeometry = ClipVisual(blur, mica); - + containerChildren.InsertAtTop(blur); containerChildren.InsertAtTop(visual); - + return new WinUICompositedWindow(_syncContext, _compositor, _pumpLock, target, surfaceInterop, visual, blur, mica, compositionRoundedRectangleGeometry); } @@ -234,10 +233,8 @@ namespace Avalonia.Win32.WinRT.Composition var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource); using var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect); using var compositionEffectBrush = blurEffectFactory.CreateBrush(); - using var backdrop = _compositor2.CreateBackdropBrush(); - using var backdropBrush = backdrop.QueryInterface(); - - + using var backdropBrush = CreateBackdropBrush(); + var saturateEffect = new SaturationEffect(blurEffect); using var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); using var sat = satEffectFactory.CreateBrush(); @@ -261,8 +258,8 @@ namespace Avalonia.Win32.WinRT.Composition foreach (var visual in containerVisuals) { visual?.SetClip(geometricClipWithGeometry.QueryInterface()); - } - + } + return roundedRectangleGeometry.CloneReference(); } @@ -271,8 +268,8 @@ namespace Avalonia.Win32.WinRT.Composition using var spriteVisual = _compositor.CreateSpriteVisual(); using var visual = spriteVisual.QueryInterface(); using var visual2 = spriteVisual.QueryInterface(); - - + + spriteVisual.SetBrush(compositionBrush); visual.SetIsVisible(0); visual2.SetRelativeSizeAdjustment(new Vector2(1.0f, 1.0f)); @@ -280,6 +277,29 @@ namespace Avalonia.Win32.WinRT.Composition return visual.CloneReference(); } + private ICompositionBrush CreateBackdropBrush() + { + ICompositionBackdropBrush brush = null; + try + { + if (Win32Platform.WindowsVersion >= MinHostBackdropVersion) + { + using var compositor3 = _compositor.QueryInterface(); + brush = compositor3.CreateHostBackdropBrush(); + } + else + { + using var compositor2 = _compositor.QueryInterface(); + brush = compositor2.CreateBackdropBrush(); + } + + return brush.QueryInterface(); + } + finally + { + brush?.Dispose(); + } + } public event Action Tick; } diff --git a/src/Windows/Avalonia.Win32/WinRT/winrt.idl b/src/Windows/Avalonia.Win32/WinRT/winrt.idl index 18a9a26fca..851df9dae6 100644 --- a/src/Windows/Avalonia.Win32/WinRT/winrt.idl +++ b/src/Windows/Avalonia.Win32/WinRT/winrt.idl @@ -358,6 +358,12 @@ interface ICompositor2 : IInspectable [overload("CreateStepEasingFunction")] HRESULT CreateStepEasingFunctionWithStepCount([in] INT32 stepCount, [out] [retval] void** result); } +[uuid(C9DD8EF0-6EB1-4E3C-A658-675D9C64D4AB)] +interface ICompositor3 : IInspectable +{ + HRESULT CreateHostBackdropBrush([out][retval] ICompositionBackdropBrush** result); +} + [uuid(0D8FB190-F122-5B8D-9FDD-543B0D8EB7F3)] interface ICompositorWithBlurredWallpaperBackdropBrush : IInspectable { From 64ae7efe209b91a3d3203c1b456fe71b9d6eeda1 Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 9 Mar 2022 11:25:03 +0100 Subject: [PATCH 43/77] fix acrylic backdrop on win11 --- .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 11 +++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index f7838c9f46..9d53691597 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -765,6 +765,14 @@ namespace Avalonia.Win32.Interop DWMWA_CLOAK, DWMWA_CLOAKED, DWMWA_FREEZE_REPRESENTATION, + DWMWA_PASSIVE_UPDATE_MODE, + DWMWA_USE_HOSTBACKDROPBRUSH, + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + DWMWA_WINDOW_CORNER_PREFERENCE = 33, + DWMWA_BORDER_COLOR, + DWMWA_CAPTION_COLOR, + DWMWA_TEXT_COLOR, + DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, DWMWA_LAST }; @@ -1456,6 +1464,9 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); + [DllImport("dwmapi.dll")] + public static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, void* pvAttribute, int cbAttribute); + [DllImport("dwmapi.dll")] public static extern int DwmIsCompositionEnabled(out bool enabled); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 8d3a891cc7..3953a0995d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -379,13 +379,24 @@ namespace Avalonia.Win32 { if (_isUsingComposition) { - _blurHost?.SetBlur(transparencyLevel switch + var effect = transparencyLevel switch { WindowTransparencyLevel.Mica => BlurEffect.Mica, WindowTransparencyLevel.AcrylicBlur => BlurEffect.Acrylic, WindowTransparencyLevel.Blur => BlurEffect.Acrylic, _ => BlurEffect.None - }); + }; + + if (Win32Platform.WindowsVersion >= WinUICompositorConnection.MinHostBackdropVersion) + { + unsafe + { + int pvUseBackdropBrush = effect == BlurEffect.Acrylic ? 1 : 0; + DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int)); + } + } + + _blurHost?.SetBlur(effect); return transparencyLevel; } From dfbc8f7dff824a6623097ac96d8785bcf86fcd68 Mon Sep 17 00:00:00 2001 From: Tim U Date: Wed, 9 Mar 2022 16:06:07 +0100 Subject: [PATCH 44/77] Implement GeometryBoundsHelper --- .../SceneGraph/GeometryBoundsHelper.cs | 31 +++++++++++++++++++ .../Rendering/SceneGraph/GeometryNode.cs | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs new file mode 100644 index 0000000000..b1129e81c4 --- /dev/null +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs @@ -0,0 +1,31 @@ +using System; +using Avalonia.Media; +using Avalonia.Utilities; + +namespace Avalonia.Rendering.SceneGraph; + +internal static class GeometryBoundsHelper +{ + /// + /// Calculates the bounds of a given geometry with respect to the pens + /// + /// The calculated bounds without s + /// The pen with information about the s + /// + public static Rect CalculateBoundsWithLineCaps(this Rect originalBounds, IPen? pen) + { + if (pen is null || MathUtilities.IsZero(pen.Thickness)) return originalBounds; + + switch (pen.LineCap) + { + case PenLineCap.Flat: + return originalBounds; + case PenLineCap.Round: + return originalBounds.Inflate(pen.Thickness / 2); + case PenLineCap.Square: + return originalBounds.Inflate(pen.Thickness); + default: + throw new ArgumentOutOfRangeException(); + } + } +} diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs index 7de1035441..70748989d6 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs @@ -24,7 +24,7 @@ namespace Avalonia.Rendering.SceneGraph IPen? pen, IGeometryImpl geometry, IDictionary? childScenes = null) - : base(geometry.GetRenderBounds(pen), transform) + : base(geometry.GetRenderBounds(pen).CalculateBoundsWithLineCaps(pen), transform) { Transform = transform; Brush = brush?.ToImmutable(); From a529ae88d59e8a8882ade3a2b9e2e281dae68988 Mon Sep 17 00:00:00 2001 From: Mike Irving Date: Wed, 9 Mar 2022 18:18:09 +0000 Subject: [PATCH 45/77] Grammatical corrections to product names: XCode -> Xcode, .Net -> .NET, Javascript -> JavaScript, MacOS -> macOS, MacOs -> macOS --- .gitignore | 2 +- build/SharedVersion.props | 2 +- native/Avalonia.Native/src/OSX/window.mm | 2 +- readme.md | 2 +- .../Remote/HtmlTransport/webapp/src/index.html | 2 +- .../Interop/Typescript/types/dotnet/index.d.ts | 2 +- tests/Avalonia.IntegrationTests.Appium/readme.md | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d7c4cdefde..44fe5e4ba4 100644 --- a/.gitignore +++ b/.gitignore @@ -192,7 +192,7 @@ dirs.sln ################## -# XCode +# Xcode ################## Index/ Logs/ diff --git a/build/SharedVersion.props b/build/SharedVersion.props index dad40cccb7..3d9548ab9d 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -11,7 +11,7 @@ latest MIT Icon.png - Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOS and with experimental support for Android, iOS and WebAssembly. + Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly. avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin https://github.com/AvaloniaUI/Avalonia/releases git diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 1de82d9f39..9b703c4838 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -735,7 +735,7 @@ private: return E_INVALIDARG; // If one tries to show a child window with a minimized parent window, then the parent window will be - // restored but MacOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive + // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. if (cparent->WindowState() == Minimized) cparent->SetWindowState(Normal); diff --git a/readme.md b/readme.md index 96c7937559..7e32dbc321 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ ## 📖 About -Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM. +Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM. ![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html index f5847cdd58..cf76de0077 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html @@ -9,6 +9,6 @@
Loading...
- + diff --git a/src/Web/Avalonia.Web.Blazor/Interop/Typescript/types/dotnet/index.d.ts b/src/Web/Avalonia.Web.Blazor/Interop/Typescript/types/dotnet/index.d.ts index ff6dc4a8f8..932dfa1e1f 100644 --- a/src/Web/Avalonia.Web.Blazor/Interop/Typescript/types/dotnet/index.d.ts +++ b/src/Web/Avalonia.Web.Blazor/Interop/Typescript/types/dotnet/index.d.ts @@ -6,7 +6,7 @@ // Here be dragons! // This is community-maintained definition file intended to ease the process of developing -// high quality JavaScript interop code to be used in Blazor application from your C# .Net code. +// high quality JavaScript interop code to be used in Blazor application from your C# .NET code. // Could be removed without a notice in case official definition types ships with Blazor itself. // tslint:disable:no-unnecessary-generics diff --git a/tests/Avalonia.IntegrationTests.Appium/readme.md b/tests/Avalonia.IntegrationTests.Appium/readme.md index 2a8c3068ba..ee630a31fd 100644 --- a/tests/Avalonia.IntegrationTests.Appium/readme.md +++ b/tests/Avalonia.IntegrationTests.Appium/readme.md @@ -11,12 +11,12 @@ - Run WinAppDriver (it gets installed to the start menu) - Run the tests in this project -## MacOS +## macOS ### Prerequisites - Install Appium: https://appium.io/ -- Give [XCode helper the required permissions](https://apple.stackexchange.com/questions/334008) +- Give [Xcode helper the required permissions](https://apple.stackexchange.com/questions/334008) - `cd samples/IntegrationTestApp` then `./bundle.sh` to create an app bundle for `IntegrationTestApp` - Register the app bundle by running `open -n ./bin/Debug/net6.0/osx-arm64/publish/IntegrationTestApp.app` From ed19f5fc9a17defbf4ce56c77e70c47dc387de9e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 00:30:03 +0000 Subject: [PATCH 46/77] remove old softkey hack. --- src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs | 6 ++++ src/iOS/Avalonia.iOS/AvaloniaView.Text.cs | 32 --------------------- src/iOS/Avalonia.iOS/Platform.cs | 7 ----- src/iOS/Avalonia.iOS/SoftKeyboardHelper.cs | 24 ---------------- 4 files changed, 6 insertions(+), 63 deletions(-) create mode 100644 src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs delete mode 100644 src/iOS/Avalonia.iOS/AvaloniaView.Text.cs delete mode 100644 src/iOS/Avalonia.iOS/SoftKeyboardHelper.cs diff --git a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs new file mode 100644 index 0000000000..7cc853312c --- /dev/null +++ b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs @@ -0,0 +1,6 @@ +namespace Avalonia.iOS; + +public class AvaloniaUIResponder +{ + +} diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs deleted file mode 100644 index dc963726b0..0000000000 --- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Avalonia.Input; -using Avalonia.Input.Raw; -using Foundation; -using ObjCRuntime; -using UIKit; - -namespace Avalonia.iOS -{ - [Adopts("UIKeyInput")] - public partial class AvaloniaView - { - public override bool CanBecomeFirstResponder => true; - - [Export("hasText")] public bool HasText => false; - - [Export("insertText:")] - public void InsertText(string text) => - _topLevelImpl.Input?.Invoke(new RawTextInputEventArgs(KeyboardDevice.Instance, - 0, InputRoot, text)); - - [Export("deleteBackward")] - public void DeleteBackward() - { - // TODO: pass this through IME infrastructure instead of emulating a backspace press - _topLevelImpl.Input?.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance, - 0, InputRoot, RawKeyEventType.KeyDown, Key.Back, RawInputModifiers.None)); - - _topLevelImpl.Input?.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance, - 0, InputRoot, RawKeyEventType.KeyUp, Key.Back, RawInputModifiers.None)); - } - } -} \ No newline at end of file diff --git a/src/iOS/Avalonia.iOS/Platform.cs b/src/iOS/Avalonia.iOS/Platform.cs index f8815e9030..2738e502de 100644 --- a/src/iOS/Avalonia.iOS/Platform.cs +++ b/src/iOS/Avalonia.iOS/Platform.cs @@ -44,7 +44,6 @@ namespace Avalonia.iOS GlFeature ??= new EaglFeature(); Timer ??= new DisplayLinkTimer(); var keyboard = new KeyboardDevice(); - var softKeyboard = new SoftKeyboardHelper(); AvaloniaLocator.CurrentMutable .Bind().ToConstant(GlFeature) @@ -58,12 +57,6 @@ namespace Avalonia.iOS .Bind().ToConstant(Timer) .Bind().ToConstant(new PlatformThreadingInterface()) .Bind().ToConstant(keyboard); - - keyboard.PropertyChanged += (_, changed) => - { - if (changed.PropertyName == nameof(KeyboardDevice.FocusedElement)) - softKeyboard.UpdateKeyboard(keyboard.FocusedElement); - }; } diff --git a/src/iOS/Avalonia.iOS/SoftKeyboardHelper.cs b/src/iOS/Avalonia.iOS/SoftKeyboardHelper.cs deleted file mode 100644 index b05ab280d2..0000000000 --- a/src/iOS/Avalonia.iOS/SoftKeyboardHelper.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Input; - -namespace Avalonia.iOS -{ - public class SoftKeyboardHelper - { - private AvaloniaView _oldView; - - public void UpdateKeyboard(IInputElement focusedElement) - { - if (_oldView?.IsFirstResponder == true) - _oldView?.ResignFirstResponder(); - _oldView = null; - - //TODO: Raise a routed event to determine if any control wants to become the text input handler - if (focusedElement is TextBox) - { - var view = ((focusedElement.VisualRoot as TopLevel)?.PlatformImpl as AvaloniaView.TopLevelImpl)?.View; - view?.BecomeFirstResponder(); - } - } - } -} \ No newline at end of file From d717d10de802da0df60f715c381d0786de67d5f3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 00:31:21 +0000 Subject: [PATCH 47/77] use ime api to hide and show osk. --- src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs | 54 ++++++++++++++++++++- src/iOS/Avalonia.iOS/AvaloniaView.cs | 7 ++- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs index 7cc853312c..a687eb9bf8 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs @@ -1,6 +1,56 @@ +using Foundation; +using ObjCRuntime; +using UIKit; +using Avalonia.Input.TextInput; + namespace Avalonia.iOS; -public class AvaloniaUIResponder +[Adopts("UIKeyInput")] +public partial class AvaloniaView : ITextInputMethodImpl { - + public override bool CanResignFirstResponder => true; + public override bool CanBecomeFirstResponder => true; + public override bool CanBecomeFocused => true; + + [Export("hasText")] public bool HasText => false; + + [Export("insertText:")] + public void InsertText(string text) + { + + } + + [Export("deleteBackward")] + public void DeleteBackward() + { + } + + void ITextInputMethodImpl.SetActive(bool active) + { + if (active) + { + var isFr = IsFirstResponder; + var next = NextResponder; + var result = BecomeFirstResponder(); + } + else + { + ResignFirstResponder(); + } + } + + void ITextInputMethodImpl.SetCursorRect(Rect rect) + { + + } + + void ITextInputMethodImpl.SetOptions(TextInputOptionsQueryEventArgs options) + { + + } + + void ITextInputMethodImpl.Reset() + { + + } } diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index 5bb2f64879..e8108dd3de 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -2,12 +2,13 @@ using System; using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Controls.Embedding; +using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Raw; +using Avalonia.Input.TextInput; using Avalonia.Platform; using Avalonia.Rendering; using CoreAnimation; -using CoreGraphics; using Foundation; using ObjCRuntime; using OpenGLES; @@ -42,7 +43,7 @@ namespace Avalonia.iOS MultipleTouchEnabled = true; } - internal class TopLevelImpl : ITopLevelImpl + internal class TopLevelImpl : ITopLevelImplWithTextInputMethod { private readonly AvaloniaView _view; public AvaloniaView View => _view; @@ -109,6 +110,8 @@ namespace Avalonia.iOS public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(); + + public ITextInputMethodImpl? TextInputMethod => _view; } [Export("layerClass")] From 1c8295022f5c7aa913ee3a5b56aae3af30010488 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 11:59:35 +0000 Subject: [PATCH 48/77] working basic text input ios --- src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs index a687eb9bf8..cd298efd30 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs @@ -2,6 +2,8 @@ using Foundation; using ObjCRuntime; using UIKit; using Avalonia.Input.TextInput; +using Avalonia.Input; +using Avalonia.Input.Raw; namespace Avalonia.iOS; @@ -17,12 +19,26 @@ public partial class AvaloniaView : ITextInputMethodImpl [Export("insertText:")] public void InsertText(string text) { - + if (KeyboardDevice.Instance is { }) + { + _topLevelImpl.Input?.Invoke(new RawTextInputEventArgs(KeyboardDevice.Instance, + 0, InputRoot, text)); + } + } [Export("deleteBackward")] public void DeleteBackward() { + if (KeyboardDevice.Instance is { }) + { + // TODO: pass this through IME infrastructure instead of emulating a backspace press + _topLevelImpl.Input?.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance, + 0, InputRoot, RawKeyEventType.KeyDown, Key.Back, RawInputModifiers.None)); + + _topLevelImpl.Input?.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance, + 0, InputRoot, RawKeyEventType.KeyUp, Key.Back, RawInputModifiers.None)); + } } void ITextInputMethodImpl.SetActive(bool active) From 557eb84d732c1d13722831cf97864b07c8af019b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 12:08:36 +0000 Subject: [PATCH 49/77] update plist --- samples/ControlCatalog.iOS/Info.plist | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/ControlCatalog.iOS/Info.plist b/samples/ControlCatalog.iOS/Info.plist index d4b91b381e..6ffe3ba662 100644 --- a/samples/ControlCatalog.iOS/Info.plist +++ b/samples/ControlCatalog.iOS/Info.plist @@ -5,7 +5,7 @@ CFBundleDisplayName ControlCatalog.iOS CFBundleIdentifier - com.companyname.ControlCatalog.iOS + Avalonia.ControlCatalog CFBundleShortVersionString 1.0 CFBundleVersion @@ -39,9 +39,9 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIStatusBarHidden - - UIViewControllerBasedStatusBarAppearance - + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + From c7c0e7fd516e8bc3008476f2db0b771cdb204415 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 14:20:50 +0000 Subject: [PATCH 50/77] remove legacy ios sample project. --- Avalonia.sln | 27 ---- .../ControlCatalog.iOS.Legacy/AppDelegate.cs | 15 --- .../AppIcon.appiconset/Contents.json | 117 ------------------ .../ControlCatalog.iOS.Legacy.csproj | 99 --------------- .../Entitlements.plist | 6 - samples/ControlCatalog.iOS.Legacy/Info.plist | 47 ------- samples/ControlCatalog.iOS.Legacy/Main.cs | 15 --- .../Resources/LaunchScreen.xib | 43 ------- 8 files changed, 369 deletions(-) delete mode 100644 samples/ControlCatalog.iOS.Legacy/AppDelegate.cs delete mode 100644 samples/ControlCatalog.iOS.Legacy/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 samples/ControlCatalog.iOS.Legacy/ControlCatalog.iOS.Legacy.csproj delete mode 100644 samples/ControlCatalog.iOS.Legacy/Entitlements.plist delete mode 100644 samples/ControlCatalog.iOS.Legacy/Info.plist delete mode 100644 samples/ControlCatalog.iOS.Legacy/Main.cs delete mode 100644 samples/ControlCatalog.iOS.Legacy/Resources/LaunchScreen.xib diff --git a/Avalonia.sln b/Avalonia.sln index c4f64be109..a989fb828d 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -234,8 +234,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS.Legacy", "samples\ControlCatalog.iOS.Legacy\ControlCatalog.iOS.Legacy.csproj", "{3AF75F00-B497-4517-9491-922173DE216E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -2214,30 +2212,6 @@ Global {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.Build.0 = Release|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Debug|iPhone.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Release|Any CPU.Build.0 = Release|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Release|iPhone.ActiveCfg = Release|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Release|iPhone.Build.0 = Release|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3AF75F00-B497-4517-9491-922173DE216E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2302,7 +2276,6 @@ Global {26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098} {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {3AF75F00-B497-4517-9491-922173DE216E} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/ControlCatalog.iOS.Legacy/AppDelegate.cs b/samples/ControlCatalog.iOS.Legacy/AppDelegate.cs deleted file mode 100644 index a67de98259..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/AppDelegate.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Avalonia.iOS; -using Foundation; -using UIKit; - -namespace ControlCatalog.iOS.Legacy -{ - // The UIApplicationDelegate for the application. This class is responsible for launching the - // User Interface of the application, as well as listening (and optionally responding) to - // application events from iOS. - [Register("AppDelegate")] - public partial class AppDelegate : AvaloniaAppDelegate - { - - } -} diff --git a/samples/ControlCatalog.iOS.Legacy/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/ControlCatalog.iOS.Legacy/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 98f4d035c8..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "images": [ - { - "scale": "2x", - "size": "20x20", - "idiom": "iphone", - "filename": "Icon40.png" - }, - { - "scale": "3x", - "size": "20x20", - "idiom": "iphone", - "filename": "Icon60.png" - }, - { - "scale": "2x", - "size": "29x29", - "idiom": "iphone", - "filename": "Icon58.png" - }, - { - "scale": "3x", - "size": "29x29", - "idiom": "iphone", - "filename": "Icon87.png" - }, - { - "scale": "2x", - "size": "40x40", - "idiom": "iphone", - "filename": "Icon80.png" - }, - { - "scale": "3x", - "size": "40x40", - "idiom": "iphone", - "filename": "Icon120.png" - }, - { - "scale": "2x", - "size": "60x60", - "idiom": "iphone", - "filename": "Icon120.png" - }, - { - "scale": "3x", - "size": "60x60", - "idiom": "iphone", - "filename": "Icon180.png" - }, - { - "scale": "1x", - "size": "20x20", - "idiom": "ipad", - "filename": "Icon20.png" - }, - { - "scale": "2x", - "size": "20x20", - "idiom": "ipad", - "filename": "Icon40.png" - }, - { - "scale": "1x", - "size": "29x29", - "idiom": "ipad", - "filename": "Icon29.png" - }, - { - "scale": "2x", - "size": "29x29", - "idiom": "ipad", - "filename": "Icon58.png" - }, - { - "scale": "1x", - "size": "40x40", - "idiom": "ipad", - "filename": "Icon40.png" - }, - { - "scale": "2x", - "size": "40x40", - "idiom": "ipad", - "filename": "Icon80.png" - }, - { - "scale": "1x", - "size": "76x76", - "idiom": "ipad", - "filename": "Icon76.png" - }, - { - "scale": "2x", - "size": "76x76", - "idiom": "ipad", - "filename": "Icon152.png" - }, - { - "scale": "2x", - "size": "83.5x83.5", - "idiom": "ipad", - "filename": "Icon167.png" - }, - { - "scale": "1x", - "size": "1024x1024", - "idiom": "ios-marketing", - "filename": "Icon1024.png" - } - ], - "properties": {}, - "info": { - "version": 1, - "author": "xcode" - } -} \ No newline at end of file diff --git a/samples/ControlCatalog.iOS.Legacy/ControlCatalog.iOS.Legacy.csproj b/samples/ControlCatalog.iOS.Legacy/ControlCatalog.iOS.Legacy.csproj deleted file mode 100644 index 045114ff5a..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/ControlCatalog.iOS.Legacy.csproj +++ /dev/null @@ -1,99 +0,0 @@ - - - xamarin.ios10 - 15.0 - true - Debug - iPhoneSimulator - {3AF75F00-B497-4517-9491-922173DE216E} - {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Exe - ControlCatalog.iOS.Legacy - Resources - ControlCatalog.iOS.Legacy - NSUrlSessionHandler - manual - -all - - - true - full - false - bin\iPhoneSimulator\Debug - DEBUG - prompt - 4 - false - x86_64 - None - true - - - none - true - bin\iPhoneSimulator\Release - prompt - 4 - None - x86_64 - false - - - true - full - false - bin\iPhone\Debug - DEBUG - prompt - 4 - false - ARM64 - Entitlements.plist - true - - - none - true - bin\iPhone\Release - prompt - 4 - Entitlements.plist - ARM64 - false - - - - - - - - - - - - - - false - - - - - - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {4488ad85-1495-4809-9aa4-ddfe0a48527e} - Avalonia.iOS - - - {d0a739b9-3c68-4ba6-a328-41606954b6bd} - ControlCatalog - - - - - - diff --git a/samples/ControlCatalog.iOS.Legacy/Entitlements.plist b/samples/ControlCatalog.iOS.Legacy/Entitlements.plist deleted file mode 100644 index 36a8706706..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/Entitlements.plist +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/samples/ControlCatalog.iOS.Legacy/Info.plist b/samples/ControlCatalog.iOS.Legacy/Info.plist deleted file mode 100644 index 430ffb3aca..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDisplayName - ControlCatalog - CFBundleIdentifier - com.companyname.ControlCatalog.iOS - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - MinimumOSVersion - 15.0 - UIDeviceFamily - - 12 - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - XSAppIconAssets - Assets.xcassets/AppIcon.appiconset - UIStatusBarHidden - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/samples/ControlCatalog.iOS.Legacy/Main.cs b/samples/ControlCatalog.iOS.Legacy/Main.cs deleted file mode 100644 index 08b3831d73..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/Main.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UIKit; - -namespace ControlCatalog.iOS.Legacy -{ - public class Application - { - // This is the main entry point of the application. - static void Main(string[] args) - { - // if you want to use a different Application Delegate class from "AppDelegate" - // you can specify it here. - UIApplication.Main(args, null, "AppDelegate"); - } - } -} diff --git a/samples/ControlCatalog.iOS.Legacy/Resources/LaunchScreen.xib b/samples/ControlCatalog.iOS.Legacy/Resources/LaunchScreen.xib deleted file mode 100644 index 3a3df8b38e..0000000000 --- a/samples/ControlCatalog.iOS.Legacy/Resources/LaunchScreen.xib +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 005d8e522201ed2bed004a736696f20017417a30 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 14:36:02 +0000 Subject: [PATCH 51/77] dont report an inputclient if textbox is in readonly mode. --- src/Avalonia.Controls/TextBox.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 995ec142a5..4d71717776 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -202,7 +202,10 @@ namespace Avalonia.Controls FocusableProperty.OverrideDefaultValue(typeof(TextBox), true); TextInputMethodClientRequestedEvent.AddClassHandler((tb, e) => { - e.Client = tb._imClient; + if (!tb.IsReadOnly) + { + e.Client = tb._imClient; + } }); } From 409be7f33f892189982f85642d444e16792e6dbf Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 14:40:40 +0000 Subject: [PATCH 52/77] remove event for querying text input options. replace with attached property that is inheritable. --- samples/ControlCatalog/Pages/TextBoxPage.xaml | 3 +- .../ControlCatalog/Pages/TextBoxPage.xaml.cs | 6 -- .../DBusIme/DBusTextInputMethodBase.cs | 4 +- src/Avalonia.Input/ApiCompatBaseline.txt | 8 ++- src/Avalonia.Input/InputElement.cs | 17 ----- src/Avalonia.Input/Properties/AssemblyInfo.cs | 1 + .../TextInput/ITextInputMethodImpl.cs | 2 +- .../TextInput/InputMethodManager.cs | 31 ++++---- .../TextInput/TextInputOptions.cs | 37 ++++++++++ src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs | 71 +++++++++++++++---- 10 files changed, 128 insertions(+), 52 deletions(-) create mode 100644 src/Avalonia.Input/TextInput/TextInputOptions.cs diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index f15ac8ffd6..dec5f74da8 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -18,7 +18,7 @@ - + diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs b/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs index 9eeefebb02..cd5f790312 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs @@ -13,12 +13,6 @@ namespace ControlCatalog.Pages private void InitializeComponent() { AvaloniaXamlLoader.Load(this); - - this.Get("numericWatermark") - .TextInputOptionsQuery += (s, a) => - { - a.ContentType = Avalonia.Input.TextInput.TextInputContentType.Number; - }; } } } diff --git a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs index a7e83140ae..640befba62 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs @@ -198,9 +198,9 @@ namespace Avalonia.FreeDesktop.DBusIme UpdateActive(); } - void ITextInputMethodImpl.SetActive(bool active) + void ITextInputMethodImpl.SetActive(ITextInputMethodClient client) { - _controlActive = active; + _controlActive = client is { }; UpdateActive(); } diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 270c5305e5..32cd7ae1ba 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -4,11 +4,17 @@ MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.IFocusManager.RemoveFocusScope(Avalonia.Input.IFocusScope)' is present in the implementation but not in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TextInputOptionsQueryEvent' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' is present in the contract but not in the implementation. +MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 12 +Total Issues: 18 diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index 5ec0bd6ee4..6bc9294ddd 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -126,14 +126,6 @@ namespace Avalonia.Input RoutedEvent.Register( "TextInputMethodClientRequested", RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - /// - /// Defines the event. - /// - public static readonly RoutedEvent TextInputOptionsQueryEvent = - RoutedEvent.Register( - "TextInputOptionsQuery", - RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. @@ -283,15 +275,6 @@ namespace Avalonia.Input add { AddHandler(TextInputMethodClientRequestedEvent, value); } remove { RemoveHandler(TextInputMethodClientRequestedEvent, value); } } - - /// - /// Occurs when an input element gains input focus and input method is asking for required content options - /// - public event EventHandler? TextInputOptionsQuery - { - add { AddHandler(TextInputOptionsQueryEvent, value); } - remove { RemoveHandler(TextInputOptionsQueryEvent, value); } - } /// /// Occurs when the pointer enters the control. diff --git a/src/Avalonia.Input/Properties/AssemblyInfo.cs b/src/Avalonia.Input/Properties/AssemblyInfo.cs index 433f821ca3..6a68bf60d1 100644 --- a/src/Avalonia.Input/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Input/Properties/AssemblyInfo.cs @@ -2,4 +2,5 @@ using System.Reflection; using Avalonia.Metadata; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.TextInput")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.GestureRecognizers")] diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs index 2d24ed30a0..99edb1f83a 100644 --- a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs @@ -2,7 +2,7 @@ namespace Avalonia.Input.TextInput { public interface ITextInputMethodImpl { - void SetActive(bool active); + void SetActive(ITextInputMethodClient? client); void SetCursorRect(Rect rect); void SetOptions(TextInputOptionsQueryEventArgs options); void Reset(); diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index 64422a7fdf..93af84d5a0 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -35,21 +35,25 @@ namespace Avalonia.Input.TextInput { _client.CursorRectangleChanged += OnCursorRectangleChanged; _client.TextViewVisualChanged += OnTextViewVisualChanged; - var optionsQuery = new TextInputOptionsQueryEventArgs - { - RoutedEvent = InputElement.TextInputOptionsQueryEvent - }; - _focusedElement?.RaiseEvent(optionsQuery); + _im?.Reset(); - _im?.SetOptions(optionsQuery); - _transformTracker?.SetVisual(_client?.TextViewVisual); + + if (_focusedElement is AvaloniaObject target) + { + var options = + new TextInputOptionsQueryEventArgs { ContentType = TextInputOptions.GetContentType(target) }; + + _im?.SetOptions(options); + } + + _transformTracker.SetVisual(_client?.TextViewVisual); UpdateCursorRect(); - _im?.SetActive(true); + _im?.SetActive(_client); } else { - _im?.SetActive(false); + _im?.SetActive(null); _transformTracker.SetVisual(null); } } @@ -91,12 +95,15 @@ namespace Avalonia.Input.TextInput _focusedElement = element; var inputMethod = (element?.VisualRoot as ITextInputMethodRoot)?.InputMethod; - if (_im != inputMethod) - _im?.SetActive(false); - + _im = inputMethod; TryFindAndApplyClient(); + + if (_im != inputMethod) + { + _im?.SetActive(Client); + } } private void TryFindAndApplyClient() diff --git a/src/Avalonia.Input/TextInput/TextInputOptions.cs b/src/Avalonia.Input/TextInput/TextInputOptions.cs new file mode 100644 index 0000000000..8e144e1cfb --- /dev/null +++ b/src/Avalonia.Input/TextInput/TextInputOptions.cs @@ -0,0 +1,37 @@ +namespace Avalonia.Input.TextInput; + +public class TextInputOptions +{ + /// + /// Defines the property. + /// + public static readonly AttachedProperty ContentTypeProperty = + AvaloniaProperty.RegisterAttached( + "ContentType", + defaultValue: TextInputContentType.Normal, + inherits: true); + + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetContentType(AvaloniaObject avaloniaObject, TextInputContentType value) + { + avaloniaObject.SetValue(ContentTypeProperty, value); + + + } + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The font family. + public static TextInputContentType GetContentType(AvaloniaObject avaloniaObject) + { + return avaloniaObject.GetValue(ContentTypeProperty); + } + +} diff --git a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs index cd298efd30..e3a33ff2b4 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs @@ -1,20 +1,41 @@ using Foundation; using ObjCRuntime; -using UIKit; using Avalonia.Input.TextInput; using Avalonia.Input; using Avalonia.Input.Raw; +using UIKit; namespace Avalonia.iOS; +#nullable enable + +[Adopts("UITextInputTraits")] [Adopts("UIKeyInput")] public partial class AvaloniaView : ITextInputMethodImpl { + private ITextInputMethodClient? _currentClient; + public override bool CanResignFirstResponder => true; public override bool CanBecomeFirstResponder => true; - public override bool CanBecomeFocused => true; - [Export("hasText")] public bool HasText => false; + [Export("hasText")] + public bool HasText + { + get + { + if (_currentClient is { } && _currentClient.SupportsSurroundingText && + _currentClient.SurroundingText.Text.Length > 0) + { + return true; + } + + return false; + } + } + + [Export("keyboardType")] public UIKeyboardType KeyboardType { get; private set; } = UIKeyboardType.Default; + + [Export("isSecureTextEntry")] public bool IsSecureEntry { get; private set; } [Export("insertText:")] public void InsertText(string text) @@ -24,7 +45,6 @@ public partial class AvaloniaView : ITextInputMethodImpl _topLevelImpl.Input?.Invoke(new RawTextInputEventArgs(KeyboardDevice.Instance, 0, InputRoot, text)); } - } [Export("deleteBackward")] @@ -41,13 +61,13 @@ public partial class AvaloniaView : ITextInputMethodImpl } } - void ITextInputMethodImpl.SetActive(bool active) + void ITextInputMethodImpl.SetActive(ITextInputMethodClient? client) { - if (active) + _currentClient = client; + + if (client is { }) { - var isFr = IsFirstResponder; - var next = NextResponder; - var result = BecomeFirstResponder(); + BecomeFirstResponder(); } else { @@ -57,16 +77,43 @@ public partial class AvaloniaView : ITextInputMethodImpl void ITextInputMethodImpl.SetCursorRect(Rect rect) { - + } void ITextInputMethodImpl.SetOptions(TextInputOptionsQueryEventArgs options) { - + switch (options.ContentType) + { + case TextInputContentType.Email: + KeyboardType = UIKeyboardType.EmailAddress; + break; + + case TextInputContentType.Number: + KeyboardType = UIKeyboardType.NumberPad; + break; + + case TextInputContentType.Password: + IsSecureEntry = true; + break; + + case TextInputContentType.Phone: + KeyboardType = UIKeyboardType.PhonePad; + break; + + case TextInputContentType.Url: + KeyboardType = UIKeyboardType.Url; + break; + + case TextInputContentType.Normal: + KeyboardType = UIKeyboardType.Default; + break; + } } void ITextInputMethodImpl.Reset() { - + IsSecureEntry = false; + KeyboardType = UIKeyboardType.Default; + ResignFirstResponder(); } } From 6be7ca9653bb98e09d6f7a7acb666675729c4680 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 14:43:28 +0000 Subject: [PATCH 53/77] fix issue count. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index b74b678632..e93a2b5632 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -153,4 +153,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphR MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice..ctor(System.ReadOnlyMemory, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -Total Issues: 153 +Total Issues: 154 From 079c193a82cac1428859c7c409d2821735745823 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 14:56:29 +0000 Subject: [PATCH 54/77] replace eventargs with simple options class. --- .../TextInput/ITextInputMethodImpl.cs | 2 +- .../TextInput/InputMethodManager.cs | 5 +- .../TextInput/TextInputOptions.cs | 195 +++++++++++++++++- .../TextInputOptionsQueryEventArgs.cs | 32 --- src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs | 2 +- 5 files changed, 191 insertions(+), 45 deletions(-) delete mode 100644 src/Avalonia.Input/TextInput/TextInputOptionsQueryEventArgs.cs diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs index 99edb1f83a..729b517f88 100644 --- a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs @@ -4,7 +4,7 @@ namespace Avalonia.Input.TextInput { void SetActive(ITextInputMethodClient? client); void SetCursorRect(Rect rect); - void SetOptions(TextInputOptionsQueryEventArgs options); + void SetOptions(TextInputOptions options); void Reset(); } diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index 93af84d5a0..e2d6a1fd9f 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -40,10 +40,7 @@ namespace Avalonia.Input.TextInput if (_focusedElement is AvaloniaObject target) { - var options = - new TextInputOptionsQueryEventArgs { ContentType = TextInputOptions.GetContentType(target) }; - - _im?.SetOptions(options); + _im?.SetOptions(TextInputOptions.FromAvaloniaObject(target)); } _transformTracker.SetVisual(_client?.TextViewVisual); diff --git a/src/Avalonia.Input/TextInput/TextInputOptions.cs b/src/Avalonia.Input/TextInput/TextInputOptions.cs index 8e144e1cfb..c60473ad78 100644 --- a/src/Avalonia.Input/TextInput/TextInputOptions.cs +++ b/src/Avalonia.Input/TextInput/TextInputOptions.cs @@ -2,6 +2,21 @@ namespace Avalonia.Input.TextInput; public class TextInputOptions { + public static TextInputOptions FromAvaloniaObject(AvaloniaObject avaloniaObject) + { + var result = new TextInputOptions + { + ContentType = GetContentType(avaloniaObject), + Multiline = GetMultiline(avaloniaObject), + AutoCapitalization = GetAutoCapitalization(avaloniaObject), + IsSensitive = GetIsSensitive(avaloniaObject), + Lowercase = GetLowercase(avaloniaObject), + Uppercase = GetUppercase(avaloniaObject) + }; + + return result; + } + /// /// Defines the property. /// @@ -11,7 +26,6 @@ public class TextInputOptions defaultValue: TextInputContentType.Normal, inherits: true); - /// /// Sets the value of the attached on a control. /// @@ -20,18 +34,185 @@ public class TextInputOptions public static void SetContentType(AvaloniaObject avaloniaObject, TextInputContentType value) { avaloniaObject.SetValue(ContentTypeProperty, value); - - } - + /// - /// Gets the value of the attached on a control. + /// Gets the value of the attached . /// - /// The control. - /// The font family. + /// The target. + /// TextInputContentType public static TextInputContentType GetContentType(AvaloniaObject avaloniaObject) { return avaloniaObject.GetValue(ContentTypeProperty); } + /// + /// The content type (mostly for determining the shape of the virtual keyboard) + /// + public TextInputContentType ContentType { get; set; } + + /// + /// Defines the property. + /// + public static readonly AttachedProperty MultilineProperty = + AvaloniaProperty.RegisterAttached( + "Multiline", + inherits: true); + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetMultiline(AvaloniaObject avaloniaObject, bool value) + { + avaloniaObject.SetValue(MultilineProperty, value); + } + + /// + /// Gets the value of the attached . + /// + /// The target. + /// true if multiline + public static bool GetMultiline(AvaloniaObject avaloniaObject) + { + return avaloniaObject.GetValue(MultilineProperty); + } + + /// + /// Text is multiline + /// + public bool Multiline { get; set; } + + /// + /// Defines the property. + /// + public static readonly AttachedProperty LowercaseProperty = + AvaloniaProperty.RegisterAttached( + "Lowercase", + inherits: true); + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetLowercase(AvaloniaObject avaloniaObject, bool value) + { + avaloniaObject.SetValue(LowercaseProperty, value); + } + + /// + /// Gets the value of the attached . + /// + /// The target. + /// true if Lowercase + public static bool GetLowercase(AvaloniaObject avaloniaObject) + { + return avaloniaObject.GetValue(LowercaseProperty); + } + + /// + /// Text is in lower case + /// + public bool Lowercase { get; set; } + + /// + /// Defines the property. + /// + public static readonly AttachedProperty UppercaseProperty = + AvaloniaProperty.RegisterAttached( + "Uppercase", + inherits: true); + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetUppercase(AvaloniaObject avaloniaObject, bool value) + { + avaloniaObject.SetValue(UppercaseProperty, value); + } + + /// + /// Gets the value of the attached . + /// + /// The target. + /// true if Uppercase + public static bool GetUppercase(AvaloniaObject avaloniaObject) + { + return avaloniaObject.GetValue(UppercaseProperty); + } + + /// + /// Text is in upper case + /// + public bool Uppercase { get; set; } + + /// + /// Defines the property. + /// + public static readonly AttachedProperty AutoCapitalizationProperty = + AvaloniaProperty.RegisterAttached( + "AutoCapitalization", + inherits: true); + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetAutoCapitalization(AvaloniaObject avaloniaObject, bool value) + { + avaloniaObject.SetValue(AutoCapitalizationProperty, value); + } + + /// + /// Gets the value of the attached . + /// + /// The target. + /// true if AutoCapitalization + public static bool GetAutoCapitalization(AvaloniaObject avaloniaObject) + { + return avaloniaObject.GetValue(AutoCapitalizationProperty); + } + + /// + /// Automatically capitalize letters at the start of the sentence + /// + public bool AutoCapitalization { get; set; } + + /// + /// Defines the property. + /// + public static readonly AttachedProperty IsSensitiveProperty = + AvaloniaProperty.RegisterAttached( + "IsSensitive", + inherits: true); + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetIsSensitive(AvaloniaObject avaloniaObject, bool value) + { + avaloniaObject.SetValue(IsSensitiveProperty, value); + } + + /// + /// Gets the value of the attached . + /// + /// The target. + /// true if IsSensitive + public static bool GetIsSensitive(AvaloniaObject avaloniaObject) + { + return avaloniaObject.GetValue(IsSensitiveProperty); + } + + /// + /// Text contains sensitive data like card numbers and should not be stored + /// + public bool IsSensitive { get; set; } } diff --git a/src/Avalonia.Input/TextInput/TextInputOptionsQueryEventArgs.cs b/src/Avalonia.Input/TextInput/TextInputOptionsQueryEventArgs.cs deleted file mode 100644 index 924d0eb166..0000000000 --- a/src/Avalonia.Input/TextInput/TextInputOptionsQueryEventArgs.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Avalonia.Interactivity; - -namespace Avalonia.Input.TextInput -{ - public class TextInputOptionsQueryEventArgs : RoutedEventArgs - { - /// - /// The content type (mostly for determining the shape of the virtual keyboard) - /// - public TextInputContentType ContentType { get; set; } - /// - /// Text is multiline - /// - public bool Multiline { get; set; } - /// - /// Text is in lower case - /// - public bool Lowercase { get; set; } - /// - /// Text is in upper case - /// - public bool Uppercase { get; set; } - /// - /// Automatically capitalize letters at the start of the sentence - /// - public bool AutoCapitalization { get; set; } - /// - /// Text contains sensitive data like card numbers and should not be stored - /// - public bool IsSensitive { get; set; } - } -} diff --git a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs index e3a33ff2b4..d1df67895d 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs @@ -80,7 +80,7 @@ public partial class AvaloniaView : ITextInputMethodImpl } - void ITextInputMethodImpl.SetOptions(TextInputOptionsQueryEventArgs options) + void ITextInputMethodImpl.SetOptions(TextInputOptions options) { switch (options.ContentType) { From 2b99e0159d74ee0446e4e2d0aa6e0aecffca3eae Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 14:59:33 +0000 Subject: [PATCH 55/77] fix baseline. --- src/Avalonia.Input/ApiCompatBaseline.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 32cd7ae1ba..900c646d40 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -16,5 +16,9 @@ MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_TextIn InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptions)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' is present in the contract but not in the implementation. +MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 18 +Total Issues: 22 From 2228d83eb187b22f5efa15680dfc65a27fe0297c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 16:09:59 +0000 Subject: [PATCH 56/77] use styledelement instread of AvaloniaObject --- .../TextInput/TextInputOptions.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Input/TextInput/TextInputOptions.cs b/src/Avalonia.Input/TextInput/TextInputOptions.cs index c60473ad78..c704d6b914 100644 --- a/src/Avalonia.Input/TextInput/TextInputOptions.cs +++ b/src/Avalonia.Input/TextInput/TextInputOptions.cs @@ -2,7 +2,7 @@ namespace Avalonia.Input.TextInput; public class TextInputOptions { - public static TextInputOptions FromAvaloniaObject(AvaloniaObject avaloniaObject) + public static TextInputOptions FromStyledElement(StyledElement avaloniaObject) { var result = new TextInputOptions { @@ -21,7 +21,7 @@ public class TextInputOptions /// Defines the property. /// public static readonly AttachedProperty ContentTypeProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "ContentType", defaultValue: TextInputContentType.Normal, inherits: true); @@ -31,7 +31,7 @@ public class TextInputOptions /// /// The control. /// The property value to set. - public static void SetContentType(AvaloniaObject avaloniaObject, TextInputContentType value) + public static void SetContentType(StyledElement avaloniaObject, TextInputContentType value) { avaloniaObject.SetValue(ContentTypeProperty, value); } @@ -41,7 +41,7 @@ public class TextInputOptions ///
/// The target. /// TextInputContentType - public static TextInputContentType GetContentType(AvaloniaObject avaloniaObject) + public static TextInputContentType GetContentType(StyledElement avaloniaObject) { return avaloniaObject.GetValue(ContentTypeProperty); } @@ -55,7 +55,7 @@ public class TextInputOptions /// Defines the property. ///
public static readonly AttachedProperty MultilineProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "Multiline", inherits: true); @@ -64,7 +64,7 @@ public class TextInputOptions ///
/// The control. /// The property value to set. - public static void SetMultiline(AvaloniaObject avaloniaObject, bool value) + public static void SetMultiline(StyledElement avaloniaObject, bool value) { avaloniaObject.SetValue(MultilineProperty, value); } @@ -74,7 +74,7 @@ public class TextInputOptions ///
/// The target. /// true if multiline - public static bool GetMultiline(AvaloniaObject avaloniaObject) + public static bool GetMultiline(StyledElement avaloniaObject) { return avaloniaObject.GetValue(MultilineProperty); } @@ -88,7 +88,7 @@ public class TextInputOptions /// Defines the property. ///
public static readonly AttachedProperty LowercaseProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "Lowercase", inherits: true); @@ -97,7 +97,7 @@ public class TextInputOptions ///
/// The control. /// The property value to set. - public static void SetLowercase(AvaloniaObject avaloniaObject, bool value) + public static void SetLowercase(StyledElement avaloniaObject, bool value) { avaloniaObject.SetValue(LowercaseProperty, value); } @@ -107,7 +107,7 @@ public class TextInputOptions ///
/// The target. /// true if Lowercase - public static bool GetLowercase(AvaloniaObject avaloniaObject) + public static bool GetLowercase(StyledElement avaloniaObject) { return avaloniaObject.GetValue(LowercaseProperty); } @@ -121,7 +121,7 @@ public class TextInputOptions /// Defines the property. ///
public static readonly AttachedProperty UppercaseProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "Uppercase", inherits: true); @@ -130,7 +130,7 @@ public class TextInputOptions /// /// The control. /// The property value to set. - public static void SetUppercase(AvaloniaObject avaloniaObject, bool value) + public static void SetUppercase(StyledElement avaloniaObject, bool value) { avaloniaObject.SetValue(UppercaseProperty, value); } @@ -140,7 +140,7 @@ public class TextInputOptions /// /// The target. /// true if Uppercase - public static bool GetUppercase(AvaloniaObject avaloniaObject) + public static bool GetUppercase(StyledElement avaloniaObject) { return avaloniaObject.GetValue(UppercaseProperty); } @@ -154,7 +154,7 @@ public class TextInputOptions /// Defines the property. /// public static readonly AttachedProperty AutoCapitalizationProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "AutoCapitalization", inherits: true); @@ -163,7 +163,7 @@ public class TextInputOptions /// /// The control. /// The property value to set. - public static void SetAutoCapitalization(AvaloniaObject avaloniaObject, bool value) + public static void SetAutoCapitalization(StyledElement avaloniaObject, bool value) { avaloniaObject.SetValue(AutoCapitalizationProperty, value); } @@ -173,7 +173,7 @@ public class TextInputOptions /// /// The target. /// true if AutoCapitalization - public static bool GetAutoCapitalization(AvaloniaObject avaloniaObject) + public static bool GetAutoCapitalization(StyledElement avaloniaObject) { return avaloniaObject.GetValue(AutoCapitalizationProperty); } @@ -187,7 +187,7 @@ public class TextInputOptions /// Defines the property. /// public static readonly AttachedProperty IsSensitiveProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "IsSensitive", inherits: true); @@ -196,7 +196,7 @@ public class TextInputOptions /// /// The control. /// The property value to set. - public static void SetIsSensitive(AvaloniaObject avaloniaObject, bool value) + public static void SetIsSensitive(StyledElement avaloniaObject, bool value) { avaloniaObject.SetValue(IsSensitiveProperty, value); } @@ -206,7 +206,7 @@ public class TextInputOptions /// /// The target. /// true if IsSensitive - public static bool GetIsSensitive(AvaloniaObject avaloniaObject) + public static bool GetIsSensitive(StyledElement avaloniaObject) { return avaloniaObject.GetValue(IsSensitiveProperty); } From 784b25539a7126c664b8f94827f17f5b0026d530 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 16:26:35 +0000 Subject: [PATCH 57/77] rename file. --- .../Avalonia.iOS/{AvaloniaUIResponder.cs => AvaloniaView.Text.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/iOS/Avalonia.iOS/{AvaloniaUIResponder.cs => AvaloniaView.Text.cs} (100%) diff --git a/src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs similarity index 100% rename from src/iOS/Avalonia.iOS/AvaloniaUIResponder.cs rename to src/iOS/Avalonia.iOS/AvaloniaView.Text.cs From b1d5d9b8bdab1a8a4dca6c0caf6beb849f2df900 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 16:26:47 +0000 Subject: [PATCH 58/77] fix styledelement. --- src/Avalonia.Input/TextInput/InputMethodManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index e2d6a1fd9f..12305dfe33 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -38,9 +38,9 @@ namespace Avalonia.Input.TextInput _im?.Reset(); - if (_focusedElement is AvaloniaObject target) + if (_focusedElement is StyledElement target) { - _im?.SetOptions(TextInputOptions.FromAvaloniaObject(target)); + _im?.SetOptions(TextInputOptions.FromStyledElement(target)); } _transformTracker.SetVisual(_client?.TextViewVisual); From 18b53b875d81d3890a9fc6f77efced954ba27e91 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 16:30:41 +0000 Subject: [PATCH 59/77] remove no longer needed check. --- src/Avalonia.Input/TextInput/InputMethodManager.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index 12305dfe33..9f686a150a 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -96,11 +96,6 @@ namespace Avalonia.Input.TextInput _im = inputMethod; TryFindAndApplyClient(); - - if (_im != inputMethod) - { - _im?.SetActive(Client); - } } private void TryFindAndApplyClient() From 8b8253c3bfb6f5efc636227dbca88adc5a4b18eb Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 16:35:35 +0000 Subject: [PATCH 60/77] fix broken apis. --- .../DBusIme/DBusTextInputMethodBase.cs | 4 ++-- .../DBusIme/Fcitx/FcitxX11TextInputMethod.cs | 2 +- src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs | 2 +- src/Avalonia.Input/TextInput/InputMethodManager.cs | 9 +++++++-- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs | 2 +- src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs index 640befba62..864c579319 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs @@ -198,7 +198,7 @@ namespace Avalonia.FreeDesktop.DBusIme UpdateActive(); } - void ITextInputMethodImpl.SetActive(ITextInputMethodClient client) + void ITextInputMethodImpl.SetClient(ITextInputMethodClient client) { _controlActive = client is { }; UpdateActive(); @@ -272,7 +272,7 @@ namespace Avalonia.FreeDesktop.DBusIme UpdateCursorRect(); } - public abstract void SetOptions(TextInputOptionsQueryEventArgs options); + public abstract void SetOptions(TextInputOptions options); void ITextInputMethodImpl.Reset() { diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs index 31a061571f..15dc131e4e 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs @@ -93,7 +93,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx (uint)args.Timestamp).ConfigureAwait(false); } - public override void SetOptions(TextInputOptionsQueryEventArgs options) => + public override void SetOptions(TextInputOptions options) => Enqueue(async () => { if(_context == null) diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs index 729b517f88..4404c903b7 100644 --- a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs @@ -2,7 +2,7 @@ namespace Avalonia.Input.TextInput { public interface ITextInputMethodImpl { - void SetActive(ITextInputMethodClient? client); + void SetClient(ITextInputMethodClient? client); void SetCursorRect(Rect rect); void SetOptions(TextInputOptions options); void Reset(); diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index 9f686a150a..e19bf91f95 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -46,11 +46,11 @@ namespace Avalonia.Input.TextInput _transformTracker.SetVisual(_client?.TextViewVisual); UpdateCursorRect(); - _im?.SetActive(_client); + _im?.SetClient(_client); } else { - _im?.SetActive(null); + _im?.SetClient(null); _transformTracker.SetVisual(null); } } @@ -92,6 +92,11 @@ namespace Avalonia.Input.TextInput _focusedElement = element; var inputMethod = (element?.VisualRoot as ITextInputMethodRoot)?.InputMethod; + + if (_im != inputMethod) + { + _im?.SetClient(null); + } _im = inputMethod; diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index 3649a884a6..cdc41bebf5 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -386,7 +386,7 @@ namespace Avalonia.Web.Blazor { } - public void SetOptions(TextInputOptionsQueryEventArgs options) + public void SetOptions(TextInputOptions options) { } diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 71e33554f1..ad6c112043 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -216,7 +216,7 @@ namespace Avalonia.Win32.Input ImmSetCompositionFont(himc, ref logFont); } - public void SetOptions(TextInputOptionsQueryEventArgs options) + public void SetOptions(TextInputOptions options) { // we're skipping this. not usable on windows } From 0609c2cbe30e59c1436fc9b497f8ee0428350984 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 10 Mar 2022 19:49:06 +0300 Subject: [PATCH 61/77] RoutedViewHost: Fix property registration --- src/Avalonia.ReactiveUI/RoutedViewHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs index a475cf5eac..9269dc70f8 100644 --- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs +++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs @@ -62,7 +62,7 @@ namespace Avalonia.ReactiveUI /// for the property. /// public static readonly StyledProperty ViewContractProperty = - AvaloniaProperty.Register(nameof(ViewContract)); + AvaloniaProperty.Register(nameof(ViewContract)); /// /// Initializes a new instance of the class. From 04d4c37c4fe433a5113dfbb42850535a4bcdc544 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 17:12:42 +0000 Subject: [PATCH 62/77] restore default options if no properties are found. --- src/Avalonia.Input/TextInput/InputMethodManager.cs | 4 ++++ src/Avalonia.Input/TextInput/TextInputOptions.cs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index e19bf91f95..4734224da4 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -42,6 +42,10 @@ namespace Avalonia.Input.TextInput { _im?.SetOptions(TextInputOptions.FromStyledElement(target)); } + else + { + _im?.SetOptions(TextInputOptions.Default); + } _transformTracker.SetVisual(_client?.TextViewVisual); UpdateCursorRect(); diff --git a/src/Avalonia.Input/TextInput/TextInputOptions.cs b/src/Avalonia.Input/TextInput/TextInputOptions.cs index c704d6b914..af5f142a25 100644 --- a/src/Avalonia.Input/TextInput/TextInputOptions.cs +++ b/src/Avalonia.Input/TextInput/TextInputOptions.cs @@ -16,6 +16,8 @@ public class TextInputOptions return result; } + + public static readonly TextInputOptions Default = new(); /// /// Defines the property. From 47989e484a77bf913a82881e7894858d604aafb2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 17:13:08 +0000 Subject: [PATCH 63/77] osx no need to reset text options. manually on reset. --- src/iOS/Avalonia.iOS/AvaloniaView.Text.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs index d1df67895d..85d984229f 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs @@ -61,7 +61,7 @@ public partial class AvaloniaView : ITextInputMethodImpl } } - void ITextInputMethodImpl.SetActive(ITextInputMethodClient? client) + void ITextInputMethodImpl.SetClient(ITextInputMethodClient? client) { _currentClient = client; @@ -112,8 +112,6 @@ public partial class AvaloniaView : ITextInputMethodImpl void ITextInputMethodImpl.Reset() { - IsSecureEntry = false; - KeyboardType = UIKeyboardType.Default; ResignFirstResponder(); } } From 9871144d3fad8da811d93473e48da118cc2effef Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 17:14:54 +0000 Subject: [PATCH 64/77] fix ime implementations with new api. --- .../DBusIme/IBus/IBusX11TextInputMethod.cs | 2 +- src/Avalonia.Input/ApiCompatBaseline.txt | 2 +- src/Avalonia.X11/X11Window.Xim.cs | 7 +++---- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs | 4 +++- src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs index a73de9dae8..1397eaa57b 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs @@ -97,7 +97,7 @@ namespace Avalonia.FreeDesktop.DBusIme.IBus return _context.ProcessKeyEventAsync((uint)keyVal, (uint)keyCode, (uint)state); } - public override void SetOptions(TextInputOptionsQueryEventArgs options) + public override void SetOptions(TextInputOptions options) { // No-op, because ibus } diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 900c646d40..529e93fd82 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -13,9 +13,9 @@ MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_TextInput MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetClient(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptions)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' does not exist in the implementation but it does exist in the contract. diff --git a/src/Avalonia.X11/X11Window.Xim.cs b/src/Avalonia.X11/X11Window.Xim.cs index ecb23ff097..bedd1d22fc 100644 --- a/src/Avalonia.X11/X11Window.Xim.cs +++ b/src/Avalonia.X11/X11Window.Xim.cs @@ -10,7 +10,6 @@ namespace Avalonia.X11 { partial class X11Window { - class XimInputMethod : ITextInputMethodImpl, IX11InputMethodControl { private readonly X11Window _parent; @@ -58,9 +57,9 @@ namespace Avalonia.X11 UpdateActive(); } - public void SetActive(bool active) + public void SetClient(ITextInputMethodClient client) { - _controlActive = active; + _controlActive = client is { }; UpdateActive(); } @@ -87,7 +86,7 @@ namespace Avalonia.X11 // No-op } - public void SetOptions(TextInputOptionsQueryEventArgs options) + public void SetOptions(TextInputOptions options) { // No-op } diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index cdc41bebf5..1ccf53943a 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -367,10 +367,12 @@ namespace Avalonia.Web.Blazor } } - public void SetActive(bool active) + public void SetClient(ITextInputMethodClient? client) { _inputHelper.Clear(); + var active = client is { }; + if (active) { _inputHelper.Show(); diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index ad6c112043..9ff6f76ac4 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -74,12 +74,12 @@ namespace Avalonia.Win32.Input } } - public void SetActive(bool active) + public void SetClient(ITextInputMethodClient client) { - _active = active; + _active = client is { }; Dispatcher.UIThread.Post(() => { - if (active) + if (_active) { if (DefaultImc != IntPtr.Zero) { From b2ce338adec61934fea596a8ca53e580351166a6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 17:17:32 +0000 Subject: [PATCH 65/77] add empty baselines. --- src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt | 1 + src/Avalonia.DesignerSupport/ApiCompatBaseline.txt | 1 + src/Avalonia.Interactivity/ApiCompatBaseline.txt | 1 + src/Avalonia.Layout/ApiCompatBaseline.txt | 1 + src/Avalonia.Remote.Protocol/ApiCompatBaseline.txt | 1 + src/Avalonia.Themes.Default/ApiCompatBaseline.txt | 1 + src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt | 1 + src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt | 1 + src/Markup/Avalonia.Markup/ApiCompatBaseline.txt | 1 + 9 files changed, 9 insertions(+) create mode 100644 src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt create mode 100644 src/Avalonia.DesignerSupport/ApiCompatBaseline.txt create mode 100644 src/Avalonia.Interactivity/ApiCompatBaseline.txt create mode 100644 src/Avalonia.Layout/ApiCompatBaseline.txt create mode 100644 src/Avalonia.Remote.Protocol/ApiCompatBaseline.txt create mode 100644 src/Avalonia.Themes.Default/ApiCompatBaseline.txt create mode 100644 src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt create mode 100644 src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt create mode 100644 src/Markup/Avalonia.Markup/ApiCompatBaseline.txt diff --git a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Avalonia.DesignerSupport/ApiCompatBaseline.txt b/src/Avalonia.DesignerSupport/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.DesignerSupport/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Avalonia.Interactivity/ApiCompatBaseline.txt b/src/Avalonia.Interactivity/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.Interactivity/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Avalonia.Layout/ApiCompatBaseline.txt b/src/Avalonia.Layout/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.Layout/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Avalonia.Remote.Protocol/ApiCompatBaseline.txt b/src/Avalonia.Remote.Protocol/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.Remote.Protocol/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Avalonia.Themes.Default/ApiCompatBaseline.txt b/src/Avalonia.Themes.Default/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.Themes.Default/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt b/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 diff --git a/src/Markup/Avalonia.Markup/ApiCompatBaseline.txt b/src/Markup/Avalonia.Markup/ApiCompatBaseline.txt new file mode 100644 index 0000000000..fcc74cf864 --- /dev/null +++ b/src/Markup/Avalonia.Markup/ApiCompatBaseline.txt @@ -0,0 +1 @@ +Total Issues: 0 From 31ee43dccb696ea61e1b9b0c829fb1a2407cc9d1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 17:52:00 +0000 Subject: [PATCH 66/77] add full range of keyboard content types. --- .../DBusIme/Fcitx/FcitxX11TextInputMethod.cs | 2 +- src/Avalonia.Input/ApiCompatBaseline.txt | 7 ++- .../TextInput/TextInputContentType.cs | 62 +++++++++++++++++-- src/iOS/Avalonia.iOS/AvaloniaView.Text.cs | 14 ++++- 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs index 15dc131e4e..0b85965de7 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs @@ -111,7 +111,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx flags |= FcitxCapabilityFlags.CAPACITY_NUMBER; else if (options.ContentType == TextInputContentType.Password) flags |= FcitxCapabilityFlags.CAPACITY_PASSWORD; - else if (options.ContentType == TextInputContentType.Phone) + else if (options.ContentType == TextInputContentType.Digits) flags |= FcitxCapabilityFlags.CAPACITY_DIALABLE; else if (options.ContentType == TextInputContentType.Url) flags |= FcitxCapabilityFlags.CAPACITY_URL; diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 529e93fd82..68b4d4754f 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -19,6 +19,11 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.T InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptions)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' does not exist in the implementation but it does exist in the contract. +EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Email' is (System.Int32)5 in the implementation but (System.Int32)1 in the contract. +EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Number' is (System.Int32)4 in the implementation but (System.Int32)3 in the contract. +EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Password' is (System.Int32)8 in the implementation but (System.Int32)5 in the contract. +MembersMustExist : Member 'public Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Phone' does not exist in the implementation but it does exist in the contract. +EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Url' is (System.Int32)6 in the implementation but (System.Int32)4 in the contract. TypesMustExist : Type 'Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 22 +Total Issues: 27 diff --git a/src/Avalonia.Input/TextInput/TextInputContentType.cs b/src/Avalonia.Input/TextInput/TextInputContentType.cs index 5d73fc1552..02a9385354 100644 --- a/src/Avalonia.Input/TextInput/TextInputContentType.cs +++ b/src/Avalonia.Input/TextInput/TextInputContentType.cs @@ -1,12 +1,62 @@ namespace Avalonia.Input.TextInput -{ +{ public enum TextInputContentType { + /// + /// Default keyboard for the users configured input method. + /// Normal = 0, - Email = 1, - Phone = 2, - Number = 3, - Url = 4, - Password = 5 + + /// + /// Display a keyboard that only has alphabetic characters. + /// + Alpha, + + /// + /// Display a numeric keypad only capable of numbers. i.e. Phone number + /// + Digits, + + /// + /// Display a numeric keypad for inputting a PIN. + /// + Pin, + + /// + /// Display a numeric keypad capable of inputting numbers including decimal seperator and sign. + /// + Number, + + /// + /// Display a keyboard for entering an email address. + /// + Email, + + /// + /// Display a keyboard for entering a URL. + /// + Url, + + /// + /// Display a keyboard for entering a persons name. + /// + Name, + + /// + /// Display a keyboard for entering sensitive data. + /// + Password, + + /// + /// Display a keyboard suitable for #tag and @mentions. + /// Not available on all platforms, will fallback to a suitable keyboard + /// when not available. + /// + Social, + + /// + /// Display a keyboard for entering a search keyword. + /// + Search } } diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs index 85d984229f..4f4b917a78 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs @@ -82,6 +82,8 @@ public partial class AvaloniaView : ITextInputMethodImpl void ITextInputMethodImpl.SetOptions(TextInputOptions options) { + IsSecureEntry = false; + switch (options.ContentType) { case TextInputContentType.Email: @@ -89,14 +91,19 @@ public partial class AvaloniaView : ITextInputMethodImpl break; case TextInputContentType.Number: + KeyboardType = UIKeyboardType.PhonePad; + break; + + case TextInputContentType.Pin: KeyboardType = UIKeyboardType.NumberPad; + IsSecureEntry = true; break; case TextInputContentType.Password: IsSecureEntry = true; break; - case TextInputContentType.Phone: + case TextInputContentType.Digits: KeyboardType = UIKeyboardType.PhonePad; break; @@ -108,6 +115,11 @@ public partial class AvaloniaView : ITextInputMethodImpl KeyboardType = UIKeyboardType.Default; break; } + + if (options.IsSensitive) + { + IsSecureEntry = true; + } } void ITextInputMethodImpl.Reset() From 56845e22d58db1e9cfa7c3d7f1882e4ec5d9acb5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 17:59:17 +0000 Subject: [PATCH 67/77] ios support all input types. --- src/iOS/Avalonia.iOS/AvaloniaView.Text.cs | 39 ++++++++++++++++------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs index 4f4b917a78..d49ce5310c 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs @@ -86,11 +86,15 @@ public partial class AvaloniaView : ITextInputMethodImpl switch (options.ContentType) { - case TextInputContentType.Email: - KeyboardType = UIKeyboardType.EmailAddress; + case TextInputContentType.Normal: + KeyboardType = UIKeyboardType.Default; break; - - case TextInputContentType.Number: + + case TextInputContentType.Alpha: + KeyboardType = UIKeyboardType.AsciiCapable; + break; + + case TextInputContentType.Digits: KeyboardType = UIKeyboardType.PhonePad; break; @@ -98,21 +102,34 @@ public partial class AvaloniaView : ITextInputMethodImpl KeyboardType = UIKeyboardType.NumberPad; IsSecureEntry = true; break; - - case TextInputContentType.Password: - IsSecureEntry = true; - break; - - case TextInputContentType.Digits: + + case TextInputContentType.Number: KeyboardType = UIKeyboardType.PhonePad; break; + + case TextInputContentType.Email: + KeyboardType = UIKeyboardType.EmailAddress; + break; case TextInputContentType.Url: KeyboardType = UIKeyboardType.Url; break; - case TextInputContentType.Normal: + case TextInputContentType.Name: + KeyboardType = UIKeyboardType.NamePhonePad; + break; + + case TextInputContentType.Password: KeyboardType = UIKeyboardType.Default; + IsSecureEntry = true; + break; + + case TextInputContentType.Social: + KeyboardType = UIKeyboardType.Twitter; + break; + + case TextInputContentType.Search: + KeyboardType = UIKeyboardType.WebSearch; break; } From addf992e533e6cbed86e0b92f77c789bdc24524d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 19:06:43 +0100 Subject: [PATCH 68/77] Revert --- src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 1c5f6fa786..1b2142f6c9 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -2,7 +2,6 @@ using System.Reactive.Disposables; using Avalonia.Logging; using Avalonia.Media; -using Avalonia.Media.Immutable; using Avalonia.Media.Transformation; namespace Avalonia.Animation.Animators @@ -12,7 +11,7 @@ namespace Avalonia.Animation.Animators /// public class TransformAnimator : Animator { - private DoubleAnimator _doubleAnimator; + DoubleAnimator _doubleAnimator; /// public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable obsMatch, Action onComplete) From a887a412b3bb24198e9cbee6307997b3ed97ec6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 19:07:07 +0100 Subject: [PATCH 69/77] Remove animator --- .../Animators/GradientBrushAnimator.cs | 13 +++--------- .../Animation/Animators/MatrixAnimator.cs | 20 ------------------- .../Animation/Transitions/MatrixTransition.cs | 11 ---------- src/Avalonia.Visuals/Matrix.cs | 7 ------- 4 files changed, 3 insertions(+), 48 deletions(-) delete mode 100644 src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs delete mode 100644 src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs index 3c13752992..f2d5632995 100644 --- a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs @@ -16,8 +16,6 @@ namespace Avalonia.Animation.Animators { private static readonly RelativePointAnimator s_relativePointAnimator = new RelativePointAnimator(); private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator(); - private static readonly TransformAnimator s_transformAnimator = new TransformAnimator(); - private static readonly MatrixAnimator _matrixAnimator = new MatrixAnimator(); public override IGradientBrush? Interpolate(double progress, IGradientBrush? oldValue, IGradientBrush? newValue) { @@ -32,7 +30,7 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -42,7 +40,7 @@ namespace Avalonia.Animation.Animators return new ImmutableConicGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -51,7 +49,7 @@ namespace Avalonia.Animation.Animators return new ImmutableLinearGradientBrush( InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), - oldValue.Transform is { } && newValue.Transform is { } ? InterpolateTransform(progress, oldValue.Transform, newValue.Transform) : null, + oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -91,11 +89,6 @@ namespace Avalonia.Animation.Animators return stops; } - private ImmutableTransform InterpolateTransform(double progress, ITransform oldValue, ITransform newValue) - { - return new ImmutableTransform(_matrixAnimator.Interpolate(progress, oldValue.Value, newValue.Value)); - } - internal static IGradientBrush ConvertSolidColorBrushToGradient(IGradientBrush gradientBrush, ISolidColorBrush solidColorBrush) { switch (gradientBrush) diff --git a/src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs deleted file mode 100644 index d5b6267a3c..0000000000 --- a/src/Avalonia.Visuals/Animation/Animators/MatrixAnimator.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Avalonia.Animation.Animators -{ - /// - /// Animator that handles properties. - /// - public class MatrixAnimator : Animator - { - /// - public override Matrix Interpolate(double progress, Matrix oldValue, Matrix newValue) - { - return new Matrix( - ((newValue.M11 - oldValue.M11) * progress) + oldValue.M11, - ((newValue.M12 - oldValue.M12) * progress) + oldValue.M12, - ((newValue.M21 - oldValue.M21) * progress) + oldValue.M21, - ((newValue.M22 - oldValue.M22) * progress) + oldValue.M22, - ((newValue.M31 - oldValue.M31) * progress) + oldValue.M31, - ((newValue.M32 - oldValue.M32) * progress) + oldValue.M32); - } - } -} diff --git a/src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs deleted file mode 100644 index 59bf06ca26..0000000000 --- a/src/Avalonia.Visuals/Animation/Transitions/MatrixTransition.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Animation.Animators; - -namespace Avalonia.Animation -{ - /// - /// Transition class that handles with type. - /// - public class MatrixTransition : AnimatorDrivenTransition - { - } -} diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 7a56aa6fe2..af62375b52 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -15,13 +15,6 @@ namespace Avalonia #endif readonly struct Matrix : IEquatable { - static Matrix() - { -#if !BUILDTASK - Animation.Animation.RegisterAnimator(prop => typeof(Matrix).IsAssignableFrom(prop.PropertyType)); -#endif - } - private readonly double _m11; private readonly double _m12; private readonly double _m21; From 3d567425db6c7f6247ae70897a84b760bd7cbfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 19:14:50 +0100 Subject: [PATCH 70/77] Revert --- src/Avalonia.Visuals/Matrix.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index af62375b52..8136f843df 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -1,8 +1,5 @@ using System; using System.Globalization; -#if !BUILDTASK -using Avalonia.Animation.Animators; -#endif using Avalonia.Utilities; namespace Avalonia From 2da1d3cfe3280efc76eeac2ea87ec472c34e5547 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 18:37:12 +0000 Subject: [PATCH 71/77] remove android keyboard hacks. --- .../Avalonia.Android/AndroidInputMethod.cs | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index b6491a5890..880b210a6c 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -13,7 +13,6 @@ namespace Avalonia.Android { private readonly TView _host; private readonly InputMethodManager _imm; - private IInputElement _inputElement; public AndroidInputMethod(TView host) { @@ -33,8 +32,10 @@ namespace Avalonia.Android _imm.RestartInput(_host); } - public void SetActive(bool active) + public void SetClient(ITextInputMethodClient client) { + var active = client is { }; + if (active) { _host.RequestFocus(); @@ -49,20 +50,8 @@ namespace Avalonia.Android { } - public void SetOptions(TextInputOptionsQueryEventArgs options) + public void SetOptions(TextInputOptions options) { - if (_inputElement != null) - { - _inputElement.PointerReleased -= RestoreSoftKeyboard; - } - - _inputElement = options.Source as InputElement; - - if (_inputElement == null) - { - _imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None); - } - _host.InitEditorInfo((outAttrs) => { outAttrs.InputType = options.ContentType switch @@ -70,7 +59,7 @@ namespace Avalonia.Android TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress, TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber, TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword, - TextInputContentType.Phone => global::Android.Text.InputTypes.ClassPhone, + TextInputContentType.Digits => global::Android.Text.InputTypes.ClassPhone, TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri, _ => global::Android.Text.InputTypes.ClassText }; @@ -86,8 +75,6 @@ namespace Avalonia.Android outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi; }); - - //_inputElement.PointerReleased += RestoreSoftKeyboard; } private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e) From 5efb44806623c4420f42294fb574308ef4113680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 20:02:14 +0100 Subject: [PATCH 72/77] Add nullable annotations --- .../Media/Immutable/ImmutableConicGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableGradientBrush.cs | 4 ++-- src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs | 2 +- .../Media/Immutable/ImmutableLinearGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableRadialGradientBrush.cs | 2 +- .../Media/Immutable/ImmutableSolidColorBrush.cs | 4 ++-- src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs | 4 ++-- src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs | 2 +- src/Avalonia.Visuals/Media/TransformExtensions.cs | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs index 869262c7a4..4b97615c4c 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media.Immutable public ImmutableConicGradientBrush( IReadOnlyList gradientStops, double opacity = 1, - ImmutableTransform transform = null, + ImmutableTransform? transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, double angle = 0) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs index 78e46f52d0..f1e51687d0 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs @@ -17,7 +17,7 @@ namespace Avalonia.Media.Immutable protected ImmutableGradientBrush( IReadOnlyList gradientStops, double opacity, - ImmutableTransform transform, + ImmutableTransform? transform, GradientSpreadMethod spreadMethod) { GradientStops = gradientStops; @@ -45,7 +45,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the transform of the brush. /// - public ITransform Transform { get; } + public ITransform? Transform { get; } /// public GradientSpreadMethod SpreadMethod { get; } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs index 41456b7f92..2d4fe45c8e 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs @@ -29,7 +29,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, double opacity = 1, - ImmutableTransform transform = null, + ImmutableTransform? transform = null, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs index 1c905c166e..64c0f9b44e 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media.Immutable public ImmutableLinearGradientBrush( IReadOnlyList gradientStops, double opacity = 1, - ImmutableTransform transform = null, + ImmutableTransform? transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? startPoint = null, RelativePoint? endPoint = null) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs index 910e14faf7..3da4bdd8e9 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -24,7 +24,7 @@ namespace Avalonia.Media.Immutable public ImmutableRadialGradientBrush( IReadOnlyList gradientStops, double opacity = 1, - ImmutableTransform transform = null, + ImmutableTransform? transform = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, RelativePoint? gradientOrigin = null, diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs index 8c6de8082a..9cd453183a 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs @@ -13,7 +13,7 @@ namespace Avalonia.Media.Immutable /// The color to use. /// The opacity of the brush. /// The transform of the brush. - public ImmutableSolidColorBrush(Color color, double opacity = 1, ImmutableTransform transform = null) + public ImmutableSolidColorBrush(Color color, double opacity = 1, ImmutableTransform? transform = null) { Color = color; Opacity = opacity; @@ -51,7 +51,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the transform of the brush. /// - public ITransform Transform { get; } + public ITransform? Transform { get; } public bool Equals(ImmutableSolidColorBrush? other) { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs index ed722275f5..1019751733 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs @@ -26,7 +26,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY, RelativeRect destinationRect, double opacity, - ImmutableTransform transform, + ImmutableTransform? transform, RelativeRect sourceRect, Stretch stretch, TileMode tileMode, @@ -76,7 +76,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the transform of the brush. /// - public ITransform Transform { get; } + public ITransform? Transform { get; } /// public RelativeRect SourceRect { get; } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs index abec170419..0fd2905660 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs @@ -29,7 +29,7 @@ namespace Avalonia.Media.Immutable AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, double opacity = 1, - ImmutableTransform transform = null, + ImmutableTransform? transform = null, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Visuals/Media/TransformExtensions.cs index c264d483cd..2295632bd6 100644 --- a/src/Avalonia.Visuals/Media/TransformExtensions.cs +++ b/src/Avalonia.Visuals/Media/TransformExtensions.cs @@ -16,7 +16,7 @@ namespace Avalonia.Media /// The result of calling if the transform is mutable, /// otherwise . /// - public static ImmutableTransform ToImmutable(this ITransform transform) + public static ImmutableTransform ToImmutable(this ITransform? transform) { Contract.Requires(transform != null); From 1228dc05e6215675ad6a89b1ea47a7f1c65450a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 20:07:35 +0100 Subject: [PATCH 73/77] Make nullable --- src/Avalonia.Visuals/Media/Brush.cs | 6 +++--- src/Avalonia.Visuals/Media/IBrush.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index c67cc39df7..9d989979a7 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -21,8 +21,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty TransformProperty = - AvaloniaProperty.Register(nameof(Transform)); + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); /// public event EventHandler? Invalidated; @@ -45,7 +45,7 @@ namespace Avalonia.Media /// /// Gets or sets the transform of the brush. /// - public ITransform Transform + public ITransform? Transform { get { return GetValue(TransformProperty); } set { SetValue(TransformProperty, value); } diff --git a/src/Avalonia.Visuals/Media/IBrush.cs b/src/Avalonia.Visuals/Media/IBrush.cs index c3d03fb35b..830c066182 100644 --- a/src/Avalonia.Visuals/Media/IBrush.cs +++ b/src/Avalonia.Visuals/Media/IBrush.cs @@ -16,6 +16,6 @@ namespace Avalonia.Media /// /// Gets the transform of the brush. /// - ITransform Transform { get; } + ITransform? Transform { get; } } } From 34833ad6b6ca1bf108aef4009b01da058c4edab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 20:20:59 +0100 Subject: [PATCH 74/77] Update TransformExtensions.cs --- src/Avalonia.Visuals/Media/TransformExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Visuals/Media/TransformExtensions.cs index 2295632bd6..ccf2231ce2 100644 --- a/src/Avalonia.Visuals/Media/TransformExtensions.cs +++ b/src/Avalonia.Visuals/Media/TransformExtensions.cs @@ -16,9 +16,9 @@ namespace Avalonia.Media /// The result of calling if the transform is mutable, /// otherwise . /// - public static ImmutableTransform ToImmutable(this ITransform? transform) + public static ImmutableTransform ToImmutable(this ITransform transform) { - Contract.Requires(transform != null); + _ = transform ?? throw new ArgumentNullException(nameof(transform)); return (transform as Transform)?.ToImmutable() ?? new ImmutableTransform(transform.Value); } From a5992d363a2344e208f6bb3edd8ebca821394c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 20:27:56 +0100 Subject: [PATCH 75/77] Fix --- .../Media/Immutable/ImmutableSolidColorBrush.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs index 9cd453183a..9b1b2500ef 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs @@ -57,7 +57,7 @@ namespace Avalonia.Media.Immutable { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Color.Equals(other.Color) && Opacity.Equals(other.Opacity) && (Transform == null && other.Transform == null ? true : Transform.Equals(other.Transform)); + return Color.Equals(other.Color) && Opacity.Equals(other.Opacity) && (Transform == null && other.Transform == null ? true : (Transform != null && Transform.Equals(other.Transform))); } public override bool Equals(object? obj) From 8b2730b0ea859da3852a598998f070af61f4a9bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 20:28:24 +0100 Subject: [PATCH 76/77] Update ApiCompatBaseline.txt --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index ee142fe5a7..c02b9d8639 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -41,6 +41,8 @@ MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphIndices.set( MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphOffsets.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphOffsets.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract. CannotSealType : Type 'Avalonia.Media.Pen' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. MembersMustExist : Member 'protected void Avalonia.Media.Pen.AffectsRender(Avalonia.AvaloniaProperty[])' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Media.Pen.RaiseInvalidated(System.EventArgs)' does not exist in the implementation but it does exist in the contract. @@ -52,6 +54,10 @@ MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsTraili MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.TextPosition.set(System.Int32)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(System.String, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableConicGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableLinearGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableSolidColorBrush..ctor(Avalonia.Media.Color, System.Double)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableTileBrush..ctor(Avalonia.Media.AlignmentX, Avalonia.Media.AlignmentY, Avalonia.RelativeRect, System.Double, Avalonia.RelativeRect, Avalonia.Media.Stretch, Avalonia.Media.TileMode, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' does not exist in the implementation but it does exist in the contract. @@ -155,4 +161,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphR MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice..ctor(System.ReadOnlyMemory, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -Total Issues: 153 +Total Issues: 162 From d2caf1241ff095260c957a7325f26e3c9fe05e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 10 Mar 2022 22:12:46 +0100 Subject: [PATCH 77/77] Add demo page for brushes with transforms --- samples/RenderDemo/MainWindow.xaml | 3 + samples/RenderDemo/Pages/BrushesPage.axaml | 71 +++++++++++++++++++ samples/RenderDemo/Pages/BrushesPage.axaml.cs | 18 +++++ 3 files changed, 92 insertions(+) create mode 100644 samples/RenderDemo/Pages/BrushesPage.axaml create mode 100644 samples/RenderDemo/Pages/BrushesPage.axaml.cs diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index 4a8fb819ca..923b51814f 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -66,5 +66,8 @@ + + + diff --git a/samples/RenderDemo/Pages/BrushesPage.axaml b/samples/RenderDemo/Pages/BrushesPage.axaml new file mode 100644 index 0000000000..9d5b6ee987 --- /dev/null +++ b/samples/RenderDemo/Pages/BrushesPage.axaml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/RenderDemo/Pages/BrushesPage.axaml.cs b/samples/RenderDemo/Pages/BrushesPage.axaml.cs new file mode 100644 index 0000000000..5852b72d9e --- /dev/null +++ b/samples/RenderDemo/Pages/BrushesPage.axaml.cs @@ -0,0 +1,18 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace RenderDemo.Pages; + +public class BrushesPage : UserControl +{ + public BrushesPage() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} +