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 001/820] 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 002/820] 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 003/820] 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 004/820] 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 005/820] 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 006/820] 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 007/820] 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 008/820] 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 009/820] 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 010/820] 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 011/820] 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 012/820] 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 013/820] 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 014/820] 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 ab06e45488b94f2d767def229460283a50281e83 Mon Sep 17 00:00:00 2001 From: RMBGAME Date: Fri, 19 Nov 2021 20:08:06 +0800 Subject: [PATCH 015/820] TrayIcon should be re-added when the Explorer is restarted --- .../Interop/UnmanagedMethods.cs | 85 ++++++++++--------- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 12 ++- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 938f4222e0..9e514461d7 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -903,9 +903,9 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData); - + public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData); - + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); @@ -996,7 +996,7 @@ namespace Avalonia.Win32.Interop public static uint GetWindowLong(IntPtr hWnd, int nIndex) { - if(IntPtr.Size == 4) + if (IntPtr.Size == 4) { return GetWindowLong32b(hWnd, nIndex); } @@ -1023,7 +1023,7 @@ namespace Avalonia.Win32.Interop return (uint)SetWindowLong64b(hWnd, nIndex, new IntPtr((uint)value)).ToInt32(); } } - + public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr handle) { if (IntPtr.Size == 4) @@ -1057,14 +1057,14 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool InvalidateRect(IntPtr hWnd, RECT* lpRect, bool bErase); - - + + [DllImport("user32.dll")] public static extern bool ValidateRect(IntPtr hWnd, IntPtr lpRect); [DllImport("user32.dll")] public static extern bool IsWindow(IntPtr hWnd); - + [DllImport("user32.dll")] public static extern bool IsWindowEnabled(IntPtr hWnd); @@ -1091,22 +1091,25 @@ namespace Avalonia.Win32.Interop [DllImport("user32")] public static extern IntPtr GetMessageExtraInfo(); - + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassExW")] public static extern ushort RegisterClassEx(ref WNDCLASSEX lpwcx); [DllImport("user32.dll")] public static extern void RegisterTouchWindow(IntPtr hWnd, int flags); - + [DllImport("user32.dll")] public static extern bool ReleaseCapture(); + [DllImport("user32.dll", SetLastError = true)] + public static extern uint RegisterWindowMessage(string lpString); + [DllImport("user32.dll")] public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetActiveWindow(); - + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetActiveWindow(IntPtr hWnd); @@ -1282,7 +1285,7 @@ namespace Avalonia.Win32.Interop [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibrary(string fileName); - + [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, int flags); @@ -1326,7 +1329,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern IntPtr MonitorFromWindow(IntPtr hwnd, MONITOR dwFlags); - + [DllImport("user32", EntryPoint = "GetMonitorInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetMonitorInfo([In] IntPtr hMonitor, ref MONITORINFO lpmi); @@ -1334,14 +1337,14 @@ namespace Avalonia.Win32.Interop [DllImport("user32")] public static extern unsafe bool GetTouchInputInfo( IntPtr hTouchInput, - uint cInputs, + uint cInputs, TOUCHINPUT* pInputs, - int cbSize + int cbSize ); - + [DllImport("user32")] public static extern bool CloseTouchInputHandle(IntPtr hTouchInput); - + [return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); @@ -1350,7 +1353,7 @@ namespace Avalonia.Win32.Interop public static extern int SetDIBitsToDevice(IntPtr hdc, int XDest, int YDest, uint dwWidth, uint dwHeight, int XSrc, int YSrc, uint uStartScan, uint cScanLines, IntPtr lpvBits, [In] ref BITMAPINFOHEADER lpbmi, uint fuColorUse); - + [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); @@ -1365,27 +1368,27 @@ namespace Avalonia.Win32.Interop [DllImport("gdi32.dll")] public static extern int ChoosePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd); - + [DllImport("gdi32.dll")] public static extern int DescribePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd); [DllImport("gdi32.dll")] public static extern int SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PixelFormatDescriptor pfd); - - + + [DllImport("gdi32.dll")] public static extern int DescribePixelFormat(IntPtr hdc, int iPixelFormat, int bytes, ref PixelFormatDescriptor pfd); - + [DllImport("gdi32.dll")] public static extern bool SwapBuffers(IntPtr hdc); [DllImport("opengl32.dll")] public static extern IntPtr wglCreateContext(IntPtr hdc); - + [DllImport("opengl32.dll")] public static extern bool wglDeleteContext(IntPtr context); - + [DllImport("opengl32.dll")] public static extern bool wglMakeCurrent(IntPtr hdc, IntPtr context); @@ -1406,9 +1409,9 @@ namespace Avalonia.Win32.Interop uint dwMaximumSizeLow, string lpName); - [DllImport("msvcrt.dll", EntryPoint="memcpy", SetLastError = false, CallingConvention=CallingConvention.Cdecl)] - public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count); - + [DllImport("msvcrt.dll", EntryPoint = "memcpy", SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count); + [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IDropTarget target); @@ -1447,10 +1450,10 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern void DwmFlush(); - + [DllImport("dwmapi.dll")] public static extern bool DwmDefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult); - + [DllImport("dwmapi.dll")] public static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind); @@ -1507,8 +1510,8 @@ namespace Avalonia.Win32.Interop throw new Exception("RtlGetVersion failed!"); } } - - [DllImport("kernel32", EntryPoint="WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)] + + [DllImport("kernel32", EntryPoint = "WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)] private static extern int IntWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable); public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); @@ -1516,7 +1519,7 @@ namespace Avalonia.Win32.Interop internal static int WaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable) { int result = IntWaitForMultipleObjectsEx(nCount, pHandles, bWaitAll, dwMilliseconds, bAlertable); - if(result == WAIT_FAILED) + if (result == WAIT_FAILED) { throw new Win32Exception(); } @@ -1558,7 +1561,7 @@ namespace Avalonia.Win32.Interop DrawLeftBorder = 0x20, DrawTopBorder = 0x40, DrawRightBorder = 0x80, - DrawBottomBorder = 0x100, + DrawBottomBorder = 0x100, } [StructLayout(LayoutKind.Sequential)] @@ -1626,9 +1629,9 @@ namespace Avalonia.Win32.Interop MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI - } + } - public enum ClipboardFormat + public enum ClipboardFormat { /// /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. Use this format for ANSI text. @@ -1679,7 +1682,7 @@ namespace Avalonia.Win32.Interop public int X; public int Y; } - + public struct SIZE { public int X; @@ -1880,7 +1883,7 @@ namespace Avalonia.Win32.Interop OFN_NOREADONLYRETURN = 0x00008000, OFN_OVERWRITEPROMPT = 0x00000002 } - + public enum HRESULT : uint { S_FALSE = 0x0001, @@ -2198,13 +2201,13 @@ namespace Avalonia.Win32.Interop internal interface IDropTarget { [PreserveSig] - UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)][In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)][In] int grfKeyState, [MarshalAs(UnmanagedType.U8)][In] long pt, [In][Out] ref DropEffect pdwEffect); [PreserveSig] - UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)][In] int grfKeyState, [MarshalAs(UnmanagedType.U8)][In] long pt, [In][Out] ref DropEffect pdwEffect); [PreserveSig] UnmanagedMethods.HRESULT DragLeave(); [PreserveSig] - UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)][In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)][In] int grfKeyState, [MarshalAs(UnmanagedType.U8)][In] long pt, [In][Out] ref DropEffect pdwEffect); } [ComImport] @@ -2213,9 +2216,9 @@ namespace Avalonia.Win32.Interop internal interface IDropSource { [PreserveSig] - int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState); + int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)][In] int grfKeyState); [PreserveSig] - int GiveFeedback([MarshalAs(UnmanagedType.U4)] [In] int dwEffect); + int GiveFeedback([MarshalAs(UnmanagedType.U4)][In] int dwEffect); } diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 23395dd9b5..c28ec94fe8 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -24,6 +24,7 @@ namespace Avalonia.Win32 private readonly Win32NativeToManagedMenuExporter _exporter; private static readonly Dictionary s_trayIcons = new Dictionary(); private bool _disposedValue; + private static readonly uint WM_TASKBARCREATED = UnmanagedMethods.RegisterWindowMessage("TaskbarCreated"); public TrayIconImpl() { @@ -44,6 +45,15 @@ namespace Avalonia.Win32 { s_trayIcons[wParam.ToInt32()].WndProc(hWnd, msg, wParam, lParam); } + + if (msg == WM_TASKBARCREATED) + { + foreach (var tray in s_trayIcons.Values) + { + tray.UpdateIcon(true); + tray.UpdateIcon(); + } + } } public void SetIcon(IWindowIconImpl? icon) @@ -145,7 +155,7 @@ namespace Avalonia.Win32 private enum CustomWindowsMessage : uint { WM_TRAYICON = WindowsMessage.WM_APP + 1024, - WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024 + WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024, } private class TrayIconMenuFlyoutPresenter : MenuFlyoutPresenter, IStyleable From 6bb6c0e5c92716521aac4915904f509450966397 Mon Sep 17 00:00:00 2001 From: RMBGAME Date: Fri, 19 Nov 2021 21:55:35 +0800 Subject: [PATCH 016/820] Only re-add visible TrayIcon --- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index c28ec94fe8..86732539f1 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -50,8 +50,11 @@ namespace Avalonia.Win32 { foreach (var tray in s_trayIcons.Values) { - tray.UpdateIcon(true); - tray.UpdateIcon(); + if (tray._iconAdded) + { + tray.UpdateIcon(true); + tray.UpdateIcon(); + } } } } From 11c1543b5a53301cc6d6ff9ae2c74085242566a5 Mon Sep 17 00:00:00 2001 From: Lubomir Tetak Date: Mon, 22 Nov 2021 10:53:25 +0100 Subject: [PATCH 017/820] Handle default actions to prevent beeping sounds --- samples/ControlCatalog/Pages/DialogsPage.xaml.cs | 3 ++- src/Avalonia.Controls/Button.cs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 49921fb7f6..cc1b8e4f5a 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -128,7 +128,8 @@ namespace ControlCatalog.Pages (button = new Button { HorizontalAlignment = HorizontalAlignment.Center, - Content = "Click to close" + Content = "Click to close", + IsDefault = true }) } }, diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 8b22cdd4ec..34180225e0 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -547,6 +547,7 @@ namespace Avalonia.Controls if (e.Key == Key.Enter && IsVisible && IsEnabled) { OnClick(); + e.Handled = true; } } @@ -560,6 +561,7 @@ namespace Avalonia.Controls if (e.Key == Key.Escape && IsVisible && IsEnabled) { OnClick(); + e.Handled = true; } } From 394a5be4026afcf41202d876fb29dcf4beb8ab7c Mon Sep 17 00:00:00 2001 From: Lubomir Tetak Date: Fri, 17 Dec 2021 14:52:22 +0100 Subject: [PATCH 018/820] OSX handle CMD+key up combinations in Avalonia --- native/Avalonia.Native/src/OSX/app.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 79175d9ff1..05b129baca 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -73,6 +73,11 @@ ComPtr _events; _isHandlingSendEvent = true; @try { [super sendEvent: event]; + if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) + { + [[self keyWindow] sendEvent:event]; + } + } @finally { _isHandlingSendEvent = oldHandling; } From a7ace8f57bad9dfdb153348d85311d65a0a9af17 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 15 Dec 2021 14:27:42 +0100 Subject: [PATCH 019/820] Add more platform specific screen from X methods. --- src/Avalonia.Controls/Platform/IScreenImpl.cs | 8 +++ src/Avalonia.Controls/Screens.cs | 51 ++++++++++++++----- src/Avalonia.Controls/Window.cs | 2 +- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 15 ++++++ .../HeadlessPlatformStubs.cs | 15 ++++++ src/Avalonia.Native/ScreenImpl.cs | 15 ++++++ src/Avalonia.X11/X11Screens.cs | 15 ++++++ src/Web/Avalonia.Web.Blazor/WinStubs.cs | 15 ++++++ src/Windows/Avalonia.Win32/ScreenImpl.cs | 39 ++++++++++++++ src/Windows/Avalonia.Win32/WinScreen.cs | 6 ++- 10 files changed, 164 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs index 5bd45057d9..b68391aa52 100644 --- a/src/Avalonia.Controls/Platform/IScreenImpl.cs +++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +#nullable enable + namespace Avalonia.Platform { public interface IScreenImpl @@ -7,5 +9,11 @@ namespace Avalonia.Platform int ScreenCount { get; } IReadOnlyList AllScreens { get; } + + Screen? ScreenFromWindow(IWindowBaseImpl window); + + Screen? ScreenFromPoint(PixelPoint point); + + Screen? ScreenFromRect(PixelRect rect); } } diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs index 8a0a0fa728..786502361c 100644 --- a/src/Avalonia.Controls/Screens.cs +++ b/src/Avalonia.Controls/Screens.cs @@ -20,30 +20,53 @@ namespace Avalonia.Controls _iScreenImpl = iScreenImpl; } - public Screen ScreenFromBounds(PixelRect bounds){ - - Screen currMaxScreen = null; - double maxAreaSize = 0; - foreach (Screen screen in All) + public Screen ScreenFromBounds(PixelRect bounds) + { + Screen currMaxScreen = _iScreenImpl.ScreenFromRect(bounds); + + if (currMaxScreen == null) { - double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double area = (right - left) * (bottom - top); - if (area > maxAreaSize) + double maxAreaSize = 0; + foreach (Screen screen in All) { - maxAreaSize = area; - currMaxScreen = screen; + double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double area = (right - left) * (bottom - top); + if (area > maxAreaSize) + { + maxAreaSize = area; + currMaxScreen = screen; + } } } return currMaxScreen; } + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + var screen = _iScreenImpl.ScreenFromWindow(window); + + if (screen == null && window.Position is { } position) + { + screen = ScreenFromPoint(position); + } + + return screen; + } + public Screen ScreenFromPoint(PixelPoint point) { - return All.FirstOrDefault(x => x.Bounds.Contains(point)); + var screen = _iScreenImpl.ScreenFromPoint(point); + + if (screen == null) + { + screen = All.FirstOrDefault(x => x.Bounds.Contains(point)); + } + + return screen; } public Screen ScreenFromVisual(IVisual visual) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 4c94b725ea..ca325229cc 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -863,7 +863,7 @@ namespace Avalonia.Controls if (WindowStartupLocation == WindowStartupLocation.CenterScreen) { - var screen = Screens.ScreenFromPoint(owner?.Position ?? Position); + var screen = Screens.ScreenFromWindow(owner); if (screen != null) { diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 9dcd4d8e87..b6988a27a6 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -236,5 +236,20 @@ namespace Avalonia.DesignerSupport.Remote public IReadOnlyList AllScreens { get; } = new Screen[] { new Screen(1, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true) }; + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 605659d464..9318ae3d59 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -203,5 +203,20 @@ namespace Avalonia.Headless new Screen(1, new PixelRect(0, 0, 1920, 1280), new PixelRect(0, 0, 1920, 1280), true), }; + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs index 7b4a001486..03f9b438b4 100644 --- a/src/Avalonia.Native/ScreenImpl.cs +++ b/src/Avalonia.Native/ScreenImpl.cs @@ -48,5 +48,20 @@ namespace Avalonia.Native _native?.Dispose(); _native = null; } + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs index bf5c74e0e5..906f4af64e 100644 --- a/src/Avalonia.X11/X11Screens.cs +++ b/src/Avalonia.X11/X11Screens.cs @@ -200,6 +200,21 @@ namespace Avalonia.X11 } + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + public int ScreenCount => _impl.Screens.Length; public IReadOnlyList AllScreens => diff --git a/src/Web/Avalonia.Web.Blazor/WinStubs.cs b/src/Web/Avalonia.Web.Blazor/WinStubs.cs index 7b2bff6bfd..17c1bca138 100644 --- a/src/Web/Avalonia.Web.Blazor/WinStubs.cs +++ b/src/Web/Avalonia.Web.Blazor/WinStubs.cs @@ -55,5 +55,20 @@ namespace Avalonia.Web.Blazor public IReadOnlyList AllScreens { get; } = new[] { new Screen(96, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true) }; + + public Screen? ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen? ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen? ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 442794f0f0..5942208594 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Avalonia.Platform; using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; @@ -70,5 +71,43 @@ namespace Avalonia.Win32 { _allScreens = null; } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + var handle = window.Handle.Handle; + + var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONEAREST); + + return FindScreenByHandle(monitor); + } + + public Screen ScreenFromPoint(PixelPoint point) + { + var monitor = MonitorFromPoint(new POINT + { + X = point.X, + Y = point.Y + }, MONITOR.MONITOR_DEFAULTTONEAREST); + + return FindScreenByHandle(monitor); + } + + public Screen ScreenFromRect(PixelRect rect) + { + var monitor = MonitorFromRect(new RECT + { + left = rect.TopLeft.X, + top = rect.TopLeft.Y, + right = rect.TopRight.X, + bottom = rect.BottomRight.Y + }, MONITOR.MONITOR_DEFAULTTONEAREST); + + return FindScreenByHandle(monitor); + } + + private Screen FindScreenByHandle(IntPtr handle) + { + return AllScreens.Cast().FirstOrDefault(m => m.Handle == handle); + } } } diff --git a/src/Windows/Avalonia.Win32/WinScreen.cs b/src/Windows/Avalonia.Win32/WinScreen.cs index 0cf9fe31db..f103cc3b66 100644 --- a/src/Windows/Avalonia.Win32/WinScreen.cs +++ b/src/Windows/Avalonia.Win32/WinScreen.cs @@ -9,9 +9,11 @@ namespace Avalonia.Win32 public WinScreen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary, IntPtr hMonitor) : base(pixelDensity, bounds, workingArea, primary) { - this._hMonitor = hMonitor; + _hMonitor = hMonitor; } + public IntPtr Handle => _hMonitor; + public override int GetHashCode() { return (int)_hMonitor; @@ -19,7 +21,7 @@ namespace Avalonia.Win32 public override bool Equals(object obj) { - return (obj is WinScreen screen) ? this._hMonitor == screen._hMonitor : base.Equals(obj); + return (obj is WinScreen screen) ? _hMonitor == screen._hMonitor : base.Equals(obj); } } } From 11ebcd176208ff08e47427cf039c901b8c9c6d67 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 15 Dec 2021 14:36:36 +0100 Subject: [PATCH 020/820] Add fallback for window position. --- src/Avalonia.Controls/Window.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index ca325229cc..3923787ab5 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -865,6 +865,11 @@ namespace Avalonia.Controls { var screen = Screens.ScreenFromWindow(owner); + if (screen == null) + { + screen = Screens.ScreenFromPoint(Position); + } + if (screen != null) { Position = screen.WorkingArea.CenterRect(rect).Position; From 6986f0d6689ca0c3bd95e70862c660f1c4a64610 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 3 Jan 2022 12:37:44 +0100 Subject: [PATCH 021/820] Implement screen helper to simply cross platform screens implementation. --- .../Platform/ScreenHelper.cs | 54 ++++++++++++++++ src/Avalonia.Controls/Screens.cs | 64 +++++-------------- src/Avalonia.Controls/Window.cs | 11 +++- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 6 +- .../HeadlessPlatformStubs.cs | 6 +- src/Avalonia.Native/ScreenImpl.cs | 6 +- src/Avalonia.X11/X11Screens.cs | 12 ++-- src/Web/Avalonia.Web.Blazor/WinStubs.cs | 6 +- src/Windows/Avalonia.Win32/ScreenImpl.cs | 6 +- 9 files changed, 100 insertions(+), 71 deletions(-) create mode 100644 src/Avalonia.Controls/Platform/ScreenHelper.cs diff --git a/src/Avalonia.Controls/Platform/ScreenHelper.cs b/src/Avalonia.Controls/Platform/ScreenHelper.cs new file mode 100644 index 0000000000..07affb5ecc --- /dev/null +++ b/src/Avalonia.Controls/Platform/ScreenHelper.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Avalonia.Utilities; + +#nullable enable + +namespace Avalonia.Platform +{ + public static class ScreenHelper + { + public static Screen? ScreenFromPoint(PixelPoint point, IReadOnlyList screens) + { + foreach (Screen screen in screens) + { + if (screen.Bounds.Contains(point)) + { + return screen; + } + } + + return null; + } + + public static Screen? ScreenFromRect(PixelRect bounds, IReadOnlyList screens) + { + Screen? currMaxScreen = null; + double maxAreaSize = 0; + + foreach (Screen screen in screens) + { + double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double area = (right - left) * (bottom - top); + if (area > maxAreaSize) + { + maxAreaSize = area; + currMaxScreen = screen; + } + } + + return currMaxScreen; + } + + public static Screen? ScreenFromWindow(IWindowBaseImpl window, IReadOnlyList screens) + { + var rect = new PixelRect( + window.Position, + PixelSize.FromSize(window.FrameSize ?? window.ClientSize, window.DesktopScaling)); + + return ScreenFromRect(rect, screens); + } + } +} diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs index 786502361c..cf4f360cb5 100644 --- a/src/Avalonia.Controls/Screens.cs +++ b/src/Avalonia.Controls/Screens.cs @@ -2,77 +2,45 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Platform; -using Avalonia.Utilities; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Controls { public class Screens { - private readonly IScreenImpl _iScreenImpl; + private readonly IScreenImpl _impl; - public int ScreenCount => _iScreenImpl.ScreenCount; - public IReadOnlyList All => _iScreenImpl?.AllScreens ?? Array.Empty(); - public Screen Primary => All.FirstOrDefault(x => x.Primary); + public int ScreenCount => _impl.ScreenCount; + public IReadOnlyList All => _impl?.AllScreens ?? Array.Empty(); + public Screen? Primary => All.FirstOrDefault(x => x.Primary); - public Screens(IScreenImpl iScreenImpl) + public Screens(IScreenImpl impl) { - _iScreenImpl = iScreenImpl; + _impl = impl; } - public Screen ScreenFromBounds(PixelRect bounds) + public Screen? ScreenFromBounds(PixelRect bounds) { - Screen currMaxScreen = _iScreenImpl.ScreenFromRect(bounds); - - if (currMaxScreen == null) - { - double maxAreaSize = 0; - foreach (Screen screen in All) - { - double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double area = (right - left) * (bottom - top); - if (area > maxAreaSize) - { - maxAreaSize = area; - currMaxScreen = screen; - } - } - } - - return currMaxScreen; + return _impl.ScreenFromRect(bounds); } - public Screen ScreenFromWindow(IWindowBaseImpl window) + public Screen? ScreenFromWindow(IWindowBaseImpl window) { - var screen = _iScreenImpl.ScreenFromWindow(window); - - if (screen == null && window.Position is { } position) - { - screen = ScreenFromPoint(position); - } - - return screen; + return _impl.ScreenFromWindow(window); } - public Screen ScreenFromPoint(PixelPoint point) + public Screen? ScreenFromPoint(PixelPoint point) { - var screen = _iScreenImpl.ScreenFromPoint(point); - - if (screen == null) - { - screen = All.FirstOrDefault(x => x.Bounds.Contains(point)); - } - - return screen; + return _impl.ScreenFromPoint(point); } - public Screen ScreenFromVisual(IVisual visual) + public Screen? ScreenFromVisual(IVisual visual) { var tl = visual.PointToScreen(visual.Bounds.TopLeft); var br = visual.PointToScreen(visual.Bounds.BottomRight); + return ScreenFromBounds(new PixelRect(tl, br)); } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 3923787ab5..10e4b65982 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -863,9 +863,16 @@ namespace Avalonia.Controls if (WindowStartupLocation == WindowStartupLocation.CenterScreen) { - var screen = Screens.ScreenFromWindow(owner); + Screen screen = null; - if (screen == null) + if (owner is not null) + { + screen = Screens.ScreenFromWindow(owner); + + screen ??= Screens.ScreenFromPoint(owner.Position); + } + + if (screen is null) { screen = Screens.ScreenFromPoint(Position); } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index b6988a27a6..ab1b918656 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -239,17 +239,17 @@ namespace Avalonia.DesignerSupport.Remote public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 9318ae3d59..6b97056882 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -206,17 +206,17 @@ namespace Avalonia.Headless public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs index 03f9b438b4..83db2e8a28 100644 --- a/src/Avalonia.Native/ScreenImpl.cs +++ b/src/Avalonia.Native/ScreenImpl.cs @@ -51,17 +51,17 @@ namespace Avalonia.Native public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs index 906f4af64e..bcaafb6a53 100644 --- a/src/Avalonia.X11/X11Screens.cs +++ b/src/Avalonia.X11/X11Screens.cs @@ -200,19 +200,19 @@ namespace Avalonia.X11 } - public Screen ScreenFromWindow(IWindowBaseImpl window) + public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } - public Screen ScreenFromPoint(PixelPoint point) + public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } - public Screen ScreenFromRect(PixelRect rect) + public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } public int ScreenCount => _impl.Screens.Length; diff --git a/src/Web/Avalonia.Web.Blazor/WinStubs.cs b/src/Web/Avalonia.Web.Blazor/WinStubs.cs index 17c1bca138..7c30a96d35 100644 --- a/src/Web/Avalonia.Web.Blazor/WinStubs.cs +++ b/src/Web/Avalonia.Web.Blazor/WinStubs.cs @@ -58,17 +58,17 @@ namespace Avalonia.Web.Blazor public Screen? ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen? ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen? ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 5942208594..96e45927da 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -76,7 +76,7 @@ namespace Avalonia.Win32 { var handle = window.Handle.Handle; - var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONEAREST); + var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } @@ -87,7 +87,7 @@ namespace Avalonia.Win32 { X = point.X, Y = point.Y - }, MONITOR.MONITOR_DEFAULTTONEAREST); + }, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } @@ -100,7 +100,7 @@ namespace Avalonia.Win32 top = rect.TopLeft.Y, right = rect.TopRight.X, bottom = rect.BottomRight.Y - }, MONITOR.MONITOR_DEFAULTTONEAREST); + }, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } From 84ead6d0f4b8ebfba4c22423e54ca90fc128eb09 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 15 Jan 2022 00:24:48 +0100 Subject: [PATCH 022/820] Update ApiCompatBaseline.txt --- src/Avalonia.Controls/ApiCompatBaseline.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 9b7d37e108..b019645131 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -44,6 +44,9 @@ MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaPro InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromRect(Avalonia.PixelRect)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromWindow(Avalonia.Platform.IWindowBaseImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Action Avalonia.Platform.ITopLevelImpl.Resized.get()' is present in the implementation but not in the contract. @@ -62,4 +65,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: 63 +Total Issues: 66 From 213cc3429b7d91a2e84c1c2978a28d82ccb2f1ab Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:27:34 +0300 Subject: [PATCH 023/820] Visual now uses WeakEvent too --- src/Avalonia.Visuals/Visual.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 324b253a0f..41c9f76dbc 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -91,11 +91,17 @@ namespace Avalonia /// public static readonly StyledProperty ZIndexProperty = AvaloniaProperty.Register(nameof(ZIndex)); + + private static readonly WeakEvent InvalidatedWeakEvent = + WeakEvent.Register( + (s, h) => s.Invalidated += h, + (s, h) => s.Invalidated -= h); private Rect _bounds; private TransformedBounds? _transformedBounds; private IRenderRoot? _visualRoot; private IVisual? _visualParent; + private WeakEventSubscriber? _affectsRenderWeakSubscriber; /// /// Initializes static members of the class. @@ -352,12 +358,21 @@ namespace Avalonia { if (e.OldValue is IAffectsRender oldValue) { - WeakEventHandlerManager.Unsubscribe(oldValue, nameof(oldValue.Invalidated), sender.AffectsRenderInvalidated); + if (sender._affectsRenderWeakSubscriber != null) + InvalidatedWeakEvent.Unsubscribe(oldValue, sender._affectsRenderWeakSubscriber); } if (e.NewValue is IAffectsRender newValue) { - WeakEventHandlerManager.Subscribe(newValue, nameof(newValue.Invalidated), sender.AffectsRenderInvalidated); + if (sender._affectsRenderWeakSubscriber == null) + { + sender._affectsRenderWeakSubscriber = new WeakEventSubscriber(); + sender._affectsRenderWeakSubscriber.Event += delegate + { + sender.InvalidateVisual(); + }; + } + InvalidatedWeakEvent.Subscribe(newValue, sender._affectsRenderWeakSubscriber); } sender.InvalidateVisual(); @@ -608,8 +623,6 @@ namespace Avalonia OnVisualParentChanged(old, value); } - private void AffectsRenderInvalidated(object? sender, EventArgs e) => InvalidateVisual(); - /// /// Called when the collection changes. /// From 7e1d9dbc72eb9c6371f8bf33800caab19916cf20 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:28:49 +0300 Subject: [PATCH 024/820] Optimized WeakEvent (9332ms to 5ms using #6660 bench) --- src/Avalonia.Base/Utilities/WeakEvent.cs | 85 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 0b32015a8a..21c165afae 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -36,7 +37,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { if (!_subscriptions.TryGetValue(target, out var subscription)) _subscriptions.Add(target, subscription = new Subscription(this, target)); - subscription.Add(new WeakReference>(subscriber)); + subscription.Add(subscriber); } public void Unsubscribe(TSender target, IWeakEventSubscriber subscriber) @@ -51,11 +52,61 @@ public class WeakEvent : WeakEvent where TEventArgs : Event private readonly TSender _target; private readonly Action _compact; - private WeakReference>?[] _data = - new WeakReference>[16]; + struct Entry + { + WeakReference>? _reference; + int _hashCode; + + public Entry(IWeakEventSubscriber r) + { + if (r == null) + { + _reference = null; + _hashCode = 0; + return; + } + + _hashCode = r.GetHashCode(); + _reference = new WeakReference>(r); + } + + public bool IsEmpty + { + get + { + if (_reference == null) + return false; + if (_reference.TryGetTarget(out var target)) + return true; + _reference = null; + return false; + } + } + + public bool TryGetTarget([MaybeNullWhen(false)]out IWeakEventSubscriber target) + { + if (_reference == null) + { + target = null!; + return false; + } + return _reference.TryGetTarget(out target); + } + + public bool Equals(IWeakEventSubscriber r) + { + if (_reference == null || r.GetHashCode() != _hashCode) + return false; + return _reference.TryGetTarget(out var target) && target == r; + } + } + + private Entry[] _data = + new Entry[16]; private int _count; private readonly Action _unsubscribe; private bool _compactScheduled; + private int _removedSinceLastCompact; public Subscription(WeakEvent ev, TSender target) { @@ -71,17 +122,17 @@ public class WeakEvent : WeakEvent where TEventArgs : Event _ev._subscriptions.Remove(_target); } - public void Add(WeakReference> s) + public void Add(IWeakEventSubscriber s) { if (_count == _data.Length) { //Extend capacity - var extendedData = new WeakReference>?[_data.Length * 2]; + var extendedData = new Entry[_data.Length * 2]; Array.Copy(_data, extendedData, _data.Length); _data = extendedData; } - _data[_count] = s; + _data[_count] = new(s); _count++; } @@ -93,16 +144,21 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { var reference = _data[c]; - if (reference != null && reference.TryGetTarget(out var instance) && instance == s) + if (reference.Equals(s)) { - _data[c] = null; + _data[c] = default; removed = true; + break; } } if (removed) { + _removedSinceLastCompact++; ScheduleCompact(); + + if (_removedSinceLastCompact > 500) + Compact(); } } @@ -116,18 +172,21 @@ public class WeakEvent : WeakEvent where TEventArgs : Event void Compact() { + if(!_compactScheduled || _removedSinceLastCompact == 0) + return; _compactScheduled = false; + _removedSinceLastCompact = 0; int empty = -1; for (var c = 0; c < _count; c++) { - var r = _data[c]; + ref var r = ref _data[c]; //Mark current index as first empty - if (r == null && empty == -1) + if (r.IsEmpty && empty == -1) empty = c; //If current element isn't null and we have an empty one - if (r != null && empty != -1) + if (!r.IsEmpty && empty != -1) { - _data[c] = null; + _data[c] = default; _data[empty] = r; empty++; } @@ -145,7 +204,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event for (var c = 0; c < _count; c++) { var r = _data[c]; - if (r?.TryGetTarget(out var sub) == true) + if (r.TryGetTarget(out var sub)) sub!.OnEvent(_target, _ev, eventArgs); else needCompact = true; From dca2ca5940f0c768529f97b89535ed172ef44403 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:29:14 +0300 Subject: [PATCH 025/820] Don't implement IWeakEventSubscriber directly on public types --- .../Utilities/IWeakEventSubscriber.cs | 10 +++++++ .../Repeater/ItemsRepeater.cs | 30 ++++++++++--------- src/Avalonia.Controls/TopLevel.cs | 16 +++++----- src/Avalonia.Visuals/Media/Pen.cs | 25 +++++++++------- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs index e48c0cb111..2a24376592 100644 --- a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs @@ -9,4 +9,14 @@ namespace Avalonia.Utilities; public interface IWeakEventSubscriber where TEventArgs : EventArgs { void OnEvent(object? sender, WeakEvent ev, TEventArgs e); +} + +public class WeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs +{ + public event Action? Event; + + void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, TEventArgs e) + { + + } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index b40cf26df5..6d2f4144eb 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -20,7 +20,7 @@ namespace Avalonia.Controls /// Represents a data-driven collection control that incorporates a flexible layout system, /// custom views, and virtualization. /// - public class ItemsRepeater : Panel, IChildIndexProvider, IWeakEventSubscriber + public class ItemsRepeater : Panel, IChildIndexProvider { /// /// Defines the property. @@ -68,6 +68,7 @@ namespace Avalonia.Controls private ItemsRepeaterElementPreparedEventArgs _elementPreparedArgs; private ItemsRepeaterElementClearingEventArgs _elementClearingArgs; private ItemsRepeaterElementIndexChangedEventArgs _elementIndexChangedArgs; + private WeakEventSubscriber _layoutWeakSubscriber = new(); /// /// Initializes a new instance of the class. @@ -77,6 +78,15 @@ namespace Avalonia.Controls _viewManager = new ViewManager(this); _viewportManager = new ViewportManager(this); KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once); + + _layoutWeakSubscriber.Event += (_, ev, e) => + { + if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent) + InvalidateArrange(); + else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) + InvalidateMeasure(); + }; + OnLayoutChanged(null, Layout); } @@ -723,8 +733,8 @@ namespace Avalonia.Controls { oldValue.UninitializeForContext(LayoutContext); - AttachedLayout.MeasureInvalidatedWeakEvent.Unsubscribe(oldValue, this); - AttachedLayout.ArrangeInvalidatedWeakEvent.Unsubscribe(oldValue, this); + AttachedLayout.MeasureInvalidatedWeakEvent.Unsubscribe(oldValue, _layoutWeakSubscriber); + AttachedLayout.ArrangeInvalidatedWeakEvent.Unsubscribe(oldValue, _layoutWeakSubscriber); // Walk through all the elements and make sure they are cleared foreach (var element in Children) @@ -742,8 +752,8 @@ namespace Avalonia.Controls { newValue.InitializeForContext(LayoutContext); - AttachedLayout.MeasureInvalidatedWeakEvent.Subscribe(newValue, this); - AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, this); + AttachedLayout.MeasureInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber); + AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber); } bool isVirtualizingLayout = newValue != null && newValue is VirtualizingLayout; @@ -793,15 +803,7 @@ namespace Avalonia.Controls { _viewportManager.OnBringIntoViewRequested(e); } - - void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, EventArgs e) - { - if(ev == AttachedLayout.ArrangeInvalidatedWeakEvent) - InvalidateArrange(); - else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) - InvalidateMeasure(); - } - + private VirtualizingLayoutContext GetLayoutContext() { if (_layoutContext == null) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index eaee5bdb50..76de113290 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -33,8 +33,7 @@ namespace Avalonia.Controls ICloseable, IStyleHost, ILogicalRoot, - ITextInputMethodRoot, - IWeakEventSubscriber + ITextInputMethodRoot { /// /// Defines the property. @@ -90,6 +89,7 @@ namespace Avalonia.Controls private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager _layoutManager; private Border _transparencyFallbackBorder; + private WeakEventSubscriber _resourcesChangesSubscriber; /// /// Initializes static members of the class. @@ -184,7 +184,12 @@ namespace Avalonia.Controls if (((IStyleHost)this).StylingParent is IResourceHost applicationResources) { - ResourcesChangedWeakEvent.Subscribe(applicationResources, this); + _resourcesChangesSubscriber = new(); + _resourcesChangesSubscriber.Event += (_, __, e) => + { + ((ILogical)this).NotifyResourcesChanged(e); + }; + ResourcesChangedWeakEvent.Subscribe(applicationResources, _resourcesChangesSubscriber); } impl.LostFocus += PlatformImpl_LostFocus; @@ -289,11 +294,6 @@ namespace Avalonia.Controls /// IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; - void IWeakEventSubscriber.OnEvent(object sender, WeakEvent ev, ResourcesChangedEventArgs e) - { - ((ILogical)this).NotifyResourcesChanged(e); - } - /// /// Gets or sets a value indicating whether access keys are shown in the window. /// diff --git a/src/Avalonia.Visuals/Media/Pen.cs b/src/Avalonia.Visuals/Media/Pen.cs index 65ba851100..f0a0d24248 100644 --- a/src/Avalonia.Visuals/Media/Pen.cs +++ b/src/Avalonia.Visuals/Media/Pen.cs @@ -7,7 +7,7 @@ namespace Avalonia.Media /// /// Describes how a stroke is drawn. /// - public sealed class Pen : AvaloniaObject, IPen, IWeakEventSubscriber + public sealed class Pen : AvaloniaObject, IPen { /// /// Defines the property. @@ -48,7 +48,8 @@ namespace Avalonia.Media private EventHandler? _invalidated; private IAffectsRender? _subscribedToBrush; private IAffectsRender? _subscribedToDashes; - + private WeakEventSubscriber? _weakSubscriber; + /// /// Initializes a new instance of the class. /// @@ -207,13 +208,23 @@ namespace Avalonia.Media { if ((_invalidated == null || field != value) && field != null) { - InvalidatedWeakEvent.Unsubscribe(field, this); + if (_weakSubscriber != null) + InvalidatedWeakEvent.Unsubscribe(field, _weakSubscriber); field = null; } if (_invalidated != null && field != value && value is IAffectsRender affectsRender) { - InvalidatedWeakEvent.Subscribe(affectsRender, this); + if (_weakSubscriber == null) + { + _weakSubscriber = new WeakEventSubscriber(); + _weakSubscriber.Event += (_, ev, __) => + { + if (ev == InvalidatedWeakEvent) + _invalidated?.Invoke(this, EventArgs.Empty); + }; + } + InvalidatedWeakEvent.Subscribe(affectsRender, _weakSubscriber); field = affectsRender; } } @@ -223,11 +234,5 @@ namespace Avalonia.Media UpdateSubscription(ref _subscribedToBrush, Brush); UpdateSubscription(ref _subscribedToDashes, DashStyle); } - - void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, EventArgs e) - { - if (ev == InvalidatedWeakEvent) - _invalidated?.Invoke(this, EventArgs.Empty); - } } } From bb9f5e75f33f460dcb729678b2c3a8a41fc81fb0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:55:46 +0300 Subject: [PATCH 026/820] Fixed WeakEvent Compact --- src/Avalonia.Base/Utilities/WeakEvent.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 21c165afae..5d5a1cb55b 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -75,7 +75,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event get { if (_reference == null) - return false; + return true; if (_reference.TryGetTarget(out var target)) return true; _reference = null; @@ -179,15 +179,15 @@ public class WeakEvent : WeakEvent where TEventArgs : Event int empty = -1; for (var c = 0; c < _count; c++) { - ref var r = ref _data[c]; + ref var currentRef = ref _data[c]; //Mark current index as first empty - if (r.IsEmpty && empty == -1) + if (currentRef.IsEmpty && empty == -1) empty = c; //If current element isn't null and we have an empty one - if (!r.IsEmpty && empty != -1) + if (!currentRef.IsEmpty && empty != -1) { - _data[c] = default; - _data[empty] = r; + _data[empty] = currentRef; + currentRef = default; empty++; } } From c73fcd25a9cfcaa17e7295b6b01080c585fe3c78 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 20:09:24 +0300 Subject: [PATCH 027/820] Fixed tests --- src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs | 2 +- src/Avalonia.Base/Utilities/WeakEvent.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs index 2a24376592..57060853c8 100644 --- a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs @@ -17,6 +17,6 @@ public class WeakEventSubscriber : IWeakEventSubscriber void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, TEventArgs e) { - + Event?.Invoke(sender, ev, e); } } \ No newline at end of file diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 5d5a1cb55b..bcee7f89f7 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -76,10 +76,10 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { if (_reference == null) return true; - if (_reference.TryGetTarget(out var target)) - return true; + if (_reference.TryGetTarget(out _)) + return false; _reference = null; - return false; + return true; } } From a4fa74977f2a9c7c8cb6fc1f8b86c88f00543463 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 20:17:56 +0300 Subject: [PATCH 028/820] Removed immediate compact since it makes things worse --- src/Avalonia.Base/Utilities/WeakEvent.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index bcee7f89f7..b27ea9f455 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -106,7 +106,6 @@ public class WeakEvent : WeakEvent where TEventArgs : Event private int _count; private readonly Action _unsubscribe; private bool _compactScheduled; - private int _removedSinceLastCompact; public Subscription(WeakEvent ev, TSender target) { @@ -154,11 +153,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event if (removed) { - _removedSinceLastCompact++; ScheduleCompact(); - - if (_removedSinceLastCompact > 500) - Compact(); } } @@ -172,10 +167,9 @@ public class WeakEvent : WeakEvent where TEventArgs : Event void Compact() { - if(!_compactScheduled || _removedSinceLastCompact == 0) + if(!_compactScheduled) return; _compactScheduled = false; - _removedSinceLastCompact = 0; int empty = -1; for (var c = 0; c < _count; c++) { From 8103f2a0b1c7dbe5f0c620229442ce8a7fe619d3 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 23:47:12 +0300 Subject: [PATCH 029/820] Use Dictionary for more than 8 WeakEvent subscribers --- src/Avalonia.Base/Utilities/WeakEvent.cs | 85 ++----- src/Avalonia.Base/Utilities/WeakHashList.cs | 236 ++++++++++++++++++++ 2 files changed, 258 insertions(+), 63 deletions(-) create mode 100644 src/Avalonia.Base/Utilities/WeakHashList.cs diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index b27ea9f455..1335d7e9b8 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -101,11 +101,10 @@ public class WeakEvent : WeakEvent where TEventArgs : Event } } - private Entry[] _data = - new Entry[16]; - private int _count; private readonly Action _unsubscribe; + private readonly WeakHashList> _list = new(); private bool _compactScheduled; + private bool _destroyed; public Subscription(WeakEvent ev, TSender target) { @@ -117,49 +116,27 @@ public class WeakEvent : WeakEvent where TEventArgs : Event void Destroy() { + if(_destroyed) + return; + _destroyed = true; _unsubscribe(); _ev._subscriptions.Remove(_target); } - public void Add(IWeakEventSubscriber s) - { - if (_count == _data.Length) - { - //Extend capacity - var extendedData = new Entry[_data.Length * 2]; - Array.Copy(_data, extendedData, _data.Length); - _data = extendedData; - } - - _data[_count] = new(s); - _count++; - } + public void Add(IWeakEventSubscriber s) => _list.Add(s); public void Remove(IWeakEventSubscriber s) { - var removed = false; - - for (int c = 0; c < _count; ++c) - { - var reference = _data[c]; - - if (reference.Equals(s)) - { - _data[c] = default; - removed = true; - break; - } - } - - if (removed) - { + _list.Remove(s); + if(_list.IsEmpty) + Destroy(); + else if(_list.NeedCompact && _compactScheduled) ScheduleCompact(); - } } void ScheduleCompact() { - if(_compactScheduled) + if(_compactScheduled || _destroyed) return; _compactScheduled = true; Dispatcher.UIThread.Post(_compact, DispatcherPriority.Background); @@ -170,42 +147,24 @@ public class WeakEvent : WeakEvent where TEventArgs : Event if(!_compactScheduled) return; _compactScheduled = false; - int empty = -1; - for (var c = 0; c < _count; c++) - { - ref var currentRef = ref _data[c]; - //Mark current index as first empty - if (currentRef.IsEmpty && empty == -1) - empty = c; - //If current element isn't null and we have an empty one - if (!currentRef.IsEmpty && empty != -1) - { - _data[empty] = currentRef; - currentRef = default; - empty++; - } - } - - if (empty != -1) - _count = empty; - if (_count == 0) + _list.Compact(); + if (_list.IsEmpty) Destroy(); } void OnEvent(object? sender, TEventArgs eventArgs) { - var needCompact = false; - for (var c = 0; c < _count; c++) + var alive = _list.GetAlive(); + if(alive == null) + Destroy(); + else { - var r = _data[c]; - if (r.TryGetTarget(out var sub)) - sub!.OnEvent(_target, _ev, eventArgs); - else - needCompact = true; + foreach(var item in alive) + item.OnEvent(_target, _ev, eventArgs); + WeakHashList>.ReturnToSharedPool(alive); + if(_list.NeedCompact && !_compactScheduled) + ScheduleCompact(); } - - if (needCompact) - ScheduleCompact(); } } diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs new file mode 100644 index 0000000000..32668872da --- /dev/null +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using Avalonia.Collections.Pooled; + +namespace Avalonia.Utilities; + +internal class WeakHashList where T : class +{ + private struct Key + { + public WeakReference? Weak; + public T? Strong; + public int HashCode; + + public static Key MakeStrong(T r) => new() + { + HashCode = r.GetHashCode(), + Strong = r + }; + + public static Key MakeWeak(T r) => new() + { + HashCode = r.GetHashCode(), + Weak = new WeakReference(r) + }; + + public override int GetHashCode() => HashCode; + } + + class KeyComparer : IEqualityComparer + { + public bool Equals(Key x, Key y) + { + if (x.HashCode != y.HashCode) + return false; + if (x.Strong != null) + { + if (y.Strong != null) + return x.Strong == y.Strong; + if (y.Weak == null) + return false; + return y.Weak.TryGetTarget(out var weakTarget) && weakTarget == x.Strong; + } + else if (y.Strong != null) + { + if (x.Weak == null) + return false; + return x.Weak.TryGetTarget(out var weakTarget) && weakTarget == y.Strong; + } + else + { + if (x.Weak == null || x.Weak.TryGetTarget(out var xTarget) == false) + return y.Weak?.TryGetTarget(out _) != true; + return y.Weak?.TryGetTarget(out var yTarget) == true && xTarget == yTarget; + } + } + + public int GetHashCode(Key obj) => obj.HashCode; + public static KeyComparer Instance = new(); + } + + Dictionary? _dic; + WeakReference?[]? _arr; + int _arrCount; + + public bool IsEmpty => _dic == null || _dic.Count == 0; + public bool NeedCompact { get; private set; } + + public void Add(T item) + { + if (_dic != null) + { + var strongKey = Key.MakeStrong(item); + if (_dic.TryGetValue(strongKey, out var cnt)) + _dic[strongKey] = cnt + 1; + else + _dic[Key.MakeWeak(item)] = 1; + return; + } + + if (_arr == null) + _arr = new WeakReference[8]; + + if (_arrCount < _arr.Length) + { + _arr[_arrCount] = new WeakReference(item); + _arrCount++; + return; + } + + // Check if something is dead + for (var c = 0; c < _arrCount; c++) + { + if (_arr[c]!.TryGetTarget(out _) == false) + { + _arr[c] = new WeakReference(item); + return; + } + } + + _dic = new Dictionary(KeyComparer.Instance); + foreach (var existing in _arr) + { + if (existing!.TryGetTarget(out var target)) + Add(target); + } + _arr = null; + + } + + public void Remove(T item) + { + if (_arr != null) + { + for (var c = 0; c < _arr.Length; c++) + { + if (_arr[c]?.TryGetTarget(out var target) == true && target == item) + { + _arr[c] = null; + Compact(); + return; + } + } + } + else if (_dic != null) + { + var strongKey = Key.MakeStrong(item); + + if (_dic.TryGetValue(strongKey, out var cnt)) + { + if (cnt > 1) + { + _dic[strongKey] = cnt - 1; + return; + } + } + + _dic.Remove(strongKey); + } + } + + private void ArrCompact() + { + if (_arr != null) + { + int empty = -1; + for (var c = 0; c < _arrCount; c++) + { + var r = _arr[c]; + //Mark current index as first empty + if (r == null && empty == -1) + empty = c; + //If current element isn't null and we have an empty one + if (r != null && empty != -1) + { + _arr[c] = null; + _arr[empty] = r; + empty++; + } + } + + if (empty != -1) + _arrCount = empty; + } + } + + public void Compact() + { + if (_dic != null) + { + PooledList? toRemove = null; + foreach (var kvp in _dic) + { + if (kvp.Key.Weak?.TryGetTarget(out _) != true) + (toRemove ??= new PooledList()).Add(kvp.Key); + } + + if (toRemove != null) + { + foreach (var k in toRemove) + _dic.Remove(k); + toRemove.Dispose(); + } + } + } + + private static readonly Stack> s_listPool = new(); + + public static void ReturnToSharedPool(PooledList list) + { + list.Clear(); + s_listPool.Push(list); + } + + public PooledList? GetAlive(Func>? factory = null) + { + PooledList? pooled = null; + if (_arr != null) + { + bool needCompact = false; + for (var c = 0; c < _arrCount; c++) + { + if (_arr[c]?.TryGetTarget(out var target) == true) + (pooled ??= factory?.Invoke() + ?? (s_listPool.Count > 0 + ? s_listPool.Pop() + : new PooledList())).Add(target!); + else + { + _arr[c] = null; + needCompact = true; + } + } + if(needCompact) + ArrCompact(); + return pooled; + } + if (_dic != null) + { + + foreach (var kvp in _dic) + { + if (kvp.Key.Weak?.TryGetTarget(out var target) == true) + (pooled ??= factory?.Invoke() + ?? (s_listPool.Count > 0 + ? s_listPool.Pop() + : new PooledList())) + .Add(target!); + else + NeedCompact = true; + } + } + + return pooled; + } +} \ No newline at end of file From 27ecb055086d437516ec6685fc22016d77ebed94 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 17 Jan 2022 11:38:20 +0300 Subject: [PATCH 030/820] Use PooledList.Span for enumeration --- src/Avalonia.Base/Utilities/WeakEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 1335d7e9b8..e72606bf70 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -159,7 +159,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event Destroy(); else { - foreach(var item in alive) + foreach(var item in alive.Span) item.OnEvent(_target, _ev, eventArgs); WeakHashList>.ReturnToSharedPool(alive); if(_list.NeedCompact && !_compactScheduled) From f41a6340f908e189204104385cd73e6e614da125 Mon Sep 17 00:00:00 2001 From: Andreas Schauerte Date: Mon, 5 Jul 2021 19:11:36 +0200 Subject: [PATCH 031/820] Cherry Pick and merge. --- src/Avalonia.Visuals/Matrix.cs | 192 +++++++++++++++++++++++++-------- 1 file changed, 145 insertions(+), 47 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index b08a0eb98a..9dd5bced86 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -1,12 +1,21 @@ using System; using System.Globalization; +using System.Linq; using Avalonia.Utilities; namespace Avalonia { /// - /// A 2x3 matrix. + /// A 3x3 matrix. /// + /// Matrix layout: + /// | 1st col | 2nd col | 3r col | + /// 1st row | scaleX | skrewY | persX | + /// 2nd row | skrewX | scaleY | persY | + /// 3rd row | transX | transY | persZ | + /// + /// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and persX/transX are swapped). + /// #if !BUILDTASK public #endif @@ -14,40 +23,77 @@ namespace Avalonia { private readonly double _m11; private readonly double _m12; + private readonly double _m13; private readonly double _m21; private readonly double _m22; + private readonly double _m23; private readonly double _m31; private readonly double _m32; + private readonly double _m33; + + + /// + /// Initializes a new instance of the struct (equivalent to a 2x3 Matrix without perspective). + /// + /// The first element of the first row. + /// The second element of the first row. + /// The first element of the second row. + /// The second element of the second row. + /// The first element of the third row. + /// The second element of the third row. + public Matrix( + double scaleX, + double skrewY, + double skrewX, + double scaleY, + double offsetX, + double offsetY) : this( scaleX, skrewY, 0, skrewX, scaleY, 0, offsetX, offsetY, 1) + { + } + + /// /// Initializes a new instance of the struct. /// - /// The first element of the first row. - /// The second element of the first row. - /// The first element of the second row. - /// The second element of the second row. + /// The first element of the first row. + /// The second element of the first row. + /// The third element of the first row. + /// The first element of the second row. + /// The second element of the second row. + /// The third element of the second row. /// The first element of the third row. /// The second element of the third row. + /// The third element of the third row. public Matrix( - double m11, - double m12, - double m21, - double m22, + double scaleX, + double skrewY, + double persX, + double skrewX, + double scaleY, + double persY, double offsetX, - double offsetY) + double offsetY, + double persZ) { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; + _m11 = scaleX; + _m12 = skrewY; + _m13 = persX; + _m21 = skrewX; + _m22 = scaleY; + _m23 = persY; _m31 = offsetX; _m32 = offsetY; + _m33 = persZ; } /// /// Returns the multiplicative identity matrix. /// - public static Matrix Identity { get; } = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + public static Matrix Identity { get; } = new Matrix( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); /// /// Returns whether the matrix is the identity matrix. @@ -60,35 +106,50 @@ namespace Avalonia public bool HasInverse => !MathUtilities.IsZero(GetDeterminant()); /// - /// The first element of the first row + /// The first element of the first row (scaleX). /// public double M11 => _m11; /// - /// The second element of the first row + /// The second element of the first row (skrewY). /// public double M12 => _m12; /// - /// The first element of the second row + /// The third element of the first row (persX: input x-axis perspective factor). + /// + public double M13 => _m13; + + /// + /// The first element of the second row (skrewX). /// public double M21 => _m21; /// - /// The second element of the second row + /// The second element of the second row (scaleY). /// public double M22 => _m22; /// - /// The first element of the third row + /// The third element of the second row (persY: input y-axis perspective factor). + /// + public double M23 => _m23; + + /// + /// The first element of the third row (offsetX/translateX). /// public double M31 => _m31; /// - /// The second element of the third row + /// The second element of the third row (offsetY/translateY). /// public double M32 => _m32; + /// + /// The third element of the third row (persZ: perspective scale factor). + /// + public double M33 => _m33; + /// /// Multiplies two matrices together and returns the resulting matrix. /// @@ -103,7 +164,7 @@ namespace Avalonia (value1.M21 * value2.M11) + (value1.M22 * value2.M21), (value1.M21 * value2.M12) + (value1.M22 * value2.M22), (value1._m31 * value2.M11) + (value1._m32 * value2.M21) + value2._m31, - (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); + (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); //TODO: include perspective } /// @@ -171,7 +232,7 @@ namespace Avalonia /// A scaling matrix. public static Matrix CreateScale(double xScale, double yScale) { - return CreateScale(new Vector(xScale, yScale)); + return new Matrix(xScale, 0, 0, yScale, 0, 0); } /// @@ -181,7 +242,7 @@ namespace Avalonia /// A scaling matrix. public static Matrix CreateScale(Vector scales) { - return new Matrix(scales.X, 0, 0, scales.Y, 0, 0); + return CreateScale(scales.X, scales.Y); } /// @@ -247,7 +308,12 @@ namespace Avalonia /// public double GetDeterminant() { - return (_m11 * _m22) - (_m12 * _m21); + //return (_m11 * _m22) - (_m12 * _m21); //TODO: ensure new implementation yields the same result as before, when pers is 0,0,1 + + // implemented using "Laplace expansion": + return _m11 * (_m22 * _m33 - _m23 * _m32) + - _m12 * (_m21 * _m33 - _m23 * _m31) + + _m13 * (_m21 * _m32 - _m22 * _m31); } /// @@ -260,10 +326,13 @@ namespace Avalonia // ReSharper disable CompareOfFloatsByEqualityOperator return _m11 == other.M11 && _m12 == other.M12 && + _m13 == other.M13 && _m21 == other.M21 && _m22 == other.M22 && + _m23 == other.M23 && _m31 == other.M31 && - _m32 == other.M32; + _m32 == other.M32 && + _m33 == other.M33; // ReSharper restore CompareOfFloatsByEqualityOperator } @@ -280,9 +349,18 @@ namespace Avalonia /// The hash code. public override int GetHashCode() { - return M11.GetHashCode() + M12.GetHashCode() + - M21.GetHashCode() + M22.GetHashCode() + - M31.GetHashCode() + M32.GetHashCode(); + return (_m11, _m12, _m13, _m21, _m22, _m23, _m31, _m32, _m33).GetHashCode(); + } + + /// + /// Determines if the current matrix contains perspective (non-affine) transforms (true) or only (affine) transforms that could be mapped into an 2x3 matrix (false). + /// + private bool ContainsPerspective() + { + + // ReSharper disable CompareOfFloatsByEqualityOperator + return _m13 != 0 || _m23 != 0 || _m33 != 1; + // ReSharper restore CompareOfFloatsByEqualityOperator } /// @@ -292,15 +370,25 @@ namespace Avalonia public override string ToString() { CultureInfo ci = CultureInfo.CurrentCulture; + + string msg; + double[] values; + + if (ContainsPerspective()) + { + msg = "{{ {{M11:{0} M12:{1} M13:{2}}} {{M21:{3} M22:{4} M23:{5}}} {{M31:{6} M32:{7} M33:{8}}} }}"; + values = new[] { M11, M12, M13, M21, M22, M23, M31, M32, M33 }; + } + else + { + msg = "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}"; + values = new[] { M11, M12, M21, M22, M31, M32 }; + } + return string.Format( ci, - "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}", - M11.ToString(ci), - M12.ToString(ci), - M21.ToString(ci), - M22.ToString(ci), - M31.ToString(ci), - M32.ToString(ci)); + msg, + values.Select((v) => v.ToString(ci)).ToArray()); } /// @@ -311,7 +399,7 @@ namespace Avalonia { double d = GetDeterminant(); - if (MathUtilities.IsZero(d)) + if (MathUtilities.IsZero(d)) //TODO: decide if special handling is required for perspective { inverted = default; @@ -347,20 +435,30 @@ namespace Avalonia /// /// Parses a string. /// - /// Six comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY) that describe the new + /// Six or nine comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY[, persX, persY, persZ]) that describe the new /// The . public static Matrix Parse(string s) { + // initialize to satisfy compiler - only used when retrieved from string. + double v8 = 0; + double v9 = 0; + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Matrix.")) { - return new Matrix( - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble() - ); + var v1 = tokenizer.ReadDouble(); + var v2 = tokenizer.ReadDouble(); + var v3 = tokenizer.ReadDouble(); + var v4 = tokenizer.ReadDouble(); + var v5 = tokenizer.ReadDouble(); + var v6 = tokenizer.ReadDouble(); + var pers = tokenizer.TryReadDouble(out var v7); + pers = pers && tokenizer.TryReadDouble(out v8); + pers = pers && tokenizer.TryReadDouble(out v9); + + if (pers) + return new Matrix(v1, v2, v7, v3, v4, v8, v5, v6, v9); + else + return new Matrix(v1, v2, v3, v4, v5, v6); } } @@ -376,7 +474,7 @@ namespace Avalonia var determinant = matrix.GetDeterminant(); - if (MathUtilities.IsZero(determinant)) + if (MathUtilities.IsZero(determinant) || matrix.ContainsPerspective()) { return false; } From c40410cdb371f37071fb5df91367c60f057ea317 Mon Sep 17 00:00:00 2001 From: Andreas Schauerte Date: Mon, 5 Jul 2021 18:29:12 +0200 Subject: [PATCH 032/820] Add basic 3d transformation. --- .../Resources/Resource.Designer.cs | 2 +- samples/RenderDemo/MainWindow.xaml | 3 + .../RenderDemo/Pages/Transform3DPage.axaml | 81 ++++++++ .../RenderDemo/Pages/Transform3DPage.axaml.cs | 21 ++ .../ViewModels/Transform3DPageViewModel.cs | 61 ++++++ .../Resources/Resource.Designer.cs | 2 +- .../Animation/Animators/TransformAnimator.cs | 1 + src/Avalonia.Visuals/Matrix.cs | 50 +++-- src/Avalonia.Visuals/Media/Transform3D.cs | 192 ++++++++++++++++++ src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs | 6 +- 10 files changed, 393 insertions(+), 26 deletions(-) create mode 100644 samples/RenderDemo/Pages/Transform3DPage.axaml create mode 100644 samples/RenderDemo/Pages/Transform3DPage.axaml.cs create mode 100644 samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs create mode 100644 src/Avalonia.Visuals/Media/Transform3D.cs diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index dccc3f7159..b1ca548e2c 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace ControlCatalog.Android { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] public partial class Resource { diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index a4c6299278..2b5bba928a 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -63,5 +63,8 @@ + + + diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml new file mode 100644 index 0000000000..ebd838eb67 --- /dev/null +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + X-Rotation: + + Y-Rotation: + + Z-Rotation: + + X: + + Y: + + Z: + + + + + + diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml.cs b/samples/RenderDemo/Pages/Transform3DPage.axaml.cs new file mode 100644 index 0000000000..5083189c4c --- /dev/null +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml.cs @@ -0,0 +1,21 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using RenderDemo.ViewModels; + +namespace RenderDemo.Pages; + +public class Transform3DPage : UserControl +{ + public Transform3DPage() + { + InitializeComponent(); + this.DataContext = new Transform3DPageViewModel(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} + diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs new file mode 100644 index 0000000000..7e7849bd01 --- /dev/null +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -0,0 +1,61 @@ +using System; +using MiniMvvm; +using Avalonia.Animation; + +namespace RenderDemo.ViewModels +{ + public class Transform3DPageViewModel : ViewModelBase + { + private double _roationX = 0; + private double _rotationY = 0; + private double _rotationZ = 0; + + private double _x = 0; + private double _y = 0; + private double _z = 0; + + private double _depth = 200; + + public double RoationX + { + get => _roationX; + set => RaiseAndSetIfChanged(ref _roationX, value); + } + + public double RotationY + { + get => _rotationY; + set => RaiseAndSetIfChanged(ref _rotationY, value); + } + + public double RotationZ + { + get => _rotationZ; + set => RaiseAndSetIfChanged(ref _rotationZ, value); + } + + public double Depth + { + get => _depth; + set => RaiseAndSetIfChanged(ref _depth, value); + } + + public double X + { + get => _x; + set => RaiseAndSetIfChanged(ref _x, value); + } + + public double Y + { + get => _y; + set => RaiseAndSetIfChanged(ref _y, value); + } + + public double Z + { + get => _z; + set => RaiseAndSetIfChanged(ref _z, value); + } + } +} diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 87fd47df25..83db67fcee 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] public partial class Resource { diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 34ec8ac503..a98f2ec65e 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -43,6 +43,7 @@ namespace Avalonia.Animation.Animators normalTransform.Children.Add(new SkewTransform()); normalTransform.Children.Add(new RotateTransform()); normalTransform.Children.Add(new TranslateTransform()); + normalTransform.Children.Add(new Transform3D()); ctrl.RenderTransform = normalTransform; } diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 9dd5bced86..e949d728d0 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -14,7 +14,7 @@ namespace Avalonia /// 2nd row | skrewX | scaleY | persY | /// 3rd row | transX | transY | persZ | /// - /// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and persX/transX are swapped). + /// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and perspp0/tranX are swapped). /// #if !BUILDTASK public @@ -30,8 +30,7 @@ namespace Avalonia private readonly double _m31; private readonly double _m32; private readonly double _m33; - - + /// /// Initializes a new instance of the struct (equivalent to a 2x3 Matrix without perspective). /// @@ -159,12 +158,15 @@ namespace Avalonia public static Matrix operator *(Matrix value1, Matrix value2) { return new Matrix( - (value1.M11 * value2.M11) + (value1.M12 * value2.M21), - (value1.M11 * value2.M12) + (value1.M12 * value2.M22), - (value1.M21 * value2.M11) + (value1.M22 * value2.M21), - (value1.M21 * value2.M12) + (value1.M22 * value2.M22), - (value1._m31 * value2.M11) + (value1._m32 * value2.M21) + value2._m31, - (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); //TODO: include perspective + (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31), + (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32), + (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33), + (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31), + (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32), + (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33), + (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31), + (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32), + (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33)); } /// @@ -288,7 +290,7 @@ namespace Avalonia } /// - /// Prpends another matrix as pre-multiplication operation. + /// Prepends another matrix as pre-multiplication operation. /// Equivalent to value * this; /// /// A matrix. @@ -359,7 +361,7 @@ namespace Avalonia { // ReSharper disable CompareOfFloatsByEqualityOperator - return _m13 != 0 || _m23 != 0 || _m33 != 1; + return _m31 != 0 || _m32 != 0 || _m33 != 1; // ReSharper restore CompareOfFloatsByEqualityOperator } @@ -399,21 +401,27 @@ namespace Avalonia { double d = GetDeterminant(); - if (MathUtilities.IsZero(d)) //TODO: decide if special handling is required for perspective + if (MathUtilities.IsZero(d)) { inverted = default; return false; } + var invdet = 1 / d; + inverted = new Matrix( - _m22 / d, - -_m12 / d, - -_m21 / d, - _m11 / d, - ((_m21 * _m32) - (_m22 * _m31)) / d, - ((_m12 * _m31) - (_m11 * _m32)) / d); - + (_m22 * _m33 - _m32 * _m23) * invdet, + (_m13 * _m31 - _m12 * _m33) * invdet, + (_m12 * _m23 - _m13 * _m22) * invdet, + (_m23 * _m31 - _m21 * _m33) * invdet, + (_m11 * _m33 - _m13 * _m31) * invdet, + (_m21 * _m13 - _m11 * _m23) * invdet, + (_m21 * _m32 - _m31 * _m22) * invdet, + (_m21 * _m12 - _m11 * _m32) * invdet, + (_m11 * _m22 - _m21 * _m12) * invdet + ); + return true; } @@ -424,7 +432,7 @@ namespace Avalonia /// The inverted matrix. public Matrix Invert() { - if (!TryInvert(out Matrix inverted)) + if (!TryInvert(out var inverted)) { throw new InvalidOperationException("Transform is not invertible."); } @@ -467,7 +475,7 @@ namespace Avalonia /// /// Matrix to decompose. /// Decomposed matrix. - /// The status of the operation. + /// The status of the operation. public static bool TryDecomposeTransform(Matrix matrix, out Decomposed decomposed) { decomposed = default; diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs new file mode 100644 index 0000000000..e063f76556 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Transform3D.cs @@ -0,0 +1,192 @@ +using System; +using System.Numerics; + +namespace Avalonia.Media; + +public class Transform3D : Transform +{ + /// + /// Defines the property. + /// + public static readonly StyledProperty s_rotationXProperty = + AvaloniaProperty.Register(nameof(RotationX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_rotationYProperty = + AvaloniaProperty.Register(nameof(RotationY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_rotationZProperty = + AvaloniaProperty.Register(nameof(RotationZ)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_depthProperty = + AvaloniaProperty.Register(nameof(Depth)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_centerXProperty = + AvaloniaProperty.Register(nameof(CenterX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_centerYProperty = + AvaloniaProperty.Register(nameof(CenterY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_xProperty = + AvaloniaProperty.Register(nameof(X)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_yProperty = + AvaloniaProperty.Register(nameof(Y)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_zProperty = + AvaloniaProperty.Register(nameof(Z)); + + + /// + /// Initializes a new instance of the class. + /// + public Transform3D() + { + this.GetObservable(s_rotationXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_rotationYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_rotationZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_depthProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_centerXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_centerYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_xProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_yProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_zProperty).Subscribe(_ => RaiseChanged()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The skew angle of X-axis, in degrees. + /// The skew angle of Y-axis, in degrees. + /// + public Transform3D( + double rotationX, + double rotationY, + double rotationZ, + double depth, + double centerX, + double centerY) : this() + { + RotationX = rotationX; + RotationY = rotationY; + RotationZ = rotationZ; + Depth = depth; + CenterX = centerX; + CenterY = centerY; + } + + /// + /// Gets or sets the X property. + /// + public double RotationX + { + get => GetValue(s_rotationXProperty); + set => SetValue(s_rotationXProperty, value); + } + + /// + /// Gets or sets the Y property. + /// + public double RotationY + { + get => GetValue(s_rotationYProperty); + set => SetValue(s_rotationYProperty, value); + } + + public double RotationZ + { + get => GetValue(s_rotationZProperty); + set => SetValue(s_rotationZProperty, value); + } + + public double Depth + { + get => GetValue(s_depthProperty); + set => SetValue(s_depthProperty, value); + } + + public double CenterX + { + get => GetValue(s_centerXProperty); + set => SetValue(s_centerXProperty, value); + } + + public double CenterY + { + get => GetValue(s_centerYProperty); + set => SetValue(s_centerYProperty, value); + } + + public double X + { + get => GetValue(s_xProperty); + set => SetValue(s_xProperty, value); + } + + public double Y + { + get => GetValue(s_yProperty); + set => SetValue(s_yProperty, value); + } + + public double Z + { + get => GetValue(s_zProperty); + set => SetValue(s_zProperty, value); + } + + /// + /// Gets the transform's . + /// + public override Matrix Value + { + get + { + var matrix44 = Matrix4x4.Identity; + + matrix44 *= Matrix4x4.CreateTranslation((float)X, (float)Y, (float)Z); + + matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); + matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); + matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); + + var matrix = new Matrix( + matrix44.M11, + matrix44.M12, + matrix44.M14, + matrix44.M21, + matrix44.M22, + matrix44.M24, + matrix44.M41, + matrix44.M42, + matrix44.M44); + + return matrix; + } + } +} diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 75b4231640..c0afc8bd9c 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -103,9 +103,9 @@ namespace Avalonia.Skia SkewY = (float)m.M12, ScaleY = (float)m.M22, TransY = (float)m.M32, - Persp0 = 0, - Persp1 = 0, - Persp2 = 1 + Persp0 = (float)m.M13, + Persp1 = (float)m.M23, + Persp2 = (float)m.M33 }; return sm; From 0a831b978483699b1dfa75ca177307aebef98e6e Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 11:16:33 +0100 Subject: [PATCH 033/820] Refactor names. --- .../RenderDemo/Pages/Transform3DPage.axaml | 7 +- .../ViewModels/Transform3DPageViewModel.cs | 8 +- src/Avalonia.Visuals/Media/Transform3D.cs | 135 ++++++++---------- 3 files changed, 64 insertions(+), 86 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index ebd838eb67..e66c498bbc 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -54,12 +54,11 @@ + CenterX="{Binding X}" + CenterY="{Binding Y}" + CenterZ="{Binding Z}" /> X-Rotation: diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs index 7e7849bd01..d28705fc0d 100644 --- a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -6,7 +6,7 @@ namespace RenderDemo.ViewModels { public class Transform3DPageViewModel : ViewModelBase { - private double _roationX = 0; + private double _rotationX = 0; private double _rotationY = 0; private double _rotationZ = 0; @@ -16,10 +16,10 @@ namespace RenderDemo.ViewModels private double _depth = 200; - public double RoationX + public double RotationX { - get => _roationX; - set => RaiseAndSetIfChanged(ref _roationX, value); + get => _rotationX; + set => RaiseAndSetIfChanged(ref _rotationX, value); } public double RotationY diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs index e063f76556..cfbde3204a 100644 --- a/src/Avalonia.Visuals/Media/Transform3D.cs +++ b/src/Avalonia.Visuals/Media/Transform3D.cs @@ -8,58 +8,41 @@ public class Transform3D : Transform /// /// Defines the property. /// - public static readonly StyledProperty s_rotationXProperty = + public static readonly StyledProperty RotationXProperty = AvaloniaProperty.Register(nameof(RotationX)); /// /// Defines the property. /// - public static readonly StyledProperty s_rotationYProperty = + public static readonly StyledProperty RotationYProperty = AvaloniaProperty.Register(nameof(RotationY)); /// /// Defines the property. /// - public static readonly StyledProperty s_rotationZProperty = + public static readonly StyledProperty RotationZProperty = AvaloniaProperty.Register(nameof(RotationZ)); - /// - /// Defines the property. - /// - public static readonly StyledProperty s_depthProperty = - AvaloniaProperty.Register(nameof(Depth)); /// /// Defines the property. /// - public static readonly StyledProperty s_centerXProperty = + public static readonly StyledProperty CenterXProperty = AvaloniaProperty.Register(nameof(CenterX)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty s_centerYProperty = - AvaloniaProperty.Register(nameof(CenterY)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty s_xProperty = - AvaloniaProperty.Register(nameof(X)); /// /// Defines the property. /// - public static readonly StyledProperty s_yProperty = - AvaloniaProperty.Register(nameof(Y)); + public static readonly StyledProperty CenterYProperty = + AvaloniaProperty.Register(nameof(CenterY)); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty s_zProperty = - AvaloniaProperty.Register(nameof(Z)); + public static readonly StyledProperty CenterZProperty = + AvaloniaProperty.Register(nameof(CenterZ)); /// @@ -67,97 +50,93 @@ public class Transform3D : Transform /// public Transform3D() { - this.GetObservable(s_rotationXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_rotationYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_rotationZProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_depthProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_centerXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_centerYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_xProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_yProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_zProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); } /// /// Initializes a new instance of the class. /// - /// The skew angle of X-axis, in degrees. - /// The skew angle of Y-axis, in degrees. - /// + /// The rotation around the X-Axis + /// The rotation around the Y-Axis + /// The rotation around the Z-Axis + /// The origin of the X-Axis + /// The origin of the Y-Axis + /// The origin of the Z-Axis public Transform3D( double rotationX, double rotationY, double rotationZ, - double depth, - double centerX, - double centerY) : this() + double originCenterX, + double originCenterY, + double originCenterZ) : this() { RotationX = rotationX; RotationY = rotationY; RotationZ = rotationZ; - Depth = depth; - CenterX = centerX; - CenterY = centerY; + CenterX = originCenterX; + CenterY = originCenterY; + CenterZ = originCenterZ; } /// - /// Gets or sets the X property. + /// Sets the rotation around the X-Axis /// public double RotationX { - get => GetValue(s_rotationXProperty); - set => SetValue(s_rotationXProperty, value); + get => GetValue(RotationXProperty); + set => SetValue(RotationXProperty, value); } /// - /// Gets or sets the Y property. + /// Sets the rotation around the Y-Axis /// public double RotationY { - get => GetValue(s_rotationYProperty); - set => SetValue(s_rotationYProperty, value); + get => GetValue(RotationYProperty); + set => SetValue(RotationYProperty, value); } + /// + /// Sets the rotation around the Z-Axis + /// public double RotationZ { - get => GetValue(s_rotationZProperty); - set => SetValue(s_rotationZProperty, value); - } - - public double Depth - { - get => GetValue(s_depthProperty); - set => SetValue(s_depthProperty, value); + get => GetValue(RotationZProperty); + set => SetValue(RotationZProperty, value); } + /// + /// Moves the origin of the X-Axis + /// public double CenterX { - get => GetValue(s_centerXProperty); - set => SetValue(s_centerXProperty, value); + get => GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); } + /// + /// Moves the origin of the Y-Axis + /// public double CenterY { - get => GetValue(s_centerYProperty); - set => SetValue(s_centerYProperty, value); + get => GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); } - public double X - { - get => GetValue(s_xProperty); - set => SetValue(s_xProperty, value); - } - - public double Y - { - get => GetValue(s_yProperty); - set => SetValue(s_yProperty, value); - } - - public double Z + /// + /// Moves the origin of the Z-Axis + /// + public double CenterZ { - get => GetValue(s_zProperty); - set => SetValue(s_zProperty, value); + get => GetValue(CenterZProperty); + set => SetValue(CenterZProperty, value); } /// @@ -169,7 +148,7 @@ public class Transform3D : Transform { var matrix44 = Matrix4x4.Identity; - matrix44 *= Matrix4x4.CreateTranslation((float)X, (float)Y, (float)Z); + matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); From 9a01f13114f77a889d68af2e06fbfebd0aa87882 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 13:39:57 +0100 Subject: [PATCH 034/820] Fix order of transforms. --- samples/RenderDemo/Pages/Transform3DPage.axaml | 2 +- src/Avalonia.Visuals/Media/Transform3D.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index e66c498bbc..0bf2279dd3 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650" x:Class="RenderDemo.Pages.Transform3DPage"> diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs index cfbde3204a..cbc1fc958d 100644 --- a/src/Avalonia.Visuals/Media/Transform3D.cs +++ b/src/Avalonia.Visuals/Media/Transform3D.cs @@ -154,6 +154,13 @@ public class Transform3D : Transform matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); + matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + + var perspectiveMatrix = Matrix4x4.Identity; + perspectiveMatrix.M34 = -1 / (float)50; + + matrix44 *= perspectiveMatrix; + var matrix = new Matrix( matrix44.M11, matrix44.M12, From 07f45cf1d724535d483f2c9b8acdfe6d393630cf Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 15:15:56 +0100 Subject: [PATCH 035/820] Add animation. Add controls instead of Vector to prof usability. --- .../RenderDemo/Pages/Transform3DPage.axaml | 252 +++++++++++++----- 1 file changed, 179 insertions(+), 73 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 0bf2279dd3..438611976a 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,79 +2,185 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650" + mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200" x:Class="RenderDemo.Pages.Transform3DPage"> - - - - - - - - - - - - - - - - - - - - X-Rotation: - - Y-Rotation: - - Z-Rotation: - - X: - - Y: - - Z: - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - From 9fdb48d0c4328ea6332851ed0758bd24ca692ee8 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 17:29:31 +0100 Subject: [PATCH 036/820] Rename transform. Re-add depth. Add slider to preview for showing effect of depth. --- .../RenderDemo/Pages/Transform3DPage.axaml | 89 ++++---- .../ViewModels/Transform3DPageViewModel.cs | 44 ---- .../Animation/Animators/TransformAnimator.cs | 2 +- .../Media/Rotate3DTransform.cs | 195 ++++++++++++++++++ src/Avalonia.Visuals/Media/Transform3D.cs | 178 ---------------- 5 files changed, 235 insertions(+), 273 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/Rotate3DTransform.cs delete mode 100644 src/Avalonia.Visuals/Media/Transform3D.cs diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 438611976a..1093399c0c 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200" + mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="400" x:Class="RenderDemo.Pages.Transform3DPage"> @@ -16,10 +16,10 @@ - + @@ -142,45 +142,34 @@ - + - + - + - + + + Depth: + + + diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs index d28705fc0d..0c1caac8ea 100644 --- a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -6,56 +6,12 @@ namespace RenderDemo.ViewModels { public class Transform3DPageViewModel : ViewModelBase { - private double _rotationX = 0; - private double _rotationY = 0; - private double _rotationZ = 0; - - private double _x = 0; - private double _y = 0; - private double _z = 0; - private double _depth = 200; - public double RotationX - { - get => _rotationX; - set => RaiseAndSetIfChanged(ref _rotationX, value); - } - - public double RotationY - { - get => _rotationY; - set => RaiseAndSetIfChanged(ref _rotationY, value); - } - - public double RotationZ - { - get => _rotationZ; - set => RaiseAndSetIfChanged(ref _rotationZ, value); - } - public double Depth { get => _depth; set => RaiseAndSetIfChanged(ref _depth, value); } - - public double X - { - get => _x; - set => RaiseAndSetIfChanged(ref _x, value); - } - - public double Y - { - get => _y; - set => RaiseAndSetIfChanged(ref _y, value); - } - - public double Z - { - get => _z; - set => RaiseAndSetIfChanged(ref _z, value); - } } } diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index a98f2ec65e..e12ca722f9 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -43,7 +43,7 @@ namespace Avalonia.Animation.Animators normalTransform.Children.Add(new SkewTransform()); normalTransform.Children.Add(new RotateTransform()); normalTransform.Children.Add(new TranslateTransform()); - normalTransform.Children.Add(new Transform3D()); + normalTransform.Children.Add(new Rotate3DTransform()); ctrl.RenderTransform = normalTransform; } diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs new file mode 100644 index 0000000000..9a81ae306c --- /dev/null +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -0,0 +1,195 @@ +using System; +using System.Numerics; + +namespace Avalonia.Media; + +/// +/// Non-Affine 3D transformation for rotating an visual around a definable axis +/// +public class Rotate3DTransform : Transform +{ + /// + /// Defines the property. + /// + public static readonly StyledProperty RotationXProperty = + AvaloniaProperty.Register(nameof(RotationX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RotationYProperty = + AvaloniaProperty.Register(nameof(RotationY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RotationZProperty = + AvaloniaProperty.Register(nameof(RotationZ)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterXProperty = + AvaloniaProperty.Register(nameof(CenterX)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterYProperty = + AvaloniaProperty.Register(nameof(CenterY)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterZProperty = + AvaloniaProperty.Register(nameof(CenterZ)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty DepthProperty = + AvaloniaProperty.Register(nameof(Depth)); + + /// + /// Initializes a new instance of the class. + /// + public Rotate3DTransform() + { + this.GetObservable(RotationXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The rotation around the X-Axis + /// The rotation around the Y-Axis + /// The rotation around the Z-Axis + /// The origin of the X-Axis + /// The origin of the Y-Axis + /// The origin of the Z-Axis + public Rotate3DTransform( + double rotationX, + double rotationY, + double rotationZ, + double centerX, + double centerY, + double centerZ) : this() + { + RotationX = rotationX; + RotationY = rotationY; + RotationZ = rotationZ; + CenterX = centerX; + CenterY = centerY; + CenterZ = centerZ; + } + + /// + /// Sets the rotation around the X-Axis + /// + public double RotationX + { + get => GetValue(RotationXProperty); + set => SetValue(RotationXProperty, value); + } + + /// + /// Sets the rotation around the Y-Axis + /// + public double RotationY + { + get => GetValue(RotationYProperty); + set => SetValue(RotationYProperty, value); + } + + /// + /// Sets the rotation around the Z-Axis + /// + public double RotationZ + { + get => GetValue(RotationZProperty); + set => SetValue(RotationZProperty, value); + } + + /// + /// Moves the origin of the X-Axis + /// + public double CenterX + { + get => GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); + } + + /// + /// Moves the origin of the Y-Axis + /// + public double CenterY + { + get => GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); + } + + /// + /// Moves the origin of the Z-Axis + /// + public double CenterZ + { + get => GetValue(CenterZProperty); + set => SetValue(CenterZProperty, value); + } + + /// + /// Affects the depth of the rotation effect + /// + public double Depth + { + get => GetValue(DepthProperty); + set => SetValue(DepthProperty, value); + } + + /// + /// Gets the transform's . + /// + public override Matrix Value + { + get + { + var matrix44 = Matrix4x4.Identity; + + matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + + matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); + matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); + matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); + + matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + + if (Depth != 0) + { + var perspectiveMatrix = Matrix4x4.Identity; + perspectiveMatrix.M34 = -1 / (float)Depth; + matrix44 *= perspectiveMatrix; + } + + var matrix = new Matrix( + matrix44.M11, + matrix44.M12, + matrix44.M14, + matrix44.M21, + matrix44.M22, + matrix44.M24, + matrix44.M41, + matrix44.M42, + matrix44.M44); + + return matrix; + } + } +} diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs deleted file mode 100644 index cbc1fc958d..0000000000 --- a/src/Avalonia.Visuals/Media/Transform3D.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Numerics; - -namespace Avalonia.Media; - -public class Transform3D : Transform -{ - /// - /// Defines the property. - /// - public static readonly StyledProperty RotationXProperty = - AvaloniaProperty.Register(nameof(RotationX)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty RotationYProperty = - AvaloniaProperty.Register(nameof(RotationY)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty RotationZProperty = - AvaloniaProperty.Register(nameof(RotationZ)); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty CenterXProperty = - AvaloniaProperty.Register(nameof(CenterX)); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty CenterYProperty = - AvaloniaProperty.Register(nameof(CenterY)); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty CenterZProperty = - AvaloniaProperty.Register(nameof(CenterZ)); - - - /// - /// Initializes a new instance of the class. - /// - public Transform3D() - { - this.GetObservable(RotationXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(RotationYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(RotationZProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); - } - - /// - /// Initializes a new instance of the class. - /// - /// The rotation around the X-Axis - /// The rotation around the Y-Axis - /// The rotation around the Z-Axis - /// The origin of the X-Axis - /// The origin of the Y-Axis - /// The origin of the Z-Axis - public Transform3D( - double rotationX, - double rotationY, - double rotationZ, - double originCenterX, - double originCenterY, - double originCenterZ) : this() - { - RotationX = rotationX; - RotationY = rotationY; - RotationZ = rotationZ; - CenterX = originCenterX; - CenterY = originCenterY; - CenterZ = originCenterZ; - } - - /// - /// Sets the rotation around the X-Axis - /// - public double RotationX - { - get => GetValue(RotationXProperty); - set => SetValue(RotationXProperty, value); - } - - /// - /// Sets the rotation around the Y-Axis - /// - public double RotationY - { - get => GetValue(RotationYProperty); - set => SetValue(RotationYProperty, value); - } - - /// - /// Sets the rotation around the Z-Axis - /// - public double RotationZ - { - get => GetValue(RotationZProperty); - set => SetValue(RotationZProperty, value); - } - - /// - /// Moves the origin of the X-Axis - /// - public double CenterX - { - get => GetValue(CenterXProperty); - set => SetValue(CenterXProperty, value); - } - - /// - /// Moves the origin of the Y-Axis - /// - public double CenterY - { - get => GetValue(CenterYProperty); - set => SetValue(CenterYProperty, value); - } - - /// - /// Moves the origin of the Z-Axis - /// - public double CenterZ - { - get => GetValue(CenterZProperty); - set => SetValue(CenterZProperty, value); - } - - /// - /// Gets the transform's . - /// - public override Matrix Value - { - get - { - var matrix44 = Matrix4x4.Identity; - - matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); - - matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); - matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); - matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); - - matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); - - var perspectiveMatrix = Matrix4x4.Identity; - perspectiveMatrix.M34 = -1 / (float)50; - - matrix44 *= perspectiveMatrix; - - var matrix = new Matrix( - matrix44.M11, - matrix44.M12, - matrix44.M14, - matrix44.M21, - matrix44.M22, - matrix44.M24, - matrix44.M41, - matrix44.M42, - matrix44.M44); - - return matrix; - } - } -} From 8c131ba100ac3890595216c4936feb54840a2688 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 20 Jan 2022 06:13:00 +0100 Subject: [PATCH 037/820] Improve readability in RenderDemo, slow down animation. --- .../RenderDemo/Pages/Transform3DPage.axaml | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 1093399c0c..8646dc6870 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -8,10 +8,15 @@ @@ -24,9 +29,16 @@ + + @@ -140,25 +156,25 @@ - + - + - + - + From e6d86fb898ce92569cb590bc2466d8ec7542627c Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 20 Jan 2022 06:54:00 +0100 Subject: [PATCH 038/820] Fix ContainsPerspective method. --- src/Avalonia.Visuals/Matrix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index e949d728d0..4326dc3670 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -361,7 +361,7 @@ namespace Avalonia { // ReSharper disable CompareOfFloatsByEqualityOperator - return _m31 != 0 || _m32 != 0 || _m33 != 1; + return _m13 != 0 || _m23 != 0 || _m33 != 1; // ReSharper restore CompareOfFloatsByEqualityOperator } From bde9c078c56eb3f7864cac17a46315af783fa123 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 20 Jan 2022 13:58:34 +0100 Subject: [PATCH 039/820] Add observable. Remove borders from sample to better visualize error. --- samples/RenderDemo/Pages/Transform3DPage.axaml | 8 ++++---- src/Avalonia.Visuals/Media/Rotate3DTransform.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 8646dc6870..9f369dae98 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -162,24 +162,24 @@ Depth="{Binding Depth}"/> - + - + diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 9a81ae306c..a3af6b8c66 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -64,6 +64,7 @@ public class Rotate3DTransform : Transform this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(DepthProperty).Subscribe(_ => RaiseChanged()); } /// From 9078ca7dc8b2512322e5a2b26e91c2ca8348f9e1 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 26 Jan 2022 14:46:54 +0100 Subject: [PATCH 040/820] Fix Rotate3DTransform for DeferredRenderer. --- .../Avalonia.Build.Tasks.csproj | 1 + src/Avalonia.Visuals/Matrix.cs | 2 +- src/Avalonia.Visuals/Point.cs | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index e864ea2007..7e4272f5d2 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -94,5 +94,6 @@ + diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 4326dc3670..38d0508ffb 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -357,7 +357,7 @@ namespace Avalonia /// /// Determines if the current matrix contains perspective (non-affine) transforms (true) or only (affine) transforms that could be mapped into an 2x3 matrix (false). /// - private bool ContainsPerspective() + public bool ContainsPerspective() { // ReSharper disable CompareOfFloatsByEqualityOperator diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 67e7d71fbc..473de8e501 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Numerics; #if !BUILDTASK using Avalonia.Animation.Animators; #endif @@ -244,6 +245,22 @@ namespace Avalonia /// The transformed point. public Point Transform(Matrix transform) { + if (transform.ContainsPerspective()) + { + var m44 = new Matrix4x4( + (float)transform.M11, (float)transform.M12, (float)transform.M13, 0, + (float)transform.M21, (float)transform.M22, (float)transform.M23, 0, + (float)transform.M31, (float)transform.M32, (float)transform.M33, 0, + 0, 0, 0, 1 + ); + + var vector = new Vector3((float)X, (float)Y, 1); + var transformedVector = Vector3.Transform(vector, m44); + var z = 1 / transformedVector.Z; + + return new Point(transformedVector.X * z, transformedVector.Y * z); + } + var x = X; var y = Y; var xadd = y * transform.M21 + transform.M31; From e8f2d3e3f3372fa226a1cb57b447974fe1da8f79 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 26 Jan 2022 16:25:18 +0100 Subject: [PATCH 041/820] Add comments, remove todos, extend example. --- .../RenderDemo/Pages/Transform3DPage.axaml | 162 ++++++++++++------ .../ViewModels/Transform3DPageViewModel.cs | 44 +++++ src/Avalonia.Visuals/Matrix.cs | 50 +++++- .../Media/Rotate3DTransform.cs | 73 ++++---- src/Avalonia.Visuals/Point.cs | 29 +--- 5 files changed, 235 insertions(+), 123 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 9f369dae98..bf63b6d1c6 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,12 +2,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="400" + mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="700" x:Class="RenderDemo.Pages.Transform3DPage"> - + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs index 2a6bdcb434..c8d1d40e3a 100644 --- a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -8,7 +8,6 @@ namespace RenderDemo.ViewModels { private double _depth = 200; - private double _depth2 = 200; private double _centerX = 0; private double _centerY = 0; private double _centerZ = 0; @@ -22,11 +21,6 @@ namespace RenderDemo.ViewModels set => RaiseAndSetIfChanged(ref _depth, value); } - public double Depth2 - { - get => _depth2; - set => RaiseAndSetIfChanged(ref _depth2, value); - } public double CenterX { get => _centerX; From 3e7f190ff9881fc0785e4698aca96099212e5b71 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 13:39:27 +0100 Subject: [PATCH 046/820] Add code review changes: Fix typos. Simplify code. Generalize code. Remove duplications. Improve performance. --- .../RenderDemo/Pages/Transform3DPage.axaml | 234 +++++++++--------- .../Resources/Resource.Designer.cs | 2 +- .../Transitions/Rotate3DTransition.cs | 124 ++++------ src/Avalonia.Visuals/Matrix.cs | 12 +- .../Media/Rotate3DTransform.cs | 2 +- src/Avalonia.Visuals/Point.cs | 7 +- 6 files changed, 163 insertions(+), 218 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index fa03c7ba49..30ed35bbac 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -21,149 +21,139 @@ + + + + + + + + + - - - - - - - - - - - + - - - - + - - - - + - - - - + diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 83db67fcee..87fd47df25 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] public partial class Resource { diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index b603fe5f7b..56567f5fea 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Avalonia.Collections; using Avalonia.Media; using Avalonia.Styling; @@ -9,18 +10,26 @@ namespace Avalonia.Animation; public class Rotate3DTransition: PageSlide { - + /// - /// Creates a new instance if the + /// Creates a new instance of the /// /// How long the rotation should take place /// The orientation of the rotation - public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal) + public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal, double? depth = null) : base(duration, orientation) - {} + { + Depth = depth; + } + + /// + /// Defines the depth of the 3D Effect. If null, depth will be calculated automatically from the width or height + /// of the common parent of the visual being rotated. + /// + public double? Depth { get; set; } /// - /// Creates a new instance if the + /// Creates a new instance of the /// public Rotate3DTransition() { } @@ -41,49 +50,32 @@ public class Rotate3DTransition: PageSlide _ => throw new ArgumentOutOfRangeException() }; - var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = center}; + var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = Depth ?? center}; var centerZSetter = new Setter {Property = Rotate3DTransform.CenterZProperty, Value = -center / 2}; + KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex) => + new() { + Setters = + { + new Setter { Property = rotateProperty, Value = rotation }, + new Setter { Property = Visual.ZIndexProperty, Value = zIndex }, + centerZSetter, + depthSetter + }, + Cue = new Cue(cue) + }; + if (from != null) { var animation = new Animation { + Easing = SlideOutEasing, Duration = Duration, Children = { - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 0d }, - new Setter { Property = Visual.ZIndexProperty, Value = 2 }, - centerZSetter, - depthSetter, - }, - Cue = new Cue(0d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 45d * (forward ? -1 : 1) }, - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - centerZSetter, - depthSetter - }, - Cue = new Cue(0.5d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 90d * (forward ? -1 : 1) }, - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - centerZSetter, - depthSetter - }, - Cue = new Cue(1d) - } + CreateKeyFrame(0d, 0d, 2), + CreateKeyFrame(0.5d, 45d * (forward ? -1 : 1), 1), + CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1) } }; @@ -95,42 +87,13 @@ public class Rotate3DTransition: PageSlide to.IsVisible = true; var animation = new Animation { + Easing = SlideInEasing, Duration = Duration, Children = { - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 90d * (forward ? 1 : -1) }, - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - centerZSetter, - depthSetter - }, - Cue = new Cue(0d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - new Setter { Property = rotateProperty, Value = 45d * (forward ? 1 : -1) }, - centerZSetter, - depthSetter - }, - Cue = new Cue(0.5d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 0d }, - new Setter { Property = Visual.ZIndexProperty, Value = 2 }, - centerZSetter, - depthSetter, - }, - Cue = new Cue(1d) - } + CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1), + CreateKeyFrame(0.5d, 45d * (forward ? 1 : -1), 1), + CreateKeyFrame(1d, 0d, 2) } }; @@ -139,15 +102,18 @@ public class Rotate3DTransition: PageSlide await Task.WhenAll(tasks); - if (from != null && !cancellationToken.IsCancellationRequested) + if (!cancellationToken.IsCancellationRequested) { - from.IsVisible = false; - from.ZIndex = 1; - } + if (from != null) + { + from.IsVisible = false; + from.ZIndex = 1; + } - if (to != null && !cancellationToken.IsCancellationRequested) - { - to.ZIndex = 2; + if (to != null) + { + to.ZIndex = 2; + } } } } diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 5c7d42676a..5bbc657385 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -348,15 +348,9 @@ namespace Avalonia } else { - var x = p.X; - var y = p.Y; - var xAdd = y * M21 + M31; - var yAdd = x * M12 + M32; - x *= M11; - x += xAdd; - y *= M22; - y += yAdd; - transformedResult = new Point(x, y); + return new Point( + (p.X * M11) + (p.Y * M21) + M31, + (p.X * M12) + (p.Y * M22) + M32); } return transformedResult; diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 0fea9d73a0..4feca7acd8 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -5,7 +5,7 @@ using Avalonia.Animation.Animators; namespace Avalonia.Media; /// -/// Non-Affine 3D transformation for rotating an visual around a definable axis +/// Non-Affine 3D transformation for rotating a visual around a definable axis /// public class Rotate3DTransform : Transform { diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 8e01ea47c5..fbdf0db800 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -169,12 +169,7 @@ namespace Avalonia /// The point. /// The matrix. /// The resulting point. - public static Point operator *(Point point, Matrix matrix) - { - return new Point( - (point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.M31, - (point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.M32); - } + public static Point operator *(Point point, Matrix matrix) => matrix.Transform(point); /// /// Parses a string. From 43de185f2fb181faa01d244d5312283ffd385ed3 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 16:52:35 +0100 Subject: [PATCH 047/820] Add unit tests. Add param to constructor. --- .../Media/Rotate3DTransform.cs | 5 +- .../Avalonia.Visuals.UnitTests/MatrixTests.cs | 95 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.Visuals.UnitTests/MatrixTests.cs diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 4feca7acd8..dbcf749789 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -70,13 +70,15 @@ public class Rotate3DTransform : Transform /// The origin of the X-Axis /// The origin of the Y-Axis /// The origin of the Z-Axis + /// The depth of the 3D effect public Rotate3DTransform( double angleX, double angleY, double angleZ, double centerX, double centerY, - double centerZ) : this() + double centerZ, + double depth) : this() { _isInitializing = true; AngleX = angleX; @@ -85,6 +87,7 @@ public class Rotate3DTransform : Transform CenterX = centerX; CenterY = centerY; CenterZ = centerZ; + Depth = depth; _isInitializing = false; } diff --git a/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs new file mode 100644 index 0000000000..cb5e2390c7 --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs @@ -0,0 +1,95 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Visuals.UnitTests; + +/// +/// These tests use the "official" Matrix4x4 and Matrix3x2 from the System.Numerics namespace, to validate +/// that Avalonias own implementation of a 3x3 Matrix works correctly. +/// +public class MatrixTests +{ + private double ReducePrecision(double input) + { + return Math.Truncate(input * 10000); + } + + /// + /// Because Avalonia is working internally with doubles, but System.Numerics Vector and Matrix implementations + /// only make use of floats, we need to reduce precision, comparing them. It should be sufficient to compare + /// 5 fractional digits to ensure, that the result is correct. + /// + /// The expected vector + /// The actual transformed point + private void AssertCoordinatesEqualWithReducedPrecision(Vector2 expected, Point actual) + { + var expectedX = ReducePrecision(expected.X); + var expectedY = ReducePrecision(expected.Y); + + var actualX = ReducePrecision(actual.X); + var actualY = ReducePrecision(actual.Y); + + Assert.Equal(expectedX, actualX); + Assert.Equal(expectedY, actualY); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Translated_Matrix() + { + var vector2 = Vector2.Transform( + new Vector2(1, 1), + Matrix3x2.CreateTranslation(2, 2)); + var expected = new Point(vector2.X, vector2.Y); + + var matrix = Matrix.CreateTranslation(2, 2); + var point = new Point(1, 1); + var transformedPoint = matrix.Transform(point); + + Assert.Equal(expected, transformedPoint); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Rotated_Matrix() + { + var expected = Vector2.Transform( + new Vector2(0, 10), + Matrix3x2.CreateRotation((float)Matrix.ToRadians(45))); + + var matrix = Matrix.CreateRotation(Matrix.ToRadians(45)); + var point = new Point(0, 10); + var actual = matrix.Transform(point); + + AssertCoordinatesEqualWithReducedPrecision(expected, actual); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Scaled_Matrix() + { + var vector2 = Vector2.Transform( + new Vector2(1, 1), + Matrix3x2.CreateScale(2, 2)); + var expected = new Point(vector2.X, vector2.Y); + var matrix = Matrix.CreateScale(2, 2); + var point = new Point(1, 1); + var actual = matrix.Transform(point); + + Assert.Equal(expected, actual); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Skewed_Matrix() + { + var expected = Vector2.Transform( + new Vector2(1, 1), + Matrix3x2.CreateSkew(30, 20)); + + var matrix = Matrix.CreateSkew(30, 20); + var point = new Point(1, 1); + var actual = matrix.Transform(point); + + AssertCoordinatesEqualWithReducedPrecision(expected, actual); + } +} From 30f99c847a767d2ff7776964ed209df6a55ddd0d Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 16:54:26 +0100 Subject: [PATCH 048/820] Refactor test. --- tests/Avalonia.Visuals.UnitTests/MatrixTests.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs index cb5e2390c7..99fe4685d7 100644 --- a/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs @@ -12,11 +12,6 @@ namespace Avalonia.Visuals.UnitTests; /// public class MatrixTests { - private double ReducePrecision(double input) - { - return Math.Truncate(input * 10000); - } - /// /// Because Avalonia is working internally with doubles, but System.Numerics Vector and Matrix implementations /// only make use of floats, we need to reduce precision, comparing them. It should be sufficient to compare @@ -26,6 +21,8 @@ public class MatrixTests /// The actual transformed point private void AssertCoordinatesEqualWithReducedPrecision(Vector2 expected, Point actual) { + double ReducePrecision(double input) => Math.Truncate(input * 10000); + var expectedX = ReducePrecision(expected.X); var expectedY = ReducePrecision(expected.Y); From 69d65f6c95715934ff295780c05fe846d22052b7 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 20:40:47 +0100 Subject: [PATCH 049/820] Revert unwanted change. --- samples/ControlCatalog.Android/Resources/Resource.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index b1ca548e2c..dccc3f7159 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace ControlCatalog.Android { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] public partial class Resource { From b07a1b0222e194b559445a0a8fac0c7f903b8512 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Fri, 28 Jan 2022 13:53:03 +0100 Subject: [PATCH 050/820] Add double.Epsilon check instead of 0. --- src/Avalonia.Visuals/Media/Rotate3DTransform.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index dbcf749789..e899f6b49b 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -164,13 +164,13 @@ public class Rotate3DTransform : Transform var matrix44 = Matrix4x4.Identity; var centerSum = CenterX + CenterY + CenterZ; - if (centerSum != 0) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(AngleX)); if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(AngleY)); if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(AngleZ)); - if (centerSum != 0) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); if (Depth != 0) { From 75788233170777fe8305bb57df8a72e11b0fd564 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Fri, 28 Jan 2022 15:36:25 +0100 Subject: [PATCH 051/820] Add copy of values as basis for matrix transform. It is not guaranteed, that values will not change during calculation. --- .../Media/Rotate3DTransform.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index e899f6b49b..a24d6b26c3 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -162,20 +162,29 @@ public class Rotate3DTransform : Transform get { var matrix44 = Matrix4x4.Identity; - var centerSum = CenterX + CenterY + CenterZ; + //Copy values first, because it's not guaranteed, that values will not change during calculation + var (copyCenterX, + copyCenterY, + copyCenterZ, + copyAngleX, + copyAngleY, + copyAngleZ, + copyDepth) = (CenterX, CenterY, CenterZ, AngleX, AngleY, AngleZ, Depth); - if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + var centerSum = copyCenterX + copyCenterY + copyCenterZ; + + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)copyCenterX, -(float)copyCenterY, -(float)copyCenterZ); - if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(AngleX)); - if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(AngleY)); - if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(AngleZ)); + if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); + if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); + if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); - if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)copyCenterX, (float)copyCenterY, (float)copyCenterZ); - if (Depth != 0) + if (copyDepth != 0) { var perspectiveMatrix = Matrix4x4.Identity; - perspectiveMatrix.M34 = -1 / (float)Depth; + perspectiveMatrix.M34 = -1 / (float)copyDepth; matrix44 *= perspectiveMatrix; } From e571559b64ca6c6c832aa6bf90e5db2743f3f74f Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Fri, 28 Jan 2022 16:47:22 +0100 Subject: [PATCH 052/820] Fix missing use of local copies. --- src/Avalonia.Visuals/Media/Rotate3DTransform.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index a24d6b26c3..306363ec39 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -175,9 +175,9 @@ public class Rotate3DTransform : Transform if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)copyCenterX, -(float)copyCenterY, -(float)copyCenterZ); - if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); - if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); - if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); + if (copyAngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); + if (copyAngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); + if (copyAngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)copyCenterX, (float)copyCenterY, (float)copyCenterZ); From b2b4014b1c6e5805e7c2330582124aa2f73f34ca Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 3 Feb 2022 14:58:28 +0300 Subject: [PATCH 053/820] Make DispatcherPriority to be a struct with static readonly field values In future versions this will allow us to extend priority lists without breaking binary compatibility --- src/Avalonia.Base/ApiCompatBaseline.txt | 3 +- .../Threading/AvaloniaScheduler.cs | 2 +- src/Avalonia.Base/Threading/Dispatcher.cs | 12 +-- .../Threading/DispatcherPriority.cs | 97 ++++++++++++++----- .../Threading/DispatcherTimer.cs | 4 +- src/Avalonia.Base/Threading/IDispatcher.cs | 12 +-- .../Diagnostics/ViewModels/MainViewModel.cs | 3 +- .../Avalonia.UnitTests/ImmediateDispatcher.cs | 12 +-- 8 files changed, 96 insertions(+), 49 deletions(-) diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt index 4701a83175..7f378d2f65 100644 --- a/src/Avalonia.Base/ApiCompatBaseline.txt +++ b/src/Avalonia.Base/ApiCompatBaseline.txt @@ -1,3 +1,4 @@ Compat issues with assembly Avalonia.Base: +MembersMustExist : Member 'public System.Int32 System.Int32 Avalonia.Threading.DispatcherPriority.value__' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Threading.IDispatcher.Post(System.Action, T, Avalonia.Threading.DispatcherPriority)' is present in the implementation but not in the contract. -Total Issues: 1 +Total Issues: 2 diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index 397826df53..6423d86e7c 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -46,7 +46,7 @@ namespace Avalonia.Threading { composite.Add(action(this, state)); } - }, DispatcherPriority.DataBind); + }, DispatcherPriority.Background); composite.Add(cancellation); diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index 49cee441d0..2eb2e7c01f 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -83,42 +83,42 @@ namespace Avalonia.Threading _jobRunner.HasJobsWithPriority(minimumPriority); /// - public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Action action, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); return _jobRunner.InvokeAsync(action, priority); } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority = default) { _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority); } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority = default) { _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority).Unwrap(); } /// - public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func> function, DispatcherPriority priority = default) { _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority).Unwrap(); } /// - public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); _jobRunner.Post(action, priority); } /// - public void Post(Action action, T arg, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, T arg, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); _jobRunner.Post(action, arg, priority); diff --git a/src/Avalonia.Base/Threading/DispatcherPriority.cs b/src/Avalonia.Base/Threading/DispatcherPriority.cs index a2b4b86bac..a93e4f406d 100644 --- a/src/Avalonia.Base/Threading/DispatcherPriority.cs +++ b/src/Avalonia.Base/Threading/DispatcherPriority.cs @@ -1,74 +1,121 @@ +using System; + namespace Avalonia.Threading { /// /// Defines the priorities with which jobs can be invoked on a . /// - // TODO: These are copied from WPF - many won't apply to Avalonia. - public enum DispatcherPriority + public readonly struct DispatcherPriority : IEquatable, IComparable { + /// + /// The integer value of the priority + /// + public int Value { get; } + + private DispatcherPriority(int value) + { + Value = value; + } + /// /// Minimum possible priority /// - MinValue = 1, - + public static readonly DispatcherPriority MinValue = new(0); + /// /// The job will be processed when the system is idle. /// - SystemIdle = 1, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority SystemIdle = MinValue; /// /// The job will be processed when the application is idle. /// - ApplicationIdle = 2, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority ApplicationIdle = MinValue; /// /// The job will be processed after background operations have completed. /// - ContextIdle = 3, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority ContextIdle = MinValue; /// - /// The job will be processed after other non-idle operations have completed. + /// The job will be processed with normal priority. /// - Background = 4, + public static readonly DispatcherPriority Normal = MinValue; /// - /// The job will be processed with the same priority as input. + /// The job will be processed after other non-idle operations have completed. /// - Input = 5, + public static readonly DispatcherPriority Background = new(1); /// - /// The job will be processed after layout and render but before input. + /// The job will be processed with the same priority as input. /// - Loaded = 6, + public static readonly DispatcherPriority Input = new(2); /// - /// The job will be processed with the same priority as render. + /// The job will be processed after layout and render but before input. /// - Render = 7, + public static readonly DispatcherPriority Loaded = new(3); /// /// The job will be processed with the same priority as render. /// - Layout = 8, - + public static readonly DispatcherPriority Render = new(5); + /// - /// The job will be processed with the same priority as data binding. + /// The job will be processed with the same priority as render. /// - DataBind = 9, + public static readonly DispatcherPriority Layout = new(6); /// - /// The job will be processed with normal priority. + /// The job will be processed with the same priority as data binding. /// - Normal = 10, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority DataBind = MinValue; /// /// The job will be processed before other asynchronous operations. /// - Send = 11, - + public static readonly DispatcherPriority Send = new(7); + /// /// Maximum possible priority /// - MaxValue = 11 + public static readonly DispatcherPriority MaxValue = Send; + + // Note: unlike ctor this one is validating + public static DispatcherPriority FromValue(int value) + { + if (value < MinValue.Value || value > MaxValue.Value) + throw new ArgumentOutOfRangeException(nameof(value)); + return new DispatcherPriority(value); + } + + public static implicit operator int(DispatcherPriority priority) => priority.Value; + + public static implicit operator DispatcherPriority(int value) => FromValue(value); + + /// + public bool Equals(DispatcherPriority other) => Value == other.Value; + + /// + public override bool Equals(object? obj) => obj is DispatcherPriority other && Equals(other); + + /// + public override int GetHashCode() => Value.GetHashCode(); + + public static bool operator ==(DispatcherPriority left, DispatcherPriority right) => left.Value == right.Value; + + public static bool operator !=(DispatcherPriority left, DispatcherPriority right) => left.Value != right.Value; + + public static bool operator <(DispatcherPriority left, DispatcherPriority right) => left.Value < right.Value; + + public static bool operator >(DispatcherPriority left, DispatcherPriority right) => left.Value > right.Value; + + public static bool operator <=(DispatcherPriority left, DispatcherPriority right) => left.Value <= right.Value; + + public static bool operator >=(DispatcherPriority left, DispatcherPriority right) => left.Value >= right.Value; + + /// + public int CompareTo(DispatcherPriority other) => Value.CompareTo(other.Value); } -} +} \ No newline at end of file diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 93023b90a5..0c25d89722 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -123,7 +123,7 @@ namespace Avalonia.Threading /// The interval at which to tick. /// The priority to use. /// An used to cancel the timer. - public static IDisposable Run(Func action, TimeSpan interval, DispatcherPriority priority = DispatcherPriority.Normal) + public static IDisposable Run(Func action, TimeSpan interval, DispatcherPriority priority = default) { var timer = new DispatcherTimer(priority) { Interval = interval }; @@ -152,7 +152,7 @@ namespace Avalonia.Threading public static IDisposable RunOnce( Action action, TimeSpan interval, - DispatcherPriority priority = DispatcherPriority.Normal) + DispatcherPriority priority = default) { interval = (interval != TimeSpan.Zero) ? interval : TimeSpan.FromTicks(1); diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index cd5add70d4..eccd42bd4e 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -24,7 +24,7 @@ namespace Avalonia.Threading /// /// The method. /// The priority with which to invoke the method. - void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + void Post(Action action, DispatcherPriority priority = default); /// /// Posts an action that will be invoked on the dispatcher thread. @@ -33,7 +33,7 @@ namespace Avalonia.Threading /// The method to call. /// The argument of method to call. /// The priority with which to invoke the method. - void Post(Action action, T arg, DispatcherPriority priority = DispatcherPriority.Normal); + void Post(Action action, T arg, DispatcherPriority priority = default); /// /// Invokes a action on the dispatcher thread. @@ -41,7 +41,7 @@ namespace Avalonia.Threading /// The method. /// The priority with which to invoke the method. /// A task that can be used to track the method's execution. - Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Action action, DispatcherPriority priority = default); /// /// Invokes a method on the dispatcher thread. @@ -49,7 +49,7 @@ namespace Avalonia.Threading /// The method. /// The priority with which to invoke the method. /// A task that can be used to track the method's execution. - Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Func function, DispatcherPriority priority = default); /// /// Queues the specified work to run on the dispatcher thread and returns a proxy for the @@ -58,7 +58,7 @@ namespace Avalonia.Threading /// The work to execute asynchronously. /// The priority with which to invoke the method. /// A task that represents a proxy for the task returned by . - Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Func function, DispatcherPriority priority = default); /// /// Queues the specified work to run on the dispatcher thread and returns a proxy for the @@ -67,6 +67,6 @@ namespace Avalonia.Threading /// The work to execute asynchronously. /// The priority with which to invoke the method. /// A task that represents a proxy for the task returned by . - Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Func> function, DispatcherPriority priority = default); } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index e08c5bc8dd..140515eb40 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -163,8 +163,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch { } }, - TimeSpan.FromMilliseconds(0), - DispatcherPriority.ApplicationIdle); + TimeSpan.FromMilliseconds(0)); } RaiseAndSetIfChanged(ref _content, value); diff --git a/tests/Avalonia.UnitTests/ImmediateDispatcher.cs b/tests/Avalonia.UnitTests/ImmediateDispatcher.cs index 5f0d41590f..03c89732f3 100644 --- a/tests/Avalonia.UnitTests/ImmediateDispatcher.cs +++ b/tests/Avalonia.UnitTests/ImmediateDispatcher.cs @@ -16,39 +16,39 @@ namespace Avalonia.UnitTests } /// - public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, DispatcherPriority priority) { action(); } /// - public void Post(Action action, T arg, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, T arg, DispatcherPriority priority) { action(arg); } /// - public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Action action, DispatcherPriority priority) { action(); return Task.CompletedTask; } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority) { var result = function(); return Task.FromResult(result); } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority) { return function(); } /// - public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func> function, DispatcherPriority priority) { return function(); } From 9318ce1612579cb5d1f7b36c33012137f955e9ac Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 7 Feb 2022 16:28:40 +0100 Subject: [PATCH 054/820] Added failing tests for #7552. --- .../LayoutableTests.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index a8aa0bbf0e..514eae12c0 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -320,6 +320,71 @@ namespace Avalonia.Layout.UnitTests Times.Once); } + [Fact] + public void Making_Control_Invisible_Should_Invalidate_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + + child.IsVisible = false; + + Assert.False(target.IsMeasureValid); + Assert.False(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + } + + [Fact] + public void Measuring_Invisible_Control_Should_Not_Invalidate_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.Equal(new Size(100, 0), child.DesiredSize); + + child.IsVisible = false; + Assert.Equal(default, child.DesiredSize); + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.Equal(default, child.DesiredSize); + } + private class TestLayoutable : Layoutable { public Size ArrangeSize { get; private set; } From 1f3cb4fa00dfc6e443d92a1658517653c039066b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 7 Feb 2022 16:29:39 +0100 Subject: [PATCH 055/820] Invalidate parent measure when child visibility changes. Fixes #7552 but breaks `ListBoxTests.LayoutManager_Should_Measure_Arrange_All`. --- src/Avalonia.Layout/Layoutable.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 09e0c4263a..516a70c3c9 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -141,7 +141,6 @@ namespace Avalonia.Layout static Layoutable() { AffectsMeasure( - IsVisibleProperty, WidthProperty, HeightProperty, MinWidthProperty, @@ -781,6 +780,17 @@ namespace Avalonia.Layout { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsVisibleProperty) + { + DesiredSize = default; + this.GetVisualParent()?.ChildDesiredSizeChanged(this); + } + } + /// protected sealed override void OnVisualParentChanged(IVisual? oldParent, IVisual? newParent) { From 662dbf78649c60fbdee853bb4076f5ac3836bf7e Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 9 Feb 2022 19:18:25 +0100 Subject: [PATCH 056/820] Switch from list to array. Fix flickering. --- .../Transitions/Rotate3DTransition.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index 56567f5fea..ebc8f44278 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Avalonia.Collections; using Avalonia.Media; using Avalonia.Styling; @@ -41,7 +39,7 @@ public class Rotate3DTransition: PageSlide return; } - var tasks = new List(); + var tasks = new Task[2]; var parent = GetVisualParent(from, to); var (rotateProperty, center) = Orientation switch { @@ -53,12 +51,13 @@ public class Rotate3DTransition: PageSlide var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = Depth ?? center}; var centerZSetter = new Setter {Property = Rotate3DTransform.CenterZProperty, Value = -center / 2}; - KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex) => + KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex, bool isVisible = true) => new() { Setters = { new Setter { Property = rotateProperty, Value = rotation }, new Setter { Property = Visual.ZIndexProperty, Value = zIndex }, + new Setter { Property = Visual.IsVisibleProperty, Value = isVisible }, centerZSetter, depthSetter }, @@ -71,15 +70,16 @@ public class Rotate3DTransition: PageSlide { Easing = SlideOutEasing, Duration = Duration, + FillMode = FillMode.Forward, Children = { CreateKeyFrame(0d, 0d, 2), CreateKeyFrame(0.5d, 45d * (forward ? -1 : 1), 1), - CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1) + CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1, isVisible: false) } }; - tasks.Add(animation.RunAsync(from, null, cancellationToken)); + tasks[0] = animation.RunAsync(from, null, cancellationToken); } if (to != null) @@ -89,6 +89,7 @@ public class Rotate3DTransition: PageSlide { Easing = SlideInEasing, Duration = Duration, + FillMode = FillMode.Forward, Children = { CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1), @@ -97,23 +98,23 @@ public class Rotate3DTransition: PageSlide } }; - tasks.Add(animation.RunAsync(to, null, cancellationToken)); + tasks[1] = animation.RunAsync(to, null, cancellationToken); } await Task.WhenAll(tasks); if (!cancellationToken.IsCancellationRequested) { + if (to != null) + { + to.ZIndex = 2; + } + if (from != null) { from.IsVisible = false; from.ZIndex = 1; } - - if (to != null) - { - to.ZIndex = 2; - } } } } From 9c5b3ab4d8c7b454e5a33ae65a2d8d501fbf4ca7 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 10 Feb 2022 13:22:33 +0100 Subject: [PATCH 057/820] Fix null reference exception for Tasks.WhenAll. --- .../Animation/Transitions/Rotate3DTransition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index ebc8f44278..01d04dff6b 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -39,7 +39,7 @@ public class Rotate3DTransition: PageSlide return; } - var tasks = new Task[2]; + var tasks = new Task[from != null && to != null ? 2 : 1]; var parent = GetVisualParent(from, to); var (rotateProperty, center) = Orientation switch { From 20c28efd90a7f4a6a636a7bb70745fcbbab8d473 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 10 Feb 2022 13:24:33 +0100 Subject: [PATCH 058/820] Improve previous fix. --- .../Animation/Transitions/Rotate3DTransition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index 01d04dff6b..60c7a97ced 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -98,7 +98,7 @@ public class Rotate3DTransition: PageSlide } }; - tasks[1] = animation.RunAsync(to, null, cancellationToken); + tasks[from != null ? 1 : 0] = animation.RunAsync(to, null, cancellationToken); } await Task.WhenAll(tasks); From 38e2ab818a5b68becab6be93b9259e07bc9ddaa7 Mon Sep 17 00:00:00 2001 From: Luthfi Tri Atmaja Date: Wed, 16 Feb 2022 13:09:49 +0700 Subject: [PATCH 059/820] introduce gtk_file_chooser_set_current_folder --- src/Avalonia.X11/NativeDialogs/Gtk.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs index a0f146afa8..73e6a2d6a4 100644 --- a/src/Avalonia.X11/NativeDialogs/Gtk.cs +++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs @@ -195,7 +195,10 @@ namespace Avalonia.X11.NativeDialogs [DllImport(GtkName)] public static extern void gtk_file_chooser_set_current_name(IntPtr chooser, Utf8Buffer file); - + + [DllImport(GtkName)] + public static extern void gtk_file_chooser_set_current_folder(IntPtr chooser, Utf8Buffer file); + [DllImport(GtkName)] public static extern IntPtr gtk_file_filter_new(); From 1888ab9fe5388d8058dbac6e91aaf242e2509621 Mon Sep 17 00:00:00 2001 From: Luthfi Tri Atmaja Date: Wed, 16 Feb 2022 13:28:06 +0700 Subject: [PATCH 060/820] navigate to initial directory --- .../NativeDialogs/GtkNativeFileDialogs.cs | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs index 1a6514eb03..9539f024b7 100644 --- a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs +++ b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs @@ -15,8 +15,9 @@ namespace Avalonia.X11.NativeDialogs class GtkSystemDialog : ISystemDialogImpl { private Task _initialized; - private unsafe Task ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action, - bool multiSelect, string initialFileName, IEnumerable filters, string defaultExtension, bool overwritePrompt) + + private unsafe Task ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action, + bool multiSelect, string initialDirectory, string initialFileName, IEnumerable filters, string defaultExtension, bool overwritePrompt) { IntPtr dlg; using (var name = new Utf8Buffer(title)) @@ -44,7 +45,7 @@ namespace Avalonia.X11.NativeDialogs filtersDic[filter] = f; using (var b = new Utf8Buffer(f.Name)) gtk_file_filter_set_name(filter, b); - + foreach (var e in f.Extensions) using (var b = new Utf8Buffer("*." + e)) gtk_file_filter_add_pattern(filter, b); @@ -100,17 +101,32 @@ namespace Avalonia.X11.NativeDialogs gtk_dialog_add_button(dlg, open, GtkResponseType.Accept); using (var open = new Utf8Buffer("Cancel")) gtk_dialog_add_button(dlg, open, GtkResponseType.Cancel); + + if (initialDirectory != null) + { + using var dir = new Utf8Buffer(initialDirectory); + gtk_file_chooser_set_current_folder(dlg, dir); + } + if (initialFileName != null) - using (var fn = new Utf8Buffer(initialFileName)) + { + // gtk_file_chooser_set_filename() expects full path + using var fn = action == GtkFileChooserAction.Open + ? new Utf8Buffer(Path.Combine(initialDirectory ?? "", initialFileName)) + : new Utf8Buffer(initialFileName); + + if (action == GtkFileChooserAction.Save) { - if (action == GtkFileChooserAction.Save) - gtk_file_chooser_set_current_name(dlg, fn); - else - gtk_file_chooser_set_filename(dlg, fn); + gtk_file_chooser_set_current_name(dlg, fn); } + else + { + gtk_file_chooser_set_filename(dlg, fn); + } + } gtk_file_chooser_set_do_overwrite_confirmation(dlg, overwritePrompt); - + gtk_window_present(dlg); return tcs.Task; } @@ -144,13 +160,15 @@ namespace Avalonia.X11.NativeDialogs var platformImpl = parent?.PlatformImpl; - return await await RunOnGlibThread( - () => ShowDialog(dialog.Title, platformImpl, - dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, - (dialog as OpenFileDialog)?.AllowMultiple ?? false, - Path.Combine(string.IsNullOrEmpty(dialog.Directory) ? "" : dialog.Directory, - string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters, - (dialog as SaveFileDialog)?.DefaultExtension, (dialog as SaveFileDialog)?.ShowOverwritePrompt ?? false)); + return await await RunOnGlibThread(() => ShowDialog( + dialog.Title, platformImpl, + dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, + (dialog as OpenFileDialog)?.AllowMultiple ?? false, + dialog.Directory, + dialog.InitialFileName, + dialog.Filters, + (dialog as SaveFileDialog)?.DefaultExtension, + (dialog as SaveFileDialog)?.ShowOverwritePrompt ?? false)); } public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) @@ -161,12 +179,20 @@ namespace Avalonia.X11.NativeDialogs return await await RunOnGlibThread(async () => { - var res = await ShowDialog(dialog.Title, platformImpl, - GtkFileChooserAction.SelectFolder, false, dialog.Directory, null, null, false); + var res = await ShowDialog( + dialog.Title, + platformImpl,GtkFileChooserAction.SelectFolder, + false, + dialog.Directory, + null, + null, + null, + false); + return res?.FirstOrDefault(); }); } - + async Task EnsureInitialized() { if (_initialized == null) _initialized = StartGtk(); @@ -174,7 +200,7 @@ namespace Avalonia.X11.NativeDialogs if (!(await _initialized)) throw new Exception("Unable to initialize GTK on separate thread"); } - + void UpdateParent(IntPtr chooser, IWindowImpl parentWindow) { var xid = parentWindow.Handle.Handle; From eefe009a810473abb0b0d4699ff8d01b9fc041a6 Mon Sep 17 00:00:00 2001 From: Luthfi Tri Atmaja Date: Wed, 16 Feb 2022 13:38:48 +0700 Subject: [PATCH 061/820] update dialogs sample page --- .../ControlCatalog/Pages/DialogsPage.xaml.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index e96b7aff08..a7af9c5cb7 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -42,13 +42,17 @@ namespace ControlCatalog.Pages this.FindControl 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 064/820] 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 065/820] 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 066/820] 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 067/820] 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 068/820] 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 069/820] 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 070/820] 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 071/820] 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 072/820] 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 073/820] 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 074/820] 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 075/820] 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 076/820] 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 077/820] 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 078/820] 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 079/820] 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 080/820] 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 081/820] 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 082/820] 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 083/820] 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 084/820] 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 085/820] 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 086/820] 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 a19dc794a57f63444094c83511f4bac3a0a55e2c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 11:58:26 +0000 Subject: [PATCH 087/820] add contains exclusive. --- src/Avalonia.Controls/Platform/ScreenHelper.cs | 2 +- src/Avalonia.Visuals/Rect.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/ScreenHelper.cs b/src/Avalonia.Controls/Platform/ScreenHelper.cs index 07affb5ecc..0bd2be69d0 100644 --- a/src/Avalonia.Controls/Platform/ScreenHelper.cs +++ b/src/Avalonia.Controls/Platform/ScreenHelper.cs @@ -11,7 +11,7 @@ namespace Avalonia.Platform { foreach (Screen screen in screens) { - if (screen.Bounds.Contains(point)) + if (screen.Bounds.ContainsExclusive(point)) { return screen; } diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 6d7d6c2e54..7930228d99 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -252,6 +252,18 @@ namespace Avalonia return p.X >= _x && p.X <= _x + _width && p.Y >= _y && p.Y <= _y + _height; } + + /// + /// Determines whether a point is in the bounds of the rectangle, exclusive of the + /// rectangle's bottom/right edge. + /// + /// The point. + /// true if the point is in the bounds of the rectangle; otherwise false. + public bool ContainsExclusive(Point p) + { + return p.X >= _x && p.X < _x + _width && + p.Y >= _y && p.Y < _y + _height; + } /// /// Determines whether the rectangle fully contains another rectangle. From a643ce6297046f4dc3ab9c89e9d6bfb8604d9463 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 12:14:06 +0000 Subject: [PATCH 088/820] add containsexclusive to pixelrect --- src/Avalonia.Visuals/Media/PixelRect.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Avalonia.Visuals/Media/PixelRect.cs b/src/Avalonia.Visuals/Media/PixelRect.cs index 0059a4b483..855ba104ad 100644 --- a/src/Avalonia.Visuals/Media/PixelRect.cs +++ b/src/Avalonia.Visuals/Media/PixelRect.cs @@ -168,6 +168,18 @@ namespace Avalonia { return p.X >= X && p.X <= Right && p.Y >= Y && p.Y <= Bottom; } + + /// + /// Determines whether a point is in the bounds of the rectangle, exclusive of the + /// rectangle's bottom/right edge. + /// + /// The point. + /// true if the point is in the bounds of the rectangle; otherwise false. + public bool ContainsExclusive(PixelPoint p) + { + return p.X >= X && p.X < X + Width && + p.Y >= Y && p.Y < Y + Height; + } /// /// Determines whether the rectangle fully contains another rectangle. From a740301faca1785a8b8e639439ceaa9fcf2cec8d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 12:16:34 +0000 Subject: [PATCH 089/820] fix nullability of local var. --- src/Avalonia.Controls/Window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 505f3f7416..feacc3e63a 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -879,7 +879,7 @@ namespace Avalonia.Controls if (startupLocation == WindowStartupLocation.CenterScreen) { - Screen screen = null; + Screen? screen = null; if (owner is not null) { From 2f35cd1b7b42d505ce215a1b4d4c2d11e7af98b9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 12:17:20 +0000 Subject: [PATCH 090/820] fix nullability error. --- src/Avalonia.Controls/WindowBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index d3c940c07c..12ba143c8a 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -60,7 +60,7 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver? dependencyResolver) : base(impl, dependencyResolver) { - Screens = new Screens(PlatformImpl?.Screen); + Screens = new Screens(impl.Screen); impl.Activated = HandleActivated; impl.Deactivated = HandleDeactivated; impl.PositionChanged = HandlePositionChanged; From b902067196fea78365ec147078879fb42d4f0d97 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 9 Mar 2022 09:02:02 +0000 Subject: [PATCH 091/820] 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 092/820] 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 093/820] 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 094/820] 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 095/820] 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 ed19f5fc9a17defbf4ce56c77e70c47dc387de9e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 10 Mar 2022 00:30:03 +0000 Subject: [PATCH 096/820] 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 097/820] 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 098/820] 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 099/820] 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 100/820] 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 101/820] 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 102/820] 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 103/820] 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 104/820] 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 105/820] 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 106/820] 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 107/820] 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 108/820] 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 109/820] 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 110/820] 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 111/820] 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 112/820] 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 113/820] 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 114/820] 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 115/820] 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 116/820] 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 117/820] 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 118/820] 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 119/820] 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 120/820] 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 121/820] 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 122/820] 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 123/820] 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 124/820] 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 125/820] 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 126/820] 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 127/820] 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); + } +} + From ccbd277e8bf46f31efeeb7c8a9b428b4a9a9eaf6 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 24 Nov 2021 10:23:18 +0100 Subject: [PATCH 128/820] feat(ContentPresenter): Content of ContentPresenter should become DataContext of the subtree whenever ContentTemplate is not null --- .../Presenters/ContentPresenter.cs | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 9886dd913a..93acd88fb1 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -107,9 +107,6 @@ namespace Avalonia.Controls.Presenters AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsArrange(HorizontalContentAlignmentProperty, VerticalContentAlignmentProperty); AffectsMeasure(BorderThicknessProperty, PaddingProperty); - ContentProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e)); - ContentTemplateProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e)); - TemplatedParentProperty.Changed.AddClassHandler((x, e) => x.TemplatedParentChanged(e)); } public ContentPresenter() @@ -240,6 +237,21 @@ namespace Avalonia.Controls.Presenters } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + switch (change.Property.Name) + { + case nameof(Content): + case nameof(ContentTemplate): + ContentChanged(change); + break; + case nameof(TemplatedParent): + TemplatedParentChanged(change); + break; + } + } + /// /// Updates the control based on the control's . /// @@ -254,8 +266,14 @@ namespace Avalonia.Controls.Presenters public void UpdateChild() { var content = Content; + UpdateChild(content); + } + + private void UpdateChild(object? content) + { + var contentTemplate = ContentTemplate; var oldChild = Child; - var newChild = CreateChild(); + var newChild = CreateChild(content, oldChild, contentTemplate); var logicalChildren = Host?.LogicalChildren ?? LogicalChildren; // Remove the old child if we're not recycling it. @@ -271,7 +289,7 @@ namespace Avalonia.Controls.Presenters } // Set the DataContext if the data isn't a control. - if (!(content is IControl)) + if (contentTemplate is { } || !(content is IControl)) { DataContext = content; } @@ -299,6 +317,7 @@ namespace Avalonia.Controls.Presenters } _createdChild = true; + } /// @@ -325,18 +344,23 @@ namespace Avalonia.Controls.Presenters { var content = Content; var oldChild = Child; + return CreateChild(content, oldChild, ContentTemplate); + } + + private IControl? CreateChild(object? content, IControl? oldChild, IDataTemplate? template) + { var newChild = content as IControl; // We want to allow creating Child from the Template, if Content is null. // But it's important to not use DataTemplates, otherwise we will break content presenters in many places, // otherwise it will blow up every ContentPresenter without Content set. - if (newChild == null - && (content != null || ContentTemplate != null)) + if ((newChild == null + && (content != null || template != null)) || (newChild is { } && template is { })) { - var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? + var dataTemplate = this.FindDataTemplate(content, template) ?? ( - RecognizesAccessKey - ? FuncDataTemplate.Access + RecognizesAccessKey + ? FuncDataTemplate.Access : FuncDataTemplate.Default ); @@ -446,7 +470,14 @@ namespace Avalonia.Controls.Presenters if (((ILogical)this).IsAttachedToLogicalTree) { - UpdateChild(); + if (e.Property.Name == nameof(Content)) + { + UpdateChild(e.NewValue); + } + else + { + UpdateChild(); + } } else if (Child != null) { From e2e3eba71d8e74574c7bd9416832f4b30e620b54 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 10 Mar 2022 18:35:22 +0100 Subject: [PATCH 129/820] add test --- .../ContentPresenterTests_InTemplate.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs index a579e869b0..76a47ea3d1 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs @@ -353,6 +353,30 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Null(target.Host); } + [Fact] + public void Content_Should_Become_Datacontext_When_ControlTemplate_Is_Not_Null() + { + var (target, _) = CreateTarget(); + + var textBlock = new TextBlock + { + [!TextBlock.TextProperty] = new Binding("Name"), + }; + + var canvas = new Canvas() + { + Name ="Canvas", + }; + + target.ContentTemplate = new FuncDataTemplate((_, __) => textBlock); + target.Content = canvas; + + Assert.NotNull(target.DataContext); + Assert.Equal(canvas, target.DataContext); + Assert.Equal("Canvas", textBlock.Text); + } + + (ContentPresenter presenter, ContentControl templatedParent) CreateTarget() { var templatedParent = new ContentControl From ad9b7c1027f50f11e89f9117fcecda2cbdb85377 Mon Sep 17 00:00:00 2001 From: Tim U Date: Fri, 11 Mar 2022 12:47:07 +0100 Subject: [PATCH 130/820] Switch disposing order for TwoWayBindingDisposable --- src/Avalonia.Base/Data/BindingOperations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index 5353e8175d..0c38f78c83 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/src/Avalonia.Base/Data/BindingOperations.cs @@ -116,8 +116,8 @@ namespace Avalonia.Data return; } - _first.Dispose(); _second.Dispose(); + _first.Dispose(); _isDisposed = true; } From 3eddf5cac4d081481675412ad7bdf0cc500e0b60 Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Fri, 11 Mar 2022 14:03:42 +0200 Subject: [PATCH 131/820] first work --- samples/ControlCatalog/MainView.xaml | 8 +++ samples/ControlCatalog/MainView.xaml.cs | 9 +++ src/Avalonia.Controls/Control.cs | 74 +++++++++++++++++++++++++ src/Avalonia.Controls/TextBlock.cs | 2 + src/Avalonia.Controls/TextBox.cs | 2 + 5 files changed, 95 insertions(+) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index facce2aa82..35fe45a25f 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -187,6 +187,14 @@ Mica + + + LeftToRight + RightToLeft + + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 79cf07c8d9..e8ea39abbb 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -76,6 +76,15 @@ namespace ControlCatalog } }; + var flowDirections = this.Find("FlowDirection"); + flowDirections.SelectionChanged += (sender, e) => + { + if (flowDirections.SelectedItem is FlowDirection flowDirection) + { + this.FlowDirection = flowDirection; + } + }; + var decorations = this.Find("Decorations"); decorations.SelectionChanged += (sender, e) => { diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index cec662aad8..265f46be5e 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -309,5 +309,79 @@ namespace Avalonia.Controls } } } + + static Control() + { + AffectsArrange(FlowDirectionProperty); + } + + private bool _mirrorApplied; + + protected virtual bool ShouldBeMirroredIfRightToLeft() + { + if (Parent is Control parent) + { + return parent.ShouldBeMirroredIfRightToLeft(); + } + else + { + return true; + } + } + + protected override void ArrangeCore(Rect finalRect) + { + base.ArrangeCore(finalRect); + + FlowDirection parentFD = FlowDirection.LeftToRight; + FlowDirection thisFD = FlowDirection; + bool shouldBeMirroredIfRightToLeft = ShouldBeMirroredIfRightToLeft(); + + if (Parent is Control control) + { + parentFD = control.FlowDirection; + } + + bool shouldMirror; + if (shouldBeMirroredIfRightToLeft) + { + shouldMirror = ShuoldApplyMirrorTransform(parentFD, thisFD); + if (this is PopupRoot && thisFD == FlowDirection.RightToLeft) + { + shouldMirror = true; + } + } + else + { + shouldMirror = ShuoldApplyMirrorTransform(parentFD, FlowDirection.LeftToRight); + } + + if (shouldMirror) + { + ApplyMirrorTransform(); + } + else + { + //RenderTransform = null; + } + } + + private void ApplyMirrorTransform() + { + if (_mirrorApplied) + { + return; + } + + var transform = new MatrixTransform(new Avalonia.Matrix(-1, 0, 0, 1, 0.0, 0.0)); + RenderTransform = transform; + _mirrorApplied = true; + } + + internal static bool ShuoldApplyMirrorTransform(FlowDirection parentFD, FlowDirection thisFD) + { + return ((parentFD == FlowDirection.LeftToRight && thisFD == FlowDirection.RightToLeft) || + (parentFD == FlowDirection.RightToLeft && thisFD == FlowDirection.LeftToRight)); + } } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 09f22612de..d29f094c38 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -612,5 +612,7 @@ namespace Avalonia.Controls { InvalidateTextLayout(); } + + protected override bool ShouldBeMirroredIfRightToLeft() => false; } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 4d71717776..76f7a185fe 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1504,5 +1504,7 @@ namespace Avalonia.Controls } } } + + protected override bool ShouldBeMirroredIfRightToLeft() => false; } } From ed85fb770ef2d0eb63244a1f765ec20243daf9e9 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Fri, 11 Mar 2022 22:10:35 +0800 Subject: [PATCH 132/820] Update ContentPresenterTests_InTemplate.cs --- .../Presenters/ContentPresenterTests_InTemplate.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs index 76a47ea3d1..c0d475842a 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs @@ -376,7 +376,6 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Equal("Canvas", textBlock.Text); } - (ContentPresenter presenter, ContentControl templatedParent) CreateTarget() { var templatedParent = new ContentControl From 6e8c5f9927222605879b65c715cff7da4b0c80f2 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Fri, 11 Mar 2022 22:11:18 +0800 Subject: [PATCH 133/820] Update ContentPresenterTests_InTemplate.cs --- .../Presenters/ContentPresenterTests_InTemplate.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs index c0d475842a..6d20c72674 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs @@ -354,7 +354,7 @@ namespace Avalonia.Controls.UnitTests.Presenters } [Fact] - public void Content_Should_Become_Datacontext_When_ControlTemplate_Is_Not_Null() + public void Content_Should_Become_DataContext_When_ControlTemplate_Is_Not_Null() { var (target, _) = CreateTarget(); @@ -365,7 +365,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var canvas = new Canvas() { - Name ="Canvas", + Name = "Canvas" }; target.ContentTemplate = new FuncDataTemplate((_, __) => textBlock); From 58df47fa0667c515e66287a5ca7550daf9d75ab4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Mar 2022 14:17:30 +0000 Subject: [PATCH 134/820] fix unit test. --- src/Avalonia.Controls/ApiCompatBaseline.txt | 4 +--- tests/Avalonia.Controls.UnitTests/WindowTests.cs | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index dc7b70229a..ce84a7fe84 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -72,6 +72,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: 70 -Total Issues: 69 -Total Issues: 66 +Total Issues: 73 diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index eb128ef038..f643a84e37 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -516,6 +516,8 @@ namespace Avalonia.Controls.UnitTests var screens = new Mock(); screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object, screen2.Object }); + screens.Setup(x => x.ScreenFromPoint(It.IsAny())).Returns(screen1.Object); + var windowImpl = MockWindowingPlatform.CreateWindowMock(); windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480)); From 9f02c649e75740957d5142646f569447b61dc51b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Mar 2022 16:11:06 +0000 Subject: [PATCH 135/820] make mica fallback to acrylic on compatible windows 10 --- src/Avalonia.Controls/WindowTransparencyLevel.cs | 5 +++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/WindowTransparencyLevel.cs b/src/Avalonia.Controls/WindowTransparencyLevel.cs index f416b5de91..d463f74a0e 100644 --- a/src/Avalonia.Controls/WindowTransparencyLevel.cs +++ b/src/Avalonia.Controls/WindowTransparencyLevel.cs @@ -22,6 +22,11 @@ /// AcrylicBlur, + /// + /// Force acrylic on some incompatible versions of Windows 10. + /// + ForceAcrylicBlur, + /// /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 /// diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3953a0995d..3e1b2a1609 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -396,6 +396,11 @@ namespace Avalonia.Win32 } } + if (Win32Platform.WindowsVersion.Major == 10 && effect == BlurEffect.Mica) + { + effect = BlurEffect.Acrylic; + } + _blurHost?.SetBlur(effect); return transparencyLevel; @@ -428,7 +433,8 @@ namespace Avalonia.Win32 break; case WindowTransparencyLevel.AcrylicBlur: - case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic. + case WindowTransparencyLevel.ForceAcrylicBlur: // hack-force acrylic. + case WindowTransparencyLevel.Mica: accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC; transparencyLevel = WindowTransparencyLevel.AcrylicBlur; break; From 321dcd38f2fdf0eba2df30967723f1b1c3e3f785 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Mar 2022 16:44:01 +0000 Subject: [PATCH 136/820] fix mica fallback check. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3e1b2a1609..41269ee7ca 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -396,7 +396,7 @@ namespace Avalonia.Win32 } } - if (Win32Platform.WindowsVersion.Major == 10 && effect == BlurEffect.Mica) + if (Win32Platform.WindowsVersion < WinUICompositorConnection.MinHostBackdropVersion && effect == BlurEffect.Mica) { effect = BlurEffect.Acrylic; } From 462ea76d2a22c4c80531b2552b02e5d7411c4bbb Mon Sep 17 00:00:00 2001 From: chylex Date: Sat, 12 Mar 2022 08:43:57 +0100 Subject: [PATCH 137/820] Fix wrong foreground TextBox color in Fluent theme --- src/Avalonia.Themes.Fluent/Controls/TextBox.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index e5b524beeb..c82757799d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -121,7 +121,7 @@ - From 3d0a13d2ba2a5fb733d0d0c6fd9337d435bf1e97 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 4 Mar 2022 22:04:48 +0100 Subject: [PATCH 138/820] Add TransitioningContentControl-DemoPage --- samples/ControlCatalog/ControlCatalog.csproj | 6 + samples/ControlCatalog/MainView.xaml | 3 + .../TransitioningContentControlPage.axaml | 35 ++++++ .../TransitioningContentControlPage.axaml.cs | 19 +++ ...ransitioningContentControlPageViewModel.cs | 110 ++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml create mode 100644 samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml.cs create mode 100644 samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index c0e24357ca..117a9fad61 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -28,5 +28,11 @@ + + + TransitioningContentControlPage.axaml + + + diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index facce2aa82..f3b52428ca 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -145,6 +145,9 @@ + + + diff --git a/samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml b/samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml new file mode 100644 index 0000000000..20cd78abd3 --- /dev/null +++ b/samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml @@ -0,0 +1,35 @@ + + + + + + + + The TransitioningContentControl control allows you to show a page transition whenever the Content changes. + + + + Select a transition + + + + + public static readonly StyledProperty TextTrimmingProperty = - AvaloniaProperty.Register(nameof(TextTrimming)); + AvaloniaProperty.Register(nameof(TextTrimming), defaultValue: TextTrimming.None); /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs index 12c40e4d59..1cac3243e3 100644 --- a/src/Avalonia.Visuals/Media/FormattedText.cs +++ b/src/Avalonia.Visuals/Media/FormattedText.cs @@ -854,19 +854,9 @@ namespace Avalonia.Media var lastRunProps = (GenericTextRunProperties)thatFormatRider.CurrentElement!; - TextCollapsingProperties trailingEllipsis; + TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps)); - if (_that._trimming == TextTrimming.CharacterEllipsis) - { - trailingEllipsis = new TextTrailingCharacterEllipsis(maxLineLength, lastRunProps); - } - else - { - Debug.Assert(_that._trimming == TextTrimming.WordEllipsis); - trailingEllipsis = new TextTrailingWordEllipsis(maxLineLength, lastRunProps); - } - - var collapsedLine = line.Collapse(trailingEllipsis); + var collapsedLine = line.Collapse(collapsingProperties); line = collapsedLine; } @@ -1121,11 +1111,6 @@ namespace Avalonia.Media { set { - if ((int)value < 0 || (int)value > (int)TextTrimming.WordEllipsis) - { - throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(TextTrimming)); - } - _trimming = value; _defaultParaProps.SetTextWrapping(_trimming == TextTrimming.None ? diff --git a/src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs b/src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs new file mode 100644 index 0000000000..78f15b724a --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs @@ -0,0 +1,16 @@ +using Avalonia.Media.TextFormatting; + +namespace Avalonia.Media +{ + public readonly struct TextCollapsingCreateInfo + { + public readonly double Width; + public readonly TextRunProperties TextRunProperties; + + public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties) + { + Width = width; + TextRunProperties = textRunProperties; + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs index 88ca596d2d..fb85766003 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs @@ -132,6 +132,29 @@ namespace Avalonia.Media.TextFormatting return length > 0; } + internal bool TryMeasureCharactersBackwards(double availableWidth, out int length, out double width) + { + length = 0; + width = 0; + + for (var i = ShapedBuffer.Length - 1; i >= 0; i--) + { + var advance = ShapedBuffer.GlyphAdvances[i]; + + if (width + advance > availableWidth) + { + break; + } + + Codepoint.ReadAt(GlyphRun.Characters, length, out var count); + + length += count; + width += advance; + } + + return length > 0; + } + internal SplitResult Split(int length) { if (IsReversed) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs index ffd65423a3..0d6ae84493 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Media.TextFormatting +using System.Collections.Generic; + +namespace Avalonia.Media.TextFormatting { /// /// Properties of text collapsing @@ -16,8 +18,9 @@ public abstract TextRun Symbol { get; } /// - /// Gets the style of collapsing + /// Collapses given text line. /// - public abstract TextCollapsingStyle Style { get; } + /// Text line to collapse. + public abstract IReadOnlyList? Collapse(TextLine textLine); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs deleted file mode 100644 index 1523cc4d9a..0000000000 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingStyle.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Avalonia.Media.TextFormatting -{ - /// - /// Text collapsing style - /// - public enum TextCollapsingStyle - { - /// - /// Collapse trailing characters - /// - TrailingCharacter, - - /// - /// Collapse trailing words - /// - TrailingWord, - } -} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs new file mode 100644 index 0000000000..6ca93a6116 --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using Avalonia.Media.TextFormatting.Unicode; + +namespace Avalonia.Media.TextFormatting +{ + internal class TextEllipsisHelper + { + public static List? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis) + { + var shapedTextRuns = textLine.TextRuns as List; + + if (shapedTextRuns is null) + { + return null; + } + + var runIndex = 0; + var currentWidth = 0.0; + var collapsedLength = 0; + var textRange = textLine.TextRange; + + // TODO: From where this is supposed to come now? + var flowDirection = FlowDirection.LeftToRight; + + var shapedSymbol = TextFormatterImpl.CreateSymbol(properties.Symbol, flowDirection); + + if (properties.Width < shapedSymbol.GlyphRun.Size.Width) + { + return new List(0); + } + + var availableWidth = properties.Width - shapedSymbol.Size.Width; + + while (runIndex < shapedTextRuns.Count) + { + var currentRun = shapedTextRuns[runIndex]; + + currentWidth += currentRun.Size.Width; + + if (currentWidth > availableWidth) + { + if (currentRun.TryMeasureCharacters(availableWidth, out var measuredLength)) + { + if (isWordEllipsis && measuredLength < textRange.End) + { + var currentBreakPosition = 0; + + var lineBreaker = new LineBreakEnumerator(currentRun.Text); + + while (currentBreakPosition < measuredLength && lineBreaker.MoveNext()) + { + var nextBreakPosition = lineBreaker.Current.PositionMeasure; + + if (nextBreakPosition == 0) + { + break; + } + + if (nextBreakPosition >= measuredLength) + { + break; + } + + currentBreakPosition = nextBreakPosition; + } + + measuredLength = currentBreakPosition; + } + } + + collapsedLength += measuredLength; + + var shapedTextCharacters = new List(shapedTextRuns.Count); + + if (collapsedLength > 0) + { + var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, collapsedLength); + + shapedTextCharacters.AddRange(splitResult.First); + + TextLineImpl.SortRuns(shapedTextCharacters); + } + + shapedTextCharacters.Add(shapedSymbol); + + return shapedTextCharacters; + } + + availableWidth -= currentRun.Size.Width; + + collapsedLength += currentRun.GlyphRun.Characters.Length; + + runIndex++; + } + + return null; + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index 4512890f08..0ff127694b 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -41,7 +41,7 @@ namespace Avalonia.Media.TextFormatting IBrush? foreground, TextAlignment textAlignment = TextAlignment.Left, TextWrapping textWrapping = TextWrapping.NoWrap, - TextTrimming textTrimming = TextTrimming.None, + TextTrimming? textTrimming = null, TextDecorationCollection? textDecorations = null, FlowDirection flowDirection = FlowDirection.LeftToRight, double maxWidth = double.PositiveInfinity, @@ -58,7 +58,7 @@ namespace Avalonia.Media.TextFormatting CreateTextParagraphProperties(typeface, fontSize, foreground, textAlignment, textWrapping, textDecorations, flowDirection, lineHeight); - _textTrimming = textTrimming; + _textTrimming = textTrimming ?? TextTrimming.None; _textStyleOverrides = textStyleOverrides; @@ -641,14 +641,7 @@ namespace Avalonia.Media.TextFormatting /// The . private TextCollapsingProperties GetCollapsingProperties(double width) { - return _textTrimming switch - { - TextTrimming.CharacterEllipsis => new TextTrailingCharacterEllipsis(width, - _paragraphProperties.DefaultTextRunProperties), - TextTrimming.WordEllipsis => new TextTrailingWordEllipsis(width, - _paragraphProperties.DefaultTextRunProperties), - _ => throw new ArgumentOutOfRangeException(), - }; + return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties)); } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs new file mode 100644 index 0000000000..f0f212b0fe --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using Avalonia.Utilities; + +namespace Avalonia.Media.TextFormatting +{ + /// + /// Ellipsis based on a fixed length leading prefix and suffix growing from the end. + /// + public class TextLeadingPrefixEllipsis : TextCollapsingProperties + { + private readonly int _prefixLength; + + /// + /// Construct a text trailing word ellipsis collapsing properties + /// + /// width in which collapsing is constrained to + /// text run properties of ellispis symbol + public TextLeadingPrefixEllipsis( + ReadOnlySlice ellipsis, + int prefixLength, + double width, + TextRunProperties textRunProperties + ) + { + _prefixLength = prefixLength; + Width = width; + Symbol = new TextCharacters(ellipsis, textRunProperties); + } + + /// + public sealed override double Width { get; } + + /// + public sealed override TextRun Symbol { get; } + + public override IReadOnlyList? Collapse(TextLine textLine) + { + var shapedTextRuns = textLine.TextRuns as List; + + if (shapedTextRuns is null) + { + return null; + } + + var runIndex = 0; + var currentWidth = 0.0; + + // TODO: From where this is supposed to come now? + var flowDirection = FlowDirection.LeftToRight; + + var shapedSymbol = TextFormatterImpl.CreateSymbol(Symbol, flowDirection); + + if (Width < shapedSymbol.GlyphRun.Size.Width) + { + return new List(0); + } + + var availableWidth = Width - shapedSymbol.Size.Width; + + while (runIndex < shapedTextRuns.Count) + { + var currentRun = shapedTextRuns[runIndex]; + + currentWidth += currentRun.Size.Width; + + if (currentWidth > availableWidth) + { + currentRun.TryMeasureCharacters(availableWidth, out var measuredLength); + + var shapedTextCharacters = new List(shapedTextRuns.Count); + + if (measuredLength > 0) + { + var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, Math.Min(_prefixLength, measuredLength)); + + shapedTextCharacters.AddRange(splitResult.First); + + TextLineImpl.SortRuns(shapedTextCharacters); + + shapedTextCharacters.Add(shapedSymbol); + + if (measuredLength > _prefixLength && splitResult.Second is not null) + { + var availableSuffixWidth = availableWidth; + + foreach (var run in splitResult.First) + { + availableSuffixWidth -= run.Size.Width; + } + + for (int i = splitResult.Second.Count - 1; i >= 0; i--) + { + var run = splitResult.Second[i]; + + if (run.TryMeasureCharactersBackwards(availableSuffixWidth, out int suffixCount, out double suffixWidth)) + { + availableSuffixWidth -= suffixWidth; + + if (suffixCount > 0) + { + var splitSuffix = run.Split(run.TextSourceLength - suffixCount); + + shapedTextCharacters.Add(splitSuffix.Second!); + } + } + } + } + } + else + { + shapedTextCharacters.Add(shapedSymbol); + } + + return shapedTextCharacters; + } + + availableWidth -= currentRun.Size.Width; + + runIndex++; + } + + return null; + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index e776655284..49bee6e776 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -106,85 +106,18 @@ namespace Avalonia.Media.TextFormatting var collapsingProperties = collapsingPropertiesList[0]; - var runIndex = 0; - var currentWidth = 0.0; - var textRange = TextRange; - var collapsedLength = 0; + var collapsedRuns = collapsingProperties.Collapse(this); - var shapedSymbol = TextFormatterImpl.CreateSymbol(collapsingProperties.Symbol, _paragraphProperties.FlowDirection); - - if (collapsingProperties.Width < shapedSymbol.GlyphRun.Size.Width) + if (collapsedRuns is List shapedRuns) { - return new TextLineImpl(new List(0), textRange, _paragraphWidth, _paragraphProperties, - _flowDirection, TextLineBreak, true); - } - - var availableWidth = collapsingProperties.Width - shapedSymbol.GlyphRun.Size.Width; - - while (runIndex < _textRuns.Count) - { - var currentRun = _textRuns[runIndex]; - - currentWidth += currentRun.Size.Width; + var collapsedLine = new TextLineImpl(shapedRuns, TextRange, _paragraphWidth, _paragraphProperties, _flowDirection, TextLineBreak, true); - if (currentWidth > availableWidth) + if (shapedRuns.Count > 0) { - if (currentRun.TryMeasureCharacters(availableWidth, out var measuredLength)) - { - if (collapsingProperties.Style == TextCollapsingStyle.TrailingWord && - measuredLength < textRange.End) - { - var currentBreakPosition = 0; - - var lineBreaker = new LineBreakEnumerator(currentRun.Text); - - while (currentBreakPosition < measuredLength && lineBreaker.MoveNext()) - { - var nextBreakPosition = lineBreaker.Current.PositionMeasure; - - if (nextBreakPosition == 0) - { - break; - } - - if (nextBreakPosition >= measuredLength) - { - break; - } - - currentBreakPosition = nextBreakPosition; - } - - measuredLength = currentBreakPosition; - } - } - - collapsedLength += measuredLength; - - var shapedTextCharacters = new List(_textRuns.Count); - - if (collapsedLength > 0) - { - var splitResult = TextFormatterImpl.SplitShapedRuns(_textRuns, collapsedLength); - - shapedTextCharacters.AddRange(splitResult.First); - - SortRuns(shapedTextCharacters); - } - - shapedTextCharacters.Add(shapedSymbol); - - var textLine = new TextLineImpl(shapedTextCharacters, textRange, _paragraphWidth, _paragraphProperties, - _flowDirection, TextLineBreak, true); - - return textLine.FinalizeLine(); + collapsedLine.FinalizeLine(); } - availableWidth -= currentRun.Size.Width; - - collapsedLength += currentRun.GlyphRun.Characters.Length; - - runIndex++; + return collapsedLine; } return this; diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index 4bd46e8c75..90424d8424 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -1,24 +1,23 @@ -using Avalonia.Utilities; +using System.Collections.Generic; +using Avalonia.Utilities; namespace Avalonia.Media.TextFormatting { /// - /// a collapsing properties to collapse whole line toward the end + /// A collapsing properties to collapse whole line toward the end /// at character granularity and with ellipsis being the collapsing symbol /// public class TextTrailingCharacterEllipsis : TextCollapsingProperties { - private static readonly ReadOnlySlice s_ellipsis = new ReadOnlySlice(new[] { '\u2026' }); - /// /// Construct a text trailing character ellipsis collapsing properties /// /// width in which collapsing is constrained to /// text run properties of ellispis symbol - public TextTrailingCharacterEllipsis(double width, TextRunProperties textRunProperties) + public TextTrailingCharacterEllipsis(ReadOnlySlice ellipsis, double width, TextRunProperties textRunProperties) { Width = width; - Symbol = new TextCharacters(s_ellipsis, textRunProperties); + Symbol = new TextCharacters(ellipsis, textRunProperties); } /// @@ -27,7 +26,9 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - /// - public sealed override TextCollapsingStyle Style { get; } = TextCollapsingStyle.TrailingCharacter; + public override IReadOnlyList? Collapse(TextLine textLine) + { + return TextEllipsisHelper.Collapse(textLine, this, false); + } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs index 9dffddd207..591df149cb 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -1,4 +1,5 @@ -using Avalonia.Utilities; +using System.Collections.Generic; +using Avalonia.Utilities; namespace Avalonia.Media.TextFormatting { @@ -8,30 +9,30 @@ namespace Avalonia.Media.TextFormatting /// public class TextTrailingWordEllipsis : TextCollapsingProperties { - private static readonly ReadOnlySlice s_ellipsis = new ReadOnlySlice(new[] { '\u2026' }); - /// /// Construct a text trailing word ellipsis collapsing properties /// /// width in which collapsing is constrained to /// text run properties of ellispis symbol public TextTrailingWordEllipsis( + ReadOnlySlice ellipsis, double width, TextRunProperties textRunProperties ) { Width = width; - Symbol = new TextCharacters(s_ellipsis, textRunProperties); + Symbol = new TextCharacters(ellipsis, textRunProperties); } - /// public sealed override double Width { get; } /// public sealed override TextRun Symbol { get; } - /// - public sealed override TextCollapsingStyle Style { get; } = TextCollapsingStyle.TrailingWord; + public override IReadOnlyList? Collapse(TextLine textLine) + { + return TextEllipsisHelper.Collapse(textLine, this, true); + } } } diff --git a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs new file mode 100644 index 0000000000..66efe96ab9 --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs @@ -0,0 +1,31 @@ +using Avalonia.Media.TextFormatting; +using Avalonia.Utilities; + +namespace Avalonia.Media +{ + public class TextLeadingPrefixTrimming : TextTrimming + { + private readonly ReadOnlySlice _ellipsis; + private readonly int _prefixLength; + + public TextLeadingPrefixTrimming(char ellipsis, int prefixLength) : this(new[] { ellipsis }, prefixLength) + { + } + + public TextLeadingPrefixTrimming(char[] ellipsis, int prefixLength) + { + _prefixLength = prefixLength; + _ellipsis = new ReadOnlySlice(ellipsis); + } + + public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo) + { + return new TextLeadingPrefixEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties); + } + + public override string ToString() + { + return nameof(PrefixEllipsis); + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextNoneTrimming.cs b/src/Avalonia.Visuals/Media/TextNoneTrimming.cs new file mode 100644 index 0000000000..6fd0bd2154 --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextNoneTrimming.cs @@ -0,0 +1,18 @@ +using System; +using Avalonia.Media.TextFormatting; + +namespace Avalonia.Media +{ + internal class TextNoneTrimming : TextTrimming + { + public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo) + { + throw new NotSupportedException(); + } + + public override string ToString() + { + return nameof(None); + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs b/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs new file mode 100644 index 0000000000..fae001c0c8 --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs @@ -0,0 +1,36 @@ +using Avalonia.Media.TextFormatting; +using Avalonia.Utilities; + +namespace Avalonia.Media +{ + public class TextTrailingTrimming : TextTrimming + { + private readonly ReadOnlySlice _ellipsis; + private readonly bool _isWordBased; + + public TextTrailingTrimming(char ellipsis, bool isWordBased) : this(new[] {ellipsis}, isWordBased) + { + } + + public TextTrailingTrimming(char[] ellipsis, bool isWordBased) + { + _isWordBased = isWordBased; + _ellipsis = new ReadOnlySlice(ellipsis); + } + + public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo) + { + if (_isWordBased) + { + return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties); + } + + return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties); + } + + public override string ToString() + { + return _isWordBased ? nameof(WordEllipsis) : nameof(CharacterEllipsis); + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextTrimming.cs b/src/Avalonia.Visuals/Media/TextTrimming.cs index f8a63dfede..c79ff4cb1a 100644 --- a/src/Avalonia.Visuals/Media/TextTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextTrimming.cs @@ -1,23 +1,71 @@ -namespace Avalonia.Media +using System; +using Avalonia.Media.TextFormatting; + +namespace Avalonia.Media { /// /// Describes how text is trimmed when it overflows. /// - public enum TextTrimming + public abstract class TextTrimming { + public static char s_defaultEllipsisChar = '\u2026'; + /// /// Text is not trimmed. /// - None, + public static TextTrimming None { get; } = new TextNoneTrimming(); /// /// Text is trimmed at a character boundary. An ellipsis (...) is drawn in place of remaining text. /// - CharacterEllipsis, + public static TextTrimming CharacterEllipsis { get; } = new TextTrailingTrimming(s_defaultEllipsisChar, false); /// /// Text is trimmed at a word boundary. An ellipsis (...) is drawn in place of remaining text. /// - WordEllipsis + public static TextTrimming WordEllipsis { get; } = new TextTrailingTrimming(s_defaultEllipsisChar, true); + + /// + /// Text is trimmed after a given prefix length. An ellipsis (...) is drawn in between prefix and suffix and represents remaining text. + /// + public static TextTrimming PrefixEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 8); + + /// + /// Creates properties that will be used for collapsing lines of text. + /// + /// Contextual info about text that will be collapsed. + public abstract TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo); + + /// + /// Parses a text trimming string. Names must match static properties defined in this class. + /// + /// The text trimming string. + /// The . + public static TextTrimming Parse(string s) + { + bool Matches(string name) + { + return name.Equals(s, StringComparison.OrdinalIgnoreCase); + } + + if (Matches(nameof(None))) + { + return None; + } + if (Matches(nameof(CharacterEllipsis))) + { + return CharacterEllipsis; + } + else if (Matches(nameof(WordEllipsis))) + { + return WordEllipsis; + } + else if (Matches(nameof(PrefixEllipsis))) + { + return PrefixEllipsis; + } + + throw new FormatException($"Invalid text trimming string: '{s}'."); + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index b622971d38..88529ae3a0 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -221,6 +221,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } + if (type.Equals(types.TextTrimming)) + { + foreach (var property in types.TextTrimming.Properties) + { + if (property.PropertyType == types.TextTrimming && property.Name.Equals(text, StringComparison.OrdinalIgnoreCase)) + { + result = new XamlStaticOrTargetedReturnMethodCallNode(node, property.Getter, Enumerable.Empty()); + + return true; + } + } + } + + if (type.Equals(types.TextDecorationCollection)) + { + foreach (var property in types.TextDecorations.Properties) + { + if (property.PropertyType == types.TextDecorationCollection && property.Name.Equals(text, StringComparison.OrdinalIgnoreCase)) + { + result = new XamlStaticOrTargetedReturnMethodCallNode(node, property.Getter, Enumerable.Empty()); + + return true; + } + } + } + result = null; return false; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 90c3989238..34a146cf37 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -88,6 +88,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType ImmutableSolidColorBrush { get; } public IXamlConstructor ImmutableSolidColorBrushConstructorColor { get; } public IXamlType TypeUtilities { get; } + public IXamlType TextDecorationCollection { get; } + public IXamlType TextDecorations { get; } + public IXamlType TextTrimming { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -193,6 +196,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers ImmutableSolidColorBrush = cfg.TypeSystem.GetType("Avalonia.Media.Immutable.ImmutableSolidColorBrush"); ImmutableSolidColorBrushConstructorColor = ImmutableSolidColorBrush.GetConstructor(new List { UInt }); TypeUtilities = cfg.TypeSystem.GetType("Avalonia.Utilities.TypeUtilities"); + TextDecorationCollection = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorationCollection"); + TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations"); + TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming"); } } diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 326997328b..7c7fb4783e 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -387,7 +387,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting if (textLine.Width > 300 || currentHeight + textLine.Height > 240) { - textLine = textLine.Collapse(new TextTrailingWordEllipsis(300, defaultProperties)); + textLine = textLine.Collapse(new TextTrailingWordEllipsis(new ReadOnlySlice(new[] {TextTrimming.s_defaultEllipsisChar}), 300, defaultProperties)); } currentHeight += textLine.Height; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index d1a8f175e7..9c0f2d0a9f 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -360,12 +360,29 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } - [InlineData("01234 01234", 58, TextCollapsingStyle.TrailingCharacter, "01234 0\u2026")] - [InlineData("01234 01234", 58, TextCollapsingStyle.TrailingWord, "01234\u2026")] - [InlineData("01234", 9, TextCollapsingStyle.TrailingCharacter, "\u2026")] - [InlineData("01234", 2, TextCollapsingStyle.TrailingCharacter, "")] + public static IEnumerable CollapsingData + { + get + { + yield return CreateData("01234 01234 01234", 120, TextTrimming.PrefixEllipsis, "01234 01\u20264 01234"); + yield return CreateData("01234 01234", 58, TextTrimming.CharacterEllipsis, "01234 0\u2026"); + yield return CreateData("01234 01234", 58, TextTrimming.WordEllipsis, "01234\u2026"); + yield return CreateData("01234", 9, TextTrimming.CharacterEllipsis, "\u2026"); + yield return CreateData("01234", 2, TextTrimming.CharacterEllipsis, ""); + + object[] CreateData(string text, double width, TextTrimming mode, string expected) + { + return new object[] + { + text, width, mode, expected + }; + } + } + } + + [MemberData(nameof(CollapsingData))] [Theory] - public void Should_Collapse_Line(string text, double width, TextCollapsingStyle style, string expected) + public void Should_Collapse_Line(string text, double width, TextTrimming trimming, string expected) { using (Start()) { @@ -381,16 +398,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.False(textLine.HasCollapsed); - TextCollapsingProperties collapsingProperties; - - if (style == TextCollapsingStyle.TrailingCharacter) - { - collapsingProperties = new TextTrailingCharacterEllipsis(width, defaultProperties); - } - else - { - collapsingProperties = new TextTrailingWordEllipsis(width, defaultProperties); - } + TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties)); var collapsedLine = textLine.Collapse(collapsingProperties); From df7fffc7c935e43115583a62f62ef153bb8120e5 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 00:00:41 +0100 Subject: [PATCH 147/820] Allow for passing flow direction when collapsing lines. --- .../TextCollapsingProperties.cs | 3 +- .../TextFormatting/TextEllipsisHelper.cs | 6 +-- .../TextLeadingPrefixCharacterEllipsis.cs | 48 +++++++++++++------ .../Media/TextFormatting/TextLineImpl.cs | 2 +- .../TextTrailingCharacterEllipsis.cs | 4 +- .../TextTrailingWordEllipsis.cs | 4 +- .../Media/TextLeadingPrefixTrimming.cs | 2 +- src/Avalonia.Visuals/Media/TextTrimming.cs | 6 +-- 8 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs index 0d6ae84493..f766d951d4 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs @@ -21,6 +21,7 @@ namespace Avalonia.Media.TextFormatting /// Collapses given text line. /// /// Text line to collapse. - public abstract IReadOnlyList? Collapse(TextLine textLine); + /// Text flow direction. + public abstract IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs index 6ca93a6116..eccaeba038 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs @@ -5,7 +5,7 @@ namespace Avalonia.Media.TextFormatting { internal class TextEllipsisHelper { - public static List? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis) + public static List? Collapse(TextLine textLine, FlowDirection flowDirection, TextCollapsingProperties properties, bool isWordEllipsis) { var shapedTextRuns = textLine.TextRuns as List; @@ -18,10 +18,6 @@ namespace Avalonia.Media.TextFormatting var currentWidth = 0.0; var collapsedLength = 0; var textRange = textLine.TextRange; - - // TODO: From where this is supposed to come now? - var flowDirection = FlowDirection.LeftToRight; - var shapedSymbol = TextFormatterImpl.CreateSymbol(properties.Symbol, flowDirection); if (properties.Width < shapedSymbol.GlyphRun.Size.Width) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index f0f212b0fe..a2810a7d36 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -20,9 +20,13 @@ namespace Avalonia.Media.TextFormatting ReadOnlySlice ellipsis, int prefixLength, double width, - TextRunProperties textRunProperties - ) + TextRunProperties textRunProperties) { + if (_prefixLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(prefixLength)); + } + _prefixLength = prefixLength; Width = width; Symbol = new TextCharacters(ellipsis, textRunProperties); @@ -34,7 +38,7 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine) + public override IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection) { var shapedTextRuns = textLine.TextRuns as List; @@ -45,10 +49,6 @@ namespace Avalonia.Media.TextFormatting var runIndex = 0; var currentWidth = 0.0; - - // TODO: From where this is supposed to come now? - var flowDirection = FlowDirection.LeftToRight; - var shapedSymbol = TextFormatterImpl.CreateSymbol(Symbol, flowDirection); if (Width < shapedSymbol.GlyphRun.Size.Width) @@ -56,6 +56,8 @@ namespace Avalonia.Media.TextFormatting return new List(0); } + // Overview of ellipsis structure + // Prefix length run | Ellipsis symbol | Post split run growing from the end | var availableWidth = Width - shapedSymbol.Size.Width; while (runIndex < shapedTextRuns.Count) @@ -72,26 +74,42 @@ namespace Avalonia.Media.TextFormatting if (measuredLength > 0) { - var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, Math.Min(_prefixLength, measuredLength)); + List? preSplitRuns = null; + List? postSplitRuns = null; + + if (_prefixLength > 0) + { + var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, Math.Min(_prefixLength, measuredLength)); - shapedTextCharacters.AddRange(splitResult.First); + shapedTextCharacters.AddRange(splitResult.First); - TextLineImpl.SortRuns(shapedTextCharacters); + TextLineImpl.SortRuns(shapedTextCharacters); + + preSplitRuns = splitResult.First; + postSplitRuns = splitResult.Second; + } + else + { + postSplitRuns = shapedTextRuns; + } shapedTextCharacters.Add(shapedSymbol); - if (measuredLength > _prefixLength && splitResult.Second is not null) + if (measuredLength > _prefixLength && postSplitRuns is not null) { var availableSuffixWidth = availableWidth; - foreach (var run in splitResult.First) + if (preSplitRuns is not null) { - availableSuffixWidth -= run.Size.Width; + foreach (var run in preSplitRuns) + { + availableSuffixWidth -= run.Size.Width; + } } - for (int i = splitResult.Second.Count - 1; i >= 0; i--) + for (int i = postSplitRuns.Count - 1; i >= 0; i--) { - var run = splitResult.Second[i]; + var run = postSplitRuns[i]; if (run.TryMeasureCharactersBackwards(availableSuffixWidth, out int suffixCount, out double suffixWidth)) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index 49bee6e776..09da6db8d4 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -106,7 +106,7 @@ namespace Avalonia.Media.TextFormatting var collapsingProperties = collapsingPropertiesList[0]; - var collapsedRuns = collapsingProperties.Collapse(this); + var collapsedRuns = collapsingProperties.Collapse(this, _paragraphProperties.FlowDirection); if (collapsedRuns is List shapedRuns) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index 90424d8424..221ba82ff9 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -26,9 +26,9 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine) + public override IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection) { - return TextEllipsisHelper.Collapse(textLine, this, false); + return TextEllipsisHelper.Collapse(textLine, flowDirection, this, false); } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs index 591df149cb..24ff871022 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -30,9 +30,9 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine) + public override IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection) { - return TextEllipsisHelper.Collapse(textLine, this, true); + return TextEllipsisHelper.Collapse(textLine, flowDirection, this, true); } } } diff --git a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs index 66efe96ab9..6cf11edd42 100644 --- a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs @@ -25,7 +25,7 @@ namespace Avalonia.Media public override string ToString() { - return nameof(PrefixEllipsis); + return nameof(PrefixCharacterEllipsis); } } } diff --git a/src/Avalonia.Visuals/Media/TextTrimming.cs b/src/Avalonia.Visuals/Media/TextTrimming.cs index c79ff4cb1a..3ba52b28f9 100644 --- a/src/Avalonia.Visuals/Media/TextTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextTrimming.cs @@ -28,7 +28,7 @@ namespace Avalonia.Media /// /// Text is trimmed after a given prefix length. An ellipsis (...) is drawn in between prefix and suffix and represents remaining text. /// - public static TextTrimming PrefixEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 8); + public static TextTrimming PrefixCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 8); /// /// Creates properties that will be used for collapsing lines of text. @@ -60,9 +60,9 @@ namespace Avalonia.Media { return WordEllipsis; } - else if (Matches(nameof(PrefixEllipsis))) + else if (Matches(nameof(PrefixCharacterEllipsis))) { - return PrefixEllipsis; + return PrefixCharacterEllipsis; } throw new FormatException($"Invalid text trimming string: '{s}'."); From 0e7c9192879b2b22826b0f1b91bcb0e2d193e0ef Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 00:08:23 +0100 Subject: [PATCH 148/820] Documentation fixes. --- .../Media/TextFormatting/TextCollapsingProperties.cs | 6 +++--- .../TextLeadingPrefixCharacterEllipsis.cs | 10 ++++++---- .../TextFormatting/TextTrailingCharacterEllipsis.cs | 7 ++++--- .../Media/TextFormatting/TextTrailingWordEllipsis.cs | 9 +++++---- .../Media/TextLeadingPrefixTrimming.cs | 2 +- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs index f766d951d4..288f9386c2 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs @@ -3,17 +3,17 @@ namespace Avalonia.Media.TextFormatting { /// - /// Properties of text collapsing + /// Properties of text collapsing. /// public abstract class TextCollapsingProperties { /// - /// Gets the width in which the collapsible range is constrained to + /// Gets the width in which the collapsible range is constrained to. /// public abstract double Width { get; } /// - /// Gets the text run that is used as collapsing symbol + /// Gets the text run that is used as collapsing symbol. /// public abstract TextRun Symbol { get; } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index a2810a7d36..7f6ab2ef90 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -5,18 +5,20 @@ using Avalonia.Utilities; namespace Avalonia.Media.TextFormatting { /// - /// Ellipsis based on a fixed length leading prefix and suffix growing from the end. + /// Ellipsis based on a fixed length leading prefix and suffix growing from the end at character granularity. /// - public class TextLeadingPrefixEllipsis : TextCollapsingProperties + public class TextLeadingPrefixCharacterEllipsis : TextCollapsingProperties { private readonly int _prefixLength; /// - /// Construct a text trailing word ellipsis collapsing properties + /// Construct a text trailing word ellipsis collapsing properties. /// + /// Text used as collapsing symbol. + /// Length of leading prefix. /// width in which collapsing is constrained to /// text run properties of ellispis symbol - public TextLeadingPrefixEllipsis( + public TextLeadingPrefixCharacterEllipsis( ReadOnlySlice ellipsis, int prefixLength, double width, diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index 221ba82ff9..06548e6590 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -5,15 +5,16 @@ namespace Avalonia.Media.TextFormatting { /// /// A collapsing properties to collapse whole line toward the end - /// at character granularity and with ellipsis being the collapsing symbol + /// at character granularity. /// public class TextTrailingCharacterEllipsis : TextCollapsingProperties { /// /// Construct a text trailing character ellipsis collapsing properties /// - /// width in which collapsing is constrained to - /// text run properties of ellispis symbol + /// Text used as collapsing symbol. + /// Width in which collapsing is constrained to. + /// Text run properties of ellispis symbol. public TextTrailingCharacterEllipsis(ReadOnlySlice ellipsis, double width, TextRunProperties textRunProperties) { Width = width; diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs index 24ff871022..bea8bd6be0 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -5,15 +5,16 @@ namespace Avalonia.Media.TextFormatting { /// /// a collapsing properties to collapse whole line toward the end - /// at word granularity and with ellipsis being the collapsing symbol + /// at word granularity. /// public class TextTrailingWordEllipsis : TextCollapsingProperties { /// - /// Construct a text trailing word ellipsis collapsing properties + /// Construct a text trailing word ellipsis collapsing properties. /// - /// width in which collapsing is constrained to - /// text run properties of ellispis symbol + /// Text used as collapsing symbol. + /// width in which collapsing is constrained to. + /// text run properties of ellispis symbol. public TextTrailingWordEllipsis( ReadOnlySlice ellipsis, double width, diff --git a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs index 6cf11edd42..a8c7c3dda0 100644 --- a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs @@ -20,7 +20,7 @@ namespace Avalonia.Media public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo) { - return new TextLeadingPrefixEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties); + return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties); } public override string ToString() From 86262340c8b74ee57d62d4efe15f26fe34960298 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 00:11:24 +0100 Subject: [PATCH 149/820] Seal APIs. --- .../Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs | 2 +- .../Media/TextFormatting/TextTrailingWordEllipsis.cs | 2 +- src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs | 2 +- src/Avalonia.Visuals/Media/TextNoneTrimming.cs | 2 +- src/Avalonia.Visuals/Media/TextTrailingTrimming.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index 7f6ab2ef90..a771b2a639 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -7,7 +7,7 @@ namespace Avalonia.Media.TextFormatting /// /// Ellipsis based on a fixed length leading prefix and suffix growing from the end at character granularity. /// - public class TextLeadingPrefixCharacterEllipsis : TextCollapsingProperties + public sealed class TextLeadingPrefixCharacterEllipsis : TextCollapsingProperties { private readonly int _prefixLength; diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs index bea8bd6be0..7a98d36510 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -7,7 +7,7 @@ namespace Avalonia.Media.TextFormatting /// a collapsing properties to collapse whole line toward the end /// at word granularity. /// - public class TextTrailingWordEllipsis : TextCollapsingProperties + public sealed class TextTrailingWordEllipsis : TextCollapsingProperties { /// /// Construct a text trailing word ellipsis collapsing properties. diff --git a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs index a8c7c3dda0..19ca1a0198 100644 --- a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs @@ -3,7 +3,7 @@ using Avalonia.Utilities; namespace Avalonia.Media { - public class TextLeadingPrefixTrimming : TextTrimming + public sealed class TextLeadingPrefixTrimming : TextTrimming { private readonly ReadOnlySlice _ellipsis; private readonly int _prefixLength; diff --git a/src/Avalonia.Visuals/Media/TextNoneTrimming.cs b/src/Avalonia.Visuals/Media/TextNoneTrimming.cs index 6fd0bd2154..ec238cf17e 100644 --- a/src/Avalonia.Visuals/Media/TextNoneTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextNoneTrimming.cs @@ -3,7 +3,7 @@ using Avalonia.Media.TextFormatting; namespace Avalonia.Media { - internal class TextNoneTrimming : TextTrimming + internal sealed class TextNoneTrimming : TextTrimming { public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo) { diff --git a/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs b/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs index fae001c0c8..5bb35f0ba7 100644 --- a/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs @@ -3,7 +3,7 @@ using Avalonia.Utilities; namespace Avalonia.Media { - public class TextTrailingTrimming : TextTrimming + public sealed class TextTrailingTrimming : TextTrimming { private readonly ReadOnlySlice _ellipsis; private readonly bool _isWordBased; From b6d2bbf9fb76474a9a9c66b1f41ceabba947debc Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 00:13:43 +0100 Subject: [PATCH 150/820] Leftover unsealed API. --- .../Media/TextFormatting/TextTrailingCharacterEllipsis.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index 06548e6590..5577fc31f5 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -7,7 +7,7 @@ namespace Avalonia.Media.TextFormatting /// A collapsing properties to collapse whole line toward the end /// at character granularity. /// - public class TextTrailingCharacterEllipsis : TextCollapsingProperties + public sealed class TextTrailingCharacterEllipsis : TextCollapsingProperties { /// /// Construct a text trailing character ellipsis collapsing properties From 47ad3d7f667eed36d922db5ad9d8227a1b60cc92 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 00:24:29 +0100 Subject: [PATCH 151/820] API compat. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index c02b9d8639..b9989ad670 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -52,6 +52,12 @@ MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult..ctor()' MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsInside.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsTrailing.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.TextPosition.set(System.Int32)' does not exist in the implementation but it does exist in the contract. +CannotMakeTypeAbstract : Type 'Avalonia.Media.TextTrimming' is abstract in the implementation but is not abstract in the contract. +TypeCannotChangeClassification : Type 'Avalonia.Media.TextTrimming' is a 'class' in the implementation but is a 'struct' in the contract. +MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.CharacterEllipsis' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.None' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.WordEllipsis' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Int32 System.Int32 Avalonia.Media.TextTrimming.value__' 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. @@ -79,6 +85,9 @@ MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextC MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract. +CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine, Avalonia.Media.FlowDirection)' is abstract in the implementation but is missing in the contract. +MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.IBrush, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Media.TextTrimming, Avalonia.Media.TextDecorationCollection, System.Double, System.Double, System.Double, System.Int32, System.Collections.Generic.IReadOnlyList>)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. @@ -124,6 +133,12 @@ CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextForma CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment.get()' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Media.TextFormatting.TextShaper.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. +CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. +MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract. +CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingWordEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. +MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingWordEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingWordEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.TextFormatting.Unicode.BiDiClass' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.Unicode.BiDiClass Avalonia.Media.TextFormatting.Unicode.Codepoint.BiDiClass.get()' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.ExportRenderingSubsystemAttribute' does not exist in the implementation but it does exist in the contract. @@ -161,4 +176,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: 162 +Total Issues: 177 From 7843e93e3a8f46780794905f7e8f5d55fa392f41 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 00:31:02 +0100 Subject: [PATCH 152/820] Fixed missing summary start tag. --- src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index a295a8cdc9..e4a280938d 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -11,6 +11,7 @@ namespace Avalonia.Platform /// public interface IPlatformRenderInterface { + /// /// Creates an ellipse geometry implementation. /// /// The bounds of the ellipse. From 127344898106f3d0e9c201784f9975f85bd0cdf4 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 11:05:09 +0100 Subject: [PATCH 153/820] API changes and test fixes. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 2 +- .../Media/TextFormatting/TextCollapsingProperties.cs | 3 +-- .../Media/TextFormatting/TextEllipsisHelper.cs | 4 ++-- .../TextFormatting/TextLeadingPrefixCharacterEllipsis.cs | 4 ++-- src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs | 2 +- .../Media/TextFormatting/TextTrailingCharacterEllipsis.cs | 4 ++-- .../Media/TextFormatting/TextTrailingWordEllipsis.cs | 4 ++-- src/Avalonia.Visuals/Media/TextTrimming.cs | 5 +++++ .../Media/TextFormatting/TextLineTests.cs | 2 +- 9 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index b9989ad670..b725993b44 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -85,7 +85,7 @@ MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextC MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine, Avalonia.Media.FlowDirection)' is abstract in the implementation but is missing in the contract. +CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract. diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs index 288f9386c2..a46f9537d0 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs @@ -21,7 +21,6 @@ namespace Avalonia.Media.TextFormatting /// Collapses given text line. /// /// Text line to collapse. - /// Text flow direction. - public abstract IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection); + public abstract IReadOnlyList? Collapse(TextLine textLine); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs index eccaeba038..2031c2ec99 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs @@ -5,7 +5,7 @@ namespace Avalonia.Media.TextFormatting { internal class TextEllipsisHelper { - public static List? Collapse(TextLine textLine, FlowDirection flowDirection, TextCollapsingProperties properties, bool isWordEllipsis) + public static List? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis) { var shapedTextRuns = textLine.TextRuns as List; @@ -18,7 +18,7 @@ namespace Avalonia.Media.TextFormatting var currentWidth = 0.0; var collapsedLength = 0; var textRange = textLine.TextRange; - var shapedSymbol = TextFormatterImpl.CreateSymbol(properties.Symbol, flowDirection); + var shapedSymbol = TextFormatterImpl.CreateSymbol(properties.Symbol, FlowDirection.LeftToRight); if (properties.Width < shapedSymbol.GlyphRun.Size.Width) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index a771b2a639..74c4573630 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -40,7 +40,7 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection) + public override IReadOnlyList? Collapse(TextLine textLine) { var shapedTextRuns = textLine.TextRuns as List; @@ -51,7 +51,7 @@ namespace Avalonia.Media.TextFormatting var runIndex = 0; var currentWidth = 0.0; - var shapedSymbol = TextFormatterImpl.CreateSymbol(Symbol, flowDirection); + var shapedSymbol = TextFormatterImpl.CreateSymbol(Symbol, FlowDirection.LeftToRight); if (Width < shapedSymbol.GlyphRun.Size.Width) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index 09da6db8d4..49bee6e776 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -106,7 +106,7 @@ namespace Avalonia.Media.TextFormatting var collapsingProperties = collapsingPropertiesList[0]; - var collapsedRuns = collapsingProperties.Collapse(this, _paragraphProperties.FlowDirection); + var collapsedRuns = collapsingProperties.Collapse(this); if (collapsedRuns is List shapedRuns) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index 5577fc31f5..83acaa021e 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -27,9 +27,9 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection) + public override IReadOnlyList? Collapse(TextLine textLine) { - return TextEllipsisHelper.Collapse(textLine, flowDirection, this, false); + return TextEllipsisHelper.Collapse(textLine, this, false); } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs index 7a98d36510..ff2e4cf325 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -31,9 +31,9 @@ namespace Avalonia.Media.TextFormatting /// public sealed override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine, FlowDirection flowDirection) + public override IReadOnlyList? Collapse(TextLine textLine) { - return TextEllipsisHelper.Collapse(textLine, flowDirection, this, true); + return TextEllipsisHelper.Collapse(textLine, this, true); } } } diff --git a/src/Avalonia.Visuals/Media/TextTrimming.cs b/src/Avalonia.Visuals/Media/TextTrimming.cs index 3ba52b28f9..b6f5be496f 100644 --- a/src/Avalonia.Visuals/Media/TextTrimming.cs +++ b/src/Avalonia.Visuals/Media/TextTrimming.cs @@ -30,6 +30,11 @@ namespace Avalonia.Media /// public static TextTrimming PrefixCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 8); + /// + /// Text is trimmed at a character boundary starting from the beginning. An ellipsis (...) is drawn in place of remaining text. + /// + public static TextTrimming LeadingCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 0); + /// /// Creates properties that will be used for collapsing lines of text. /// diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 9c0f2d0a9f..367e6f4bea 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -364,7 +364,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { get { - yield return CreateData("01234 01234 01234", 120, TextTrimming.PrefixEllipsis, "01234 01\u20264 01234"); + yield return CreateData("01234 01234 01234", 120, TextTrimming.PrefixCharacterEllipsis, "01234 01\u20264 01234"); yield return CreateData("01234 01234", 58, TextTrimming.CharacterEllipsis, "01234 0\u2026"); yield return CreateData("01234 01234", 58, TextTrimming.WordEllipsis, "01234\u2026"); yield return CreateData("01234", 9, TextTrimming.CharacterEllipsis, "\u2026"); From d5fd84ebc018927aceaf365808efa3a49bb87fbe Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Tue, 15 Mar 2022 13:58:27 +0200 Subject: [PATCH 154/820] continue working --- samples/ControlCatalog/Pages/ScreenPage.cs | 2 +- src/Avalonia.Controls/Control.cs | 170 ++++++++++++++---- .../Presenters/TextPresenter.cs | 2 + src/Avalonia.Controls/TextBlock.cs | 2 +- src/Avalonia.Controls/TextBox.cs | 2 - src/Avalonia.Controls/TopLevel.cs | 2 + .../Controls/CheckBox.xaml | 1 + src/Avalonia.Visuals/Visual.cs | 2 +- 8 files changed, 142 insertions(+), 41 deletions(-) diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 1fc37d903d..f65566a1e9 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -79,6 +79,6 @@ namespace ControlCatalog.Pages Typeface.Default, 12, Brushes.Green); } - protected override bool ShouldBeMirroredIfRightToLeft() => false; + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 16d6c3f4c5..9b41e9ee64 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -67,10 +67,17 @@ namespace Avalonia.Controls /// public static readonly AttachedProperty FlowDirectionProperty = AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true); - + + /// + /// Defines the property. + /// + public static new readonly StyledProperty RenderTransformProperty = + Visual.RenderTransformProperty.AddOwner(); + private DataTemplates? _dataTemplates; private IControl? _focusAdorner; private AutomationPeer? _automationPeer; + private bool _hasMirrorTransform; /// /// Gets or sets the control's focus adorner. @@ -126,6 +133,21 @@ namespace Avalonia.Controls set => SetValue(FlowDirectionProperty, value); } + /// + public override ITransform? RenderTransform + { + get => base.RenderTransform; + set + { + if (_hasMirrorTransform) + { + value = MargeTransforms(MirrorTrasform(), value); + } + + base.RenderTransform = value; + } + } + /// /// Occurs when the user has completed a context input gesture, such as a right-click. /// @@ -312,76 +334,152 @@ namespace Avalonia.Controls static Control() { - AffectsArrange(FlowDirectionProperty); + //var m = new StyledPropertyMetadata(coerce: (s, e) => null); + //RenderTransformProperty.OverrideMetadata(m); + + //AffectsRender(FlowDirectionProperty); + //FlowDirectionProperty.Changed.AddClassHandler((s, e) => + //{ + // s.InvalidateFlowDirection(); + // foreach (var logical in LogicalTree.LogicalExtensions.GetLogicalDescendants(s)) + // { + // if (logical is Control control) + // { + // //if (control) + // //control.InvalidateFlowDirection(); + // } + // } + //}); } - private bool _mirrorApplied; - - protected virtual bool ShouldBeMirroredIfRightToLeft() + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - if (Parent is Control parent) - { - return parent.ShouldBeMirroredIfRightToLeft(); - } - else + base.OnPropertyChanged(change); + + if (change.Property == FlowDirectionProperty) { - return true; + // Avoid inherit value change to invoke this method + if (!GetBaseValue(FlowDirectionProperty, change.Priority).HasValue) + { + return; + } + + InvalidateFlowDirection(); } } - protected override void ArrangeCore(Rect finalRect) + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - base.ArrangeCore(finalRect); + base.OnAttachedToVisualTree(e); + + InvalidateFlowDirection(); + } + protected override void OnAttachedToLogicalTree(LogicalTree.LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + //InvalidateFlowDirection(); + } + + protected virtual bool ShouldGetMirrored() => true; + + private void InvalidateFlowDirection() + { FlowDirection parentFD = FlowDirection.LeftToRight; FlowDirection thisFD = FlowDirection; - bool shouldBeMirroredIfRightToLeft = ShouldBeMirroredIfRightToLeft(); - if (Parent is Control control) + bool parentShouldGetMirrored = true; + bool thisShouldGetMirrored = ShouldGetMirrored(); + + if (((Visual)this).GetVisualParent() is Control control) { parentFD = control.FlowDirection; + parentShouldGetMirrored = control.ShouldGetMirrored(); } - - bool shouldMirror; - if (shouldBeMirroredIfRightToLeft) + else if (Parent is Control logicalControl) { - shouldMirror = ShuoldApplyMirrorTransform(parentFD, thisFD); - if (Parent is Popup && thisFD == FlowDirection.RightToLeft) - { - shouldMirror = true; - } + parentFD = logicalControl.FlowDirection; + parentShouldGetMirrored = logicalControl.ShouldGetMirrored(); + } + + bool shouldBeMirrored = thisFD == FlowDirection.RightToLeft && thisShouldGetMirrored; + bool parentMirrored = parentFD == FlowDirection.RightToLeft && parentShouldGetMirrored; + + bool shouldApplyMirrorTransform = shouldBeMirrored != parentMirrored; + + if (shouldApplyMirrorTransform) + { + AddMirrorTransform(); } else { - shouldMirror = ShuoldApplyMirrorTransform(parentFD, FlowDirection.LeftToRight); + RemoveMirrorTransform(); } - if (shouldMirror) + foreach (var visual in VisualChildren) { - ApplyMirrorTransform(); + if (visual is Control child) + { + child.InvalidateFlowDirection(); + } } - else + } + + private void AddMirrorTransform() + { + if (_hasMirrorTransform) { - //RenderTransform = null; + return; } + + var mirrorTransform = MirrorTrasform(); + + ITransform? finalTransform = mirrorTransform; + if (RenderTransform != null) + { + finalTransform = MargeTransforms(RenderTransform, mirrorTransform); + } + + RenderTransform = finalTransform; + _hasMirrorTransform = true; } - private void ApplyMirrorTransform() + private void RemoveMirrorTransform() { - if (_mirrorApplied) + if (!_hasMirrorTransform) { return; } - var transform = new MatrixTransform(new Avalonia.Matrix(-1, 0, 0, 1, 0.0, 0.0)); - RenderTransform = transform; - _mirrorApplied = true; + var mirrorTransform = MirrorTrasform(); + + ITransform? finalTransform = MargeTransforms(RenderTransform, mirrorTransform); + if (finalTransform!.Value == Matrix.Identity) + { + finalTransform = null; + } + + _hasMirrorTransform = false; + RenderTransform = finalTransform; } - internal static bool ShuoldApplyMirrorTransform(FlowDirection parentFD, FlowDirection thisFD) + static ITransform? MargeTransforms(ITransform? iTransform1, ITransform? iTransform2) { - return ((parentFD == FlowDirection.LeftToRight && thisFD == FlowDirection.RightToLeft) || - (parentFD == FlowDirection.RightToLeft && thisFD == FlowDirection.LeftToRight)); + // don't know how to marge ITransform + if (iTransform1 is Transform transform1 && iTransform2 is Transform transform2) + { + TransformGroup groupTransform = new TransformGroup(); + + groupTransform.Children.Add(transform1); + groupTransform.Children.Add(transform2); + + return groupTransform; + } + + return iTransform1; } + + static ITransform MirrorTrasform() => + new MatrixTransform(new Avalonia.Matrix(-1, 0, 0, 1, 0.0, 0.0)); } } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 10ce31088a..858544a872 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -798,5 +798,7 @@ namespace Avalonia.Controls.Presenters } } } + + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index d29f094c38..a5b1d44dc8 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -613,6 +613,6 @@ namespace Avalonia.Controls InvalidateTextLayout(); } - protected override bool ShouldBeMirroredIfRightToLeft() => false; + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 76f7a185fe..4d71717776 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1504,7 +1504,5 @@ namespace Avalonia.Controls } } } - - protected override bool ShouldBeMirroredIfRightToLeft() => false; } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index a4fe154515..da85824457 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -529,5 +529,7 @@ namespace Avalonia.Controls ITextInputMethodImpl? ITextInputMethodRoot.InputMethod => (PlatformImpl as ITopLevelImplWithTextInputMethod)?.TextInputMethod; + + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index ef28593711..66dc17a417 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -152,6 +152,7 @@ + diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 324b253a0f..fcb4298895 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -222,7 +222,7 @@ namespace Avalonia /// /// Gets or sets the render transform of the control. /// - public ITransform? RenderTransform + public virtual ITransform? RenderTransform { get { return GetValue(RenderTransformProperty); } set { SetValue(RenderTransformProperty, value); } From 86a4ee86fb7d6022e4386e69d1e923f530027a9a Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Tue, 15 Mar 2022 17:24:57 +0200 Subject: [PATCH 155/820] continure working --- src/Avalonia.Controls/Control.cs | 52 ++++++++------------------------ 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 9b41e9ee64..0b74516c33 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -332,39 +332,17 @@ namespace Avalonia.Controls } } - static Control() - { - //var m = new StyledPropertyMetadata(coerce: (s, e) => null); - //RenderTransformProperty.OverrideMetadata(m); - - //AffectsRender(FlowDirectionProperty); - //FlowDirectionProperty.Changed.AddClassHandler((s, e) => - //{ - // s.InvalidateFlowDirection(); - // foreach (var logical in LogicalTree.LogicalExtensions.GetLogicalDescendants(s)) - // { - // if (logical is Control control) - // { - // //if (control) - // //control.InvalidateFlowDirection(); - // } - // } - //}); - } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (change.Property == FlowDirectionProperty) { - // Avoid inherit value change to invoke this method - if (!GetBaseValue(FlowDirectionProperty, change.Priority).HasValue) + // Avoid inherited value change to call this method + if (GetBaseValue(FlowDirectionProperty, change.Priority).HasValue) { - return; + InvalidateFlowDirection(); } - - InvalidateFlowDirection(); } } @@ -375,14 +353,6 @@ namespace Avalonia.Controls InvalidateFlowDirection(); } - protected override void OnAttachedToLogicalTree(LogicalTree.LogicalTreeAttachmentEventArgs e) - { - base.OnAttachedToLogicalTree(e); - //InvalidateFlowDirection(); - } - - protected virtual bool ShouldGetMirrored() => true; - private void InvalidateFlowDirection() { FlowDirection parentFD = FlowDirection.LeftToRight; @@ -391,7 +361,7 @@ namespace Avalonia.Controls bool parentShouldGetMirrored = true; bool thisShouldGetMirrored = ShouldGetMirrored(); - if (((Visual)this).GetVisualParent() is Control control) + if (this.GetVisualParent() is Control control) { parentFD = control.FlowDirection; parentShouldGetMirrored = control.ShouldGetMirrored(); @@ -433,14 +403,15 @@ namespace Avalonia.Controls } var mirrorTransform = MirrorTrasform(); + var rendertransform = RenderTransform; ITransform? finalTransform = mirrorTransform; - if (RenderTransform != null) + if (rendertransform != null) { - finalTransform = MargeTransforms(RenderTransform, mirrorTransform); + finalTransform = MargeTransforms(rendertransform, mirrorTransform); } - RenderTransform = finalTransform; + base.RenderTransform = finalTransform; _hasMirrorTransform = true; } @@ -452,17 +423,20 @@ namespace Avalonia.Controls } var mirrorTransform = MirrorTrasform(); + var rendertransform = RenderTransform; - ITransform? finalTransform = MargeTransforms(RenderTransform, mirrorTransform); + ITransform? finalTransform = MargeTransforms(rendertransform, mirrorTransform); if (finalTransform!.Value == Matrix.Identity) { finalTransform = null; } _hasMirrorTransform = false; - RenderTransform = finalTransform; + base.RenderTransform = finalTransform; } + protected virtual bool ShouldGetMirrored() => true; + static ITransform? MargeTransforms(ITransform? iTransform1, ITransform? iTransform2) { // don't know how to marge ITransform From 031ff2f49888076424636c6c8473eb039f86c1a0 Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Tue, 15 Mar 2022 17:34:09 +0200 Subject: [PATCH 156/820] continue --- src/Avalonia.Controls/Image.cs | 2 ++ src/Avalonia.Themes.Default/Controls/CheckBox.xaml | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 3d67880638..cbde2bd338 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -127,5 +127,7 @@ namespace Avalonia.Controls return new Size(); } } + + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Themes.Default/Controls/CheckBox.xaml b/src/Avalonia.Themes.Default/Controls/CheckBox.xaml index 75d6f853be..6cb991ba1b 100644 --- a/src/Avalonia.Themes.Default/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Default/Controls/CheckBox.xaml @@ -26,6 +26,7 @@ Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" + FlowDirection="LeftToRight" Data="M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"/> Date: Tue, 15 Mar 2022 12:15:38 -0400 Subject: [PATCH 157/820] Make android test application buildable --- .../Avalonia.AndroidTestApplication.csproj | 8 +++- .../MainActivity.cs | 39 +++++++------------ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index 8cb7aa1cfd..548393fb2f 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -11,7 +11,13 @@ true portable - + + + + Resources\drawable\Icon.png + + + True True diff --git a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs index 471b982d9e..e34fa6cf7b 100644 --- a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs +++ b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs @@ -1,9 +1,9 @@ using System; using Android.App; using Android.Content.PM; -using Android.OS; using Avalonia.Android; using Avalonia.Controls; +using Avalonia.Input.TextInput; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Styling; @@ -14,22 +14,15 @@ namespace Avalonia.AndroidTestApplication [Activity(Label = "Main", MainLauncher = true, Icon = "@drawable/icon", - Theme = "@style/Theme.AppCompat.NoActionBar", - ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, + Theme = "@style/Theme.AppCompat.NoActionBar", + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, LaunchMode = LaunchMode.SingleInstance/*, ScreenOrientation = ScreenOrientation.Landscape*/)] - public class MainBaseActivity : AvaloniaActivity + public class MainActivity : AvaloniaActivity { - protected override void OnCreate(Bundle savedInstanceState) + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) { - if (Avalonia.Application.Current == null) - { - AppBuilder.Configure() - .UseAndroid() - .SetupWithoutStarting(); - } - base.OnCreate(savedInstanceState); - Content = App.CreateSimpleWindow(); + return base.CustomizeAppBuilder(builder); } } @@ -42,8 +35,6 @@ namespace Avalonia.AndroidTestApplication var baseLight = (IStyle)AvaloniaXamlLoader.Load( new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")); Styles.Add(baseLight); - - } // This provides a simple UI tree for testing input handling, drawing, etc @@ -76,12 +67,12 @@ namespace Avalonia.AndroidTestApplication Foreground = Brushes.Black }, - CreateTextBox(Input.TextInput.TextInputContentType.Normal), - CreateTextBox(Input.TextInput.TextInputContentType.Password), - CreateTextBox(Input.TextInput.TextInputContentType.Email), - CreateTextBox(Input.TextInput.TextInputContentType.Url), - CreateTextBox(Input.TextInput.TextInputContentType.Phone), - CreateTextBox(Input.TextInput.TextInputContentType.Number), + CreateTextBox(TextInputContentType.Normal), + CreateTextBox(TextInputContentType.Password), + CreateTextBox(TextInputContentType.Email), + CreateTextBox(TextInputContentType.Url), + CreateTextBox(TextInputContentType.Digits), + CreateTextBox(TextInputContentType.Number), } } }; @@ -89,16 +80,16 @@ namespace Avalonia.AndroidTestApplication return window; } - private static TextBox CreateTextBox(Input.TextInput.TextInputContentType contentType) + private static TextBox CreateTextBox(TextInputContentType contentType) { var textBox = new TextBox() { Margin = new Thickness(20, 10), Watermark = contentType.ToString(), BorderThickness = new Thickness(3), - FontSize = 20 + FontSize = 20, + [TextInputOptions.ContentTypeProperty] = contentType }; - textBox.TextInputOptionsQuery += (s, e) => { e.ContentType = contentType; }; return textBox; } From e91b3318e33746e59ef6e93fc3a1b48b9159be04 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 15 Mar 2022 13:00:47 -0400 Subject: [PATCH 158/820] Enable AOT on android projects (release configuration only) --- samples/ControlCatalog.Android/ControlCatalog.Android.csproj | 3 ++- .../Avalonia.AndroidTestApplication.csproj | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 516acfe4b9..9777bb46c3 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -21,8 +21,9 @@ True - False + True True + True diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index 548393fb2f..db0bb01410 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -22,7 +22,7 @@ True True True - + True From ab46885d62262441614c5069ffbe9ef821a0adce Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 15 Mar 2022 13:01:05 -0400 Subject: [PATCH 159/820] Fix Visual Studio android projects build --- packages/Avalonia/Avalonia.csproj | 4 +++- src/Avalonia.MicroCom/Avalonia.MicroCom.csproj | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 4b28527465..4d0ed866a3 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -8,7 +8,9 @@ all - + true + TargetFramework=netstandard2.0 + diff --git a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj index b796e173c4..d7f39f6642 100644 --- a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj +++ b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj @@ -10,6 +10,8 @@ false all + true + TargetFramework=netstandard2.0 From 3fe50be1683a3bba081b726c1c87728f6bf0b4d9 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 15 Mar 2022 13:02:00 -0400 Subject: [PATCH 160/820] Update AndroidTestApplication futher --- .../MainActivity.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs index e34fa6cf7b..8f4beb2737 100644 --- a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs +++ b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs @@ -3,6 +3,7 @@ using Android.App; using Android.Content.PM; using Avalonia.Android; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input.TextInput; using Avalonia.Markup.Xaml; using Avalonia.Media; @@ -30,11 +31,17 @@ namespace Avalonia.AndroidTestApplication { public override void Initialize() { - Styles.Add(new DefaultTheme()); + Styles.Add(new SimpleTheme(new Uri("avares://Avalonia.AndroidTestApplication"))); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + { + singleViewLifetime.MainView = CreateSimpleWindow(); + } - var baseLight = (IStyle)AvaloniaXamlLoader.Load( - new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")); - Styles.Add(baseLight); + base.OnFrameworkInitializationCompleted(); } // This provides a simple UI tree for testing input handling, drawing, etc From 0d32823c598cdd91fb18d90c698b831bf80ce3d8 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 15 Mar 2022 22:59:29 +0100 Subject: [PATCH 161/820] Fixed compiled self binding not working from setters. Add extra tests for both reflection and compiled bindings. --- .../AvaloniaXamlIlBindingPathTransformer.cs | 10 +++++- .../AvaloniaXamlIlWellKnownTypes.cs | 2 ++ .../CompiledBindingExtensionTests.cs | 31 ++++++++++++++++++ .../Xaml/BindingTests.cs | 32 +++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs index 048a6220c5..3cc3504e16 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs @@ -117,7 +117,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return parentDataContextNode.DataContextType; }; - XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, binding, startTypeResolver, context.ParentNodes().OfType().First().Type.GetClrType()); + var selfType = context.ParentNodes().OfType().First().Type.GetClrType(); + + // When using self bindings with setters we need to change target type to resolved selector type. + if (context.GetAvaloniaTypes().ISetter.IsAssignableFrom(selfType)) + { + selfType = context.ParentNodes().OfType().First().TargetType.GetClrType(); + } + + XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, binding, startTypeResolver, selfType); } return node; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 34a146cf37..99072ace02 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -91,6 +91,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType TextDecorationCollection { get; } public IXamlType TextDecorations { get; } public IXamlType TextTrimming { get; } + public IXamlType ISetter { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -199,6 +200,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers TextDecorationCollection = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorationCollection"); TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations"); TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming"); + ISetter = cfg.TypeSystem.GetType("Avalonia.Styling.ISetter"); } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index ecfb54a624..c1c2284372 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1064,6 +1064,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Binds_To_Self_In_Style() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + public virtual void InvalidateFlowDirection() { - bool parentShouldPresentedMirrored = false; - bool thisShouldPresentedMirrored = ShouldPresentedMirrored(); + bool parentShouldPresentMirrored = false; + bool thisShouldPresentMirrored = ShouldPresentMirrored(); var parent = this.FindAncestorOfType(); if (parent != null) { - parentShouldPresentedMirrored = parent.ShouldPresentedMirrored(); + parentShouldPresentMirrored = parent.ShouldPresentMirrored(); } else if (this.Parent is Control logicalParent) { - parentShouldPresentedMirrored = logicalParent.ShouldPresentedMirrored(); + parentShouldPresentMirrored = logicalParent.ShouldPresentMirrored(); } - bool shouldApplyMirrorTransform = thisShouldPresentedMirrored != parentShouldPresentedMirrored; + bool shouldApplyMirrorTransform = thisShouldPresentMirrored != parentShouldPresentMirrored; IsMirrorTransform = shouldApplyMirrorTransform; } /// - /// Determines whether the element should be presented mirrored, this + /// Determines whether the element should be present mirrored, this /// method related to FlowDirection system and returns true if FlowDirection /// is RightToLeft. For controls that want to avoid this behavior, it is /// possible to override this method and return false. /// - protected virtual bool ShouldPresentedMirrored() + protected virtual bool ShouldPresentMirrored() { return FlowDirection == FlowDirection.RightToLeft; } diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index b3275f6369..fa654f8ad1 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -128,6 +128,6 @@ namespace Avalonia.Controls } } - protected override bool ShouldPresentedMirrored() => false; + protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index e125acbcfa..9b77b6ade1 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -799,6 +799,6 @@ namespace Avalonia.Controls.Presenters } } - protected override bool ShouldPresentedMirrored() => false; + protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 624f0b671a..bf510014b5 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -613,6 +613,6 @@ namespace Avalonia.Controls InvalidateTextLayout(); } - protected override bool ShouldPresentedMirrored() => false; + protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f1b311dc52..a54bcf3bff 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -534,6 +534,6 @@ namespace Avalonia.Controls { } - protected override bool ShouldPresentedMirrored() => false; + protected override bool ShouldPresentMirrored() => false; } } From 61ffbcc687bb6c44107a01474a7a371aec8d2e0f Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Tue, 22 Mar 2022 11:23:09 +0200 Subject: [PATCH 225/820] naming more like wpf --- samples/ControlCatalog/Pages/ScreenPage.cs | 4 +- src/Avalonia.Controls/ComboBox.cs | 16 +++++-- src/Avalonia.Controls/Control.cs | 42 +++++++++++-------- src/Avalonia.Controls/Image.cs | 4 +- .../Presenters/TextPresenter.cs | 4 +- src/Avalonia.Controls/TextBlock.cs | 4 +- src/Avalonia.Controls/TopLevel.cs | 12 +++--- 7 files changed, 52 insertions(+), 34 deletions(-) diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 3cd0521481..3d3ed00aac 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -13,6 +13,8 @@ namespace ControlCatalog.Pages { private double _leftMost; + protected override bool BypassFlowDirectionPolicies => true; + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); @@ -78,7 +80,5 @@ namespace ControlCatalog.Pages return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface.Default, 12, Brushes.Green); } - - protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index f209a07a31..cc2f79f574 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -183,12 +183,24 @@ namespace Avalonia.Controls this.UpdateSelectionBoxItem(SelectedItem); } - // Because the SelectedItem isn't connected to the visual tree + // Because the SelectedItem and his children sometimes isn't connected + // to the visual tree and sometimes not to logical tree public override void InvalidateFlowDirection() { + base.InvalidateFlowDirection(); + if (SelectedItem is Control selectedControl) { selectedControl.InvalidateFlowDirection(); + + foreach (var visual in selectedControl.GetVisualDescendants()) + { + if (visual is Control childControl) + { + childControl.InvalidateFlowDirection(); + } + } + foreach (var logical in selectedControl.GetLogicalDescendants()) { if (logical is Control childControl) @@ -197,8 +209,6 @@ namespace Avalonia.Controls } } } - - base.InvalidateFlowDirection(); } /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 16c3b1d5dd..ab8d17fbdf 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -140,6 +140,17 @@ namespace Avalonia.Controls /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; + /// + /// Gets a value indicating whether control bypass FlowDirecton policies. + /// + /// + /// Related to FlowDirection system and returns false as default, so if + /// is RTL then control will get a mirror presentation. + /// For controls that want to avoid mirror presentation, it is possible to override + /// this property and return true. + /// + protected virtual bool BypassFlowDirectionPolicies => false; + /// void ISetterValue.Initialize(ISetter setter) { @@ -338,37 +349,34 @@ namespace Avalonia.Controls /// /// Computes the value according to the - /// + /// and /// public virtual void InvalidateFlowDirection() { - bool parentShouldPresentMirrored = false; - bool thisShouldPresentMirrored = ShouldPresentMirrored(); + FlowDirection thisFD = this.FlowDirection; + FlowDirection parentFD = FlowDirection.LeftToRight; + + bool thisBypassFlowDirectionPolicies = BypassFlowDirectionPolicies; + bool parentBypassFlowDirectionPolicies = false; var parent = this.FindAncestorOfType(); if (parent != null) { - parentShouldPresentMirrored = parent.ShouldPresentMirrored(); + parentFD = parent.FlowDirection; + parentBypassFlowDirectionPolicies = parent.BypassFlowDirectionPolicies; } else if (this.Parent is Control logicalParent) { - parentShouldPresentMirrored = logicalParent.ShouldPresentMirrored(); + parentFD = logicalParent.FlowDirection; + parentBypassFlowDirectionPolicies = logicalParent.BypassFlowDirectionPolicies; } - bool shouldApplyMirrorTransform = thisShouldPresentMirrored != parentShouldPresentMirrored; + bool thisShouldBeMirrored = thisFD == FlowDirection.RightToLeft && !BypassFlowDirectionPolicies; + bool parentShouldBeMirrored = parentFD == FlowDirection.RightToLeft && !parentBypassFlowDirectionPolicies; - IsMirrorTransform = shouldApplyMirrorTransform; - } + bool shouldApplyMirrorTransform = thisShouldBeMirrored != parentShouldBeMirrored; - /// - /// Determines whether the element should be present mirrored, this - /// method related to FlowDirection system and returns true if FlowDirection - /// is RightToLeft. For controls that want to avoid this behavior, it is - /// possible to override this method and return false. - /// - protected virtual bool ShouldPresentMirrored() - { - return FlowDirection == FlowDirection.RightToLeft; + IsMirrorTransform = shouldApplyMirrorTransform; } } } diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index fa654f8ad1..7408bff902 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -66,6 +66,8 @@ namespace Avalonia.Controls set { SetValue(StretchDirectionProperty, value); } } + protected override bool BypassFlowDirectionPolicies => true; + /// /// Renders the control. /// @@ -127,7 +129,5 @@ namespace Avalonia.Controls return new Size(); } } - - protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 9b77b6ade1..5539afad46 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -281,6 +281,8 @@ namespace Avalonia.Controls.Presenters } } + protected override bool BypassFlowDirectionPolicies => true; + /// /// Creates the used to render the text. /// @@ -798,7 +800,5 @@ namespace Avalonia.Controls.Presenters } } } - - protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index bf510014b5..49800e5760 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -329,6 +329,8 @@ namespace Avalonia.Controls set => SetValue(TextDecorationsProperty, value); } + protected override bool BypassFlowDirectionPolicies => true; + /// /// Gets the value of the attached on a control. /// @@ -612,7 +614,5 @@ namespace Avalonia.Controls { InvalidateTextLayout(); } - - protected override bool ShouldPresentMirrored() => false; } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index a54bcf3bff..9bc96805a5 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -348,6 +348,12 @@ namespace Avalonia.Controls /// protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this); + public override void InvalidateFlowDirection() + { + } + + protected override bool BypassFlowDirectionPolicies => true; + /// /// Handles a paint notification from . /// @@ -529,11 +535,5 @@ namespace Avalonia.Controls ITextInputMethodImpl? ITextInputMethodRoot.InputMethod => (PlatformImpl as ITopLevelImplWithTextInputMethod)?.TextInputMethod; - - public override void InvalidateFlowDirection() - { - } - - protected override bool ShouldPresentMirrored() => false; } } From 8f1720b22cc9f6c5e76bdfd9fa2e1cf26fa4b704 Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Tue, 22 Mar 2022 11:27:14 +0200 Subject: [PATCH 226/820] typo --- src/Avalonia.Controls/Control.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index ab8d17fbdf..c8149fec10 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -146,8 +146,7 @@ namespace Avalonia.Controls /// /// Related to FlowDirection system and returns false as default, so if /// is RTL then control will get a mirror presentation. - /// For controls that want to avoid mirror presentation, it is possible to override - /// this property and return true. + /// For controls that want to avoid this behavior, override this property and return true. /// protected virtual bool BypassFlowDirectionPolicies => false; From 928a048e9cb12b4fa8abfb46654eb0b07ea450b7 Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Tue, 22 Mar 2022 12:30:27 +0100 Subject: [PATCH 227/820] Fix ToggleSwitchFillOffDisabled resource key typo --- src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml | 2 +- src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml b/src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml index 305f1b1814..2c831cf360 100644 --- a/src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml @@ -254,7 +254,7 @@ + + + + @@ -138,6 +152,17 @@ + + + + + + + + + + + @@ -147,10 +172,10 @@ - + { - + } @@ -158,17 +183,6 @@ - - - - - - - - - - - @@ -179,11 +193,11 @@ - + ( - + ) @@ -200,7 +214,7 @@ - + diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml.cs index 78919a2105..08ffe2c081 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml.cs @@ -7,9 +7,13 @@ namespace Avalonia.Diagnostics.Views { internal class ControlDetailsView : UserControl { + private DataGrid _dataGrid; + public ControlDetailsView() { InitializeComponent(); + + _dataGrid = this.GetControl("DataGrid"); } private void InitializeComponent() @@ -25,5 +29,25 @@ namespace Avalonia.Diagnostics.Views } } + + private void PropertyNamePressed(object sender, PointerPressedEventArgs e) + { + var mainVm = (ControlDetailsViewModel?) DataContext; + + if (mainVm is null) + { + return; + } + + if (sender is Control control && control.DataContext is SetterViewModel setterVm) + { + mainVm.SelectProperty(setterVm.Property); + + if (mainVm.SelectedProperty is not null) + { + _dataGrid.ScrollIntoView(mainVm.SelectedProperty, null); + } + } + } } } From b18cb5cac5b80981847acc75cc53c0914ba2bcbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Wed, 23 Mar 2022 23:14:36 +0100 Subject: [PATCH 243/820] Improve hit testing of inactive property names. --- .../Diagnostics/Views/ControlDetailsView.xaml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index 8280c4fe2f..cc392853be 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -141,6 +141,14 @@ + + + @@ -179,7 +187,7 @@ } - + @@ -201,7 +209,7 @@ ) - + @@ -218,7 +226,7 @@ - + From ad1779390f9131b8d0cf0a64e21855b803414c96 Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Wed, 23 Mar 2022 19:38:36 -0400 Subject: [PATCH 244/820] Add UseDarkMode property to DevToolsOptions --- .../Diagnostics/DevToolsOptions.cs | 5 +++++ .../Diagnostics/Views/MainWindow.xaml.cs | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs b/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs index 46ee8e686c..5672641602 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs @@ -41,5 +41,10 @@ namespace Avalonia.Diagnostics /// Default handler is public IScreenshotHandler ScreenshotHandler { get; set; } = Convetions.DefaultScreenshotHandler; + + /// + /// Gets or sets whether DevTools should use the dark mode theme + /// + public bool UseDarkMode { get; set; } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index a3cff7f3d3..1e152dcdd4 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -11,6 +11,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Markup.Xaml; using Avalonia.Styling; +using Avalonia.Themes.Default; using Avalonia.VisualTree; namespace Avalonia.Diagnostics.Views @@ -242,7 +243,17 @@ namespace Avalonia.Diagnostics.Views private void RootClosed(object? sender, EventArgs e) => Close(); - public void SetOptions(DevToolsOptions options) => + public void SetOptions(DevToolsOptions options) + { (DataContext as MainViewModel)?.SetOptions(options); + + if (options.UseDarkMode) + { + if (Styles[0] is SimpleTheme st) + { + st.Mode = SimpleThemeMode.Dark; + } + } + } } } From 9ad87658f194757a66b62251f2ebb02308035dd9 Mon Sep 17 00:00:00 2001 From: Andrej Bunjac Date: Thu, 24 Mar 2022 14:16:18 +0100 Subject: [PATCH 245/820] Added fixes for Margin, Padding and Thickness properties with UseLayoutRounding = true. --- src/Avalonia.Controls/Border.cs | 25 +++++++- .../Presenters/ContentPresenter.cs | 40 +++++++++++-- src/Avalonia.Layout/LayoutHelper.cs | 59 ++++++++++++++++++- src/Avalonia.Layout/Layoutable.cs | 14 ++++- 4 files changed, 128 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index ee3be1d5b3..ce64570dc8 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -169,13 +169,36 @@ namespace Avalonia.Controls set => SetValue(BoxShadowProperty, value); } + private Thickness _layoutThickness = default; + + private Thickness LayoutThickness + { + get + { + if (_layoutThickness == default) + { + var borderThickness = BorderThickness; + + if (UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(this); + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); + } + + _layoutThickness = borderThickness; + } + + return _layoutThickness; + } + } + /// /// Renders the control. /// /// The drawing context. public override void Render(DrawingContext context) { - _borderRenderHelper.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush, + _borderRenderHelper.Render(context, Bounds.Size, LayoutThickness, CornerRadius, Background, BorderBrush, BoxShadow, BorderDashOffset, BorderLineCap, BorderLineJoin, BorderDashArray); } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 93acd88fb1..bbb772a4ce 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -329,10 +329,33 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } + private Thickness _layoutThickness = default; + + private Thickness LayoutThickness + { + get + { + if (_layoutThickness == default) + { + var borderThickness = BorderThickness; + + if (UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(this); + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); + } + + _layoutThickness = borderThickness; + } + + return _layoutThickness; + } + } + /// public override void Render(DrawingContext context) { - _borderRenderer.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush, + _borderRenderer.Render(context, Bounds.Size, LayoutThickness, CornerRadius, Background, BorderBrush, BoxShadow); } @@ -400,13 +423,22 @@ namespace Avalonia.Controls.Presenters { if (Child == null) return finalSize; - var padding = Padding + BorderThickness; + var useLayoutRounding = UseLayoutRounding; + var scale = LayoutHelper.GetLayoutScale(this); + var padding = Padding; + var borderThickness = BorderThickness; + + if (useLayoutRounding) + { + padding = LayoutHelper.RoundLayoutThickness(padding, scale, scale); + borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, scale, scale); + } + + padding += borderThickness; var horizontalContentAlignment = HorizontalContentAlignment; var verticalContentAlignment = VerticalContentAlignment; - var useLayoutRounding = UseLayoutRounding; var availableSize = finalSize; var sizeForChild = availableSize; - var scale = LayoutHelper.GetLayoutScale(this); var originX = offset.X; var originY = offset.Y; diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index d4154a6d0c..d24be57d2b 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -52,16 +52,44 @@ namespace Avalonia.Layout public static Size ArrangeChild(ILayoutable? child, Size availableSize, Thickness padding, Thickness borderThickness) { - return ArrangeChild(child, availableSize, padding + borderThickness); + if (IsParentLayoutRounded(child, out double scale)) + { + padding = RoundLayoutThickness(padding, scale, scale); + borderThickness = RoundLayoutThickness(borderThickness, scale, scale); + } + + return ArrangeChildInternal(child, availableSize, padding + borderThickness); } public static Size ArrangeChild(ILayoutable? child, Size availableSize, Thickness padding) + { + if(IsParentLayoutRounded(child, out double scale)) + padding = RoundLayoutThickness(padding, scale, scale); + + return ArrangeChildInternal(child, availableSize, padding); + } + + private static Size ArrangeChildInternal(ILayoutable? child, Size availableSize, Thickness padding) { child?.Arrange(new Rect(availableSize).Deflate(padding)); return availableSize; } + private static bool IsParentLayoutRounded(ILayoutable? child, out double scale) + { + var layoutableParent = (ILayoutable?)child?.GetVisualParent(); + + if (layoutableParent == null || !((Layoutable)layoutableParent).UseLayoutRounding) + { + scale = 1.0; + return false; + } + + scale = GetLayoutScale(layoutableParent); + return true; + } + /// /// Invalidates measure for given control and all visual children recursively. /// @@ -126,6 +154,32 @@ namespace Avalonia.Layout return new Size(RoundLayoutValue(size.Width, dpiScaleX), RoundLayoutValue(size.Height, dpiScaleY)); } + /// + /// Rounds a thickness to integer values for layout purposes, compensating for high DPI screen + /// coordinates. + /// + /// Input thickness. + /// DPI along x-dimension. + /// DPI along y-dimension. + /// Value of thickness that will be rounded under screen DPI. + /// + /// This is a layout helper method. It takes DPI into account and also does not return + /// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper + /// associated with the UseLayoutRounding property and should not be used as a general rounding + /// utility. + /// + public static Thickness RoundLayoutThickness(Thickness thickness, double dpiScaleX, double dpiScaleY) + { + return new Thickness( + RoundLayoutValue(thickness.Left, dpiScaleX), + RoundLayoutValue(thickness.Top, dpiScaleY), + RoundLayoutValue(thickness.Right, dpiScaleX), + RoundLayoutValue(thickness.Bottom, dpiScaleY) + ); + } + + + /// /// Calculates the value to be used for layout rounding at high DPI. /// @@ -163,8 +217,7 @@ namespace Avalonia.Layout return newValue; } - - + /// /// Calculates the min and max height for a control. Ported from WPF. /// diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 09e0c4263a..23a76f6ee2 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -643,17 +643,27 @@ namespace Avalonia.Layout { if (IsVisible) { + var useLayoutRounding = UseLayoutRounding; + var scale = LayoutHelper.GetLayoutScale(this); + var margin = Margin; var originX = finalRect.X + margin.Left; var originY = finalRect.Y + margin.Top; + + // Margin has to be treated separately because the layout rounding function is not linear + // f(a + b) != f(a) + f(b) + // If the margin isn't pre-rounded some sizes will be offset by 1 pixel in certain scales. + if (UseLayoutRounding) + { + margin = LayoutHelper.RoundLayoutThickness(margin, scale, scale); + } + var availableSizeMinusMargins = new Size( Math.Max(0, finalRect.Width - margin.Left - margin.Right), Math.Max(0, finalRect.Height - margin.Top - margin.Bottom)); var horizontalAlignment = HorizontalAlignment; var verticalAlignment = VerticalAlignment; var size = availableSizeMinusMargins; - var scale = LayoutHelper.GetLayoutScale(this); - var useLayoutRounding = UseLayoutRounding; if (horizontalAlignment != HorizontalAlignment.Stretch) { From 9eff7810f88e342274f3664ea55d3cc03241bbd2 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 25 Mar 2022 08:14:15 +0100 Subject: [PATCH 246/820] Relax typface resolution --- src/Skia/Avalonia.Skia/FontManagerImpl.cs | 4 +--- .../Avalonia.Skia/SKTypefaceCollection.cs | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs index 075a2cc746..125dd0e455 100644 --- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs +++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs @@ -122,9 +122,7 @@ namespace Avalonia.Skia skTypeface = _skFontManager.MatchFamily(familyName, fontStyle); - if (skTypeface is null - || (!skTypeface.FamilyName.Equals(familyName, StringComparison.Ordinal) - && defaultName.Equals(skTypeface.FamilyName, StringComparison.Ordinal))) + if (skTypeface is null || defaultName.Equals(skTypeface.FamilyName, StringComparison.Ordinal)) { continue; } diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs index f66df9e6e9..1f3f20730f 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs @@ -59,15 +59,30 @@ namespace Avalonia.Skia return typeface; } - //Nothing was found so we try some regular cases. + if (TryFindStretchFallback(key, out typeface)) + { + return typeface; + } + + //Nothing was found so we try some regular typeface. if (_typefaces.TryGetValue(new Typeface(key.FontFamily), out typeface)) { return typeface; } - return _typefaces.TryGetValue(new Typeface(key.FontFamily, FontStyle.Italic), out typeface) ? - typeface : - null; + SKTypeface skTypeface = null; + + foreach(var pair in _typefaces) + { + skTypeface = pair.Value; + + if (skTypeface.FamilyName.Contains(key.FontFamily.Name)) + { + return skTypeface; + } + } + + return skTypeface; } private bool TryFindStretchFallback(Typeface key, out SKTypeface typeface) From 5dd49bbf50908e67bbabe634bb83a52058480803 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 25 Mar 2022 09:50:21 +0100 Subject: [PATCH 247/820] Update unit tests --- .../Media/SKTypefaceCollectionCacheTests.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Skia.UnitTests/Media/SKTypefaceCollectionCacheTests.cs b/tests/Avalonia.Skia.UnitTests/Media/SKTypefaceCollectionCacheTests.cs index ddf4a36dcd..64050bd85e 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/SKTypefaceCollectionCacheTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/SKTypefaceCollectionCacheTests.cs @@ -28,7 +28,7 @@ namespace Avalonia.Skia.UnitTests.Media } [Fact] - public void Should_Get_Null_For_Invalid_FamilyName() + public void Should_Get_Typeface_For_Invalid_FamilyName() { using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) { @@ -39,7 +39,24 @@ namespace Avalonia.Skia.UnitTests.Media var typeface = notoMonoCollection.Get(new Typeface("ABC")); - Assert.Null(typeface); + Assert.NotNull(typeface); + } + } + + [Fact] + public void Should_Get_Typeface_For_Partial_FamilyName() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var fontFamily = new FontFamily("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#T"); + + var fontCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(fontFamily); + + var typeface = fontCollection.Get(new Typeface(fontFamily)); + + Assert.NotNull(typeface); + + Assert.Equal("Twitter Color Emoji", typeface.FamilyName); } } } From b0f2c20c37b09d0e1c1f4b7b422a3c69ac3f8feb Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Sat, 26 Mar 2022 15:31:01 -0400 Subject: [PATCH 248/820] ThicknessEditor always use black foreground for readibility --- .../Diagnostics/Views/LayoutExplorerView.axaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml b/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml index af6c84a76a..db48aaaa8e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml @@ -15,6 +15,7 @@ + @@ -46,6 +47,7 @@ From a3b5ee837539de36e1f9c170ba4e9b69e47e5d70 Mon Sep 17 00:00:00 2001 From: Tim Date: Sun, 27 Mar 2022 16:30:12 +0200 Subject: [PATCH 249/820] Adding Test for BindingDisposable --- .../AvaloniaObjectTests_Binding.cs | 109 ++++++++++++++++-- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index d4e14d00b8..6b4a6f89df 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -11,6 +11,7 @@ using Avalonia.Logging; using Avalonia.Platform; using Avalonia.Threading; using Avalonia.UnitTests; +using Avalonia.Utilities; using Microsoft.Reactive.Testing; using Moq; using Xunit; @@ -785,7 +786,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }); - Assert.False(source.SetterCalled); + Assert.False(source.ValueSetterCalled); } [Fact] @@ -796,7 +797,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source }); - Assert.False(source.SetterCalled); + Assert.False(source.ValueSetterCalled); } [Fact] @@ -838,7 +839,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }); - Assert.False(source.SetterCalled); + Assert.False(source.ValueSetterCalled); } [Fact] @@ -850,7 +851,36 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source }); - Assert.False(source.SetterCalled); + Assert.False(source.ValueSetterCalled); + } + + + [Fact] + public void Disposing_a_TwoWay_Binding_Should_Set_Default_Value_On_Binding_Target_But_Not_On_Source() + { + var target = new Class3(); + + // Create a source class which has a Value set to -1 and a Minimum set to -2 + var source = new TestTwoWayBindingViewModel() { Value = -1, Minimum = -2 }; + + // Reset the setter counter + source.ResetSetterCalled(); + + // 1. bind the minimum + var disposable_1 = target.Bind(Class3.MinimumProperty, new Binding("Minimum", BindingMode.TwoWay) { Source = source }); + // 2. Bind the value + var disposable_2 = target.Bind(Class3.ValueProperty, new Binding("Value", BindingMode.TwoWay) { Source = source }); + + // Dispose the minimum binding + disposable_1.Dispose(); + // Dispose the value binding + disposable_2.Dispose(); + + + // The value setter should be called here as we have disposed minimum fist and the default value of minimum is 0, so this should be changed. + Assert.True(source.ValueSetterCalled); + // The minimum value should not be changed in the source. + Assert.False(source.MinimumSetterCalled); } /// @@ -888,6 +918,56 @@ namespace Avalonia.Base.UnitTests AvaloniaProperty.Register("Bar", "bardefault"); } + private class Class3 : AvaloniaObject + { + static Class3() + { + MinimumProperty.Changed.Subscribe(x => OnMinimumChanged(x)); + } + + private static void OnMinimumChanged(AvaloniaPropertyChangedEventArgs e) + { + if (e.Sender is Class3 s) + { + s.SetValue(ValueProperty, MathUtilities.Clamp(s.Value, e.NewValue.Value, double.PositiveInfinity)); + } + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ValueProperty = + AvaloniaProperty.Register(nameof(Value), 0); + + /// + /// Gets or sets the Value property + /// + public double Value + { + get { return GetValue(ValueProperty); } + set { SetValue(ValueProperty, value); } + } + + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinimumProperty = + AvaloniaProperty.Register(nameof(Minimum), 0); + + /// + /// Gets or sets the minimum property + /// + public double Minimum + { + get { return GetValue(MinimumProperty); } + set { SetValue(MinimumProperty, value); } + } + + + } + + private class TestOneTimeBinding : IBinding { private IObservable _source; @@ -952,7 +1032,18 @@ namespace Avalonia.Base.UnitTests set { _value = value; - SetterCalled = true; + ValueSetterCalled = true; + } + } + + private double _minimum; + public double Minimum + { + get => _minimum; + set + { + _minimum = value; + MinimumSetterCalled = true; } } @@ -962,15 +1053,17 @@ namespace Avalonia.Base.UnitTests set { _value = value; - SetterCalled = true; + ValueSetterCalled = true; } } - public bool SetterCalled { get; private set; } + public bool ValueSetterCalled { get; private set; } + public bool MinimumSetterCalled { get; private set; } public void ResetSetterCalled() { - SetterCalled = false; + ValueSetterCalled = false; + MinimumSetterCalled = false; } } } From d3551ce99fa850c840f8730fafcfab256632db01 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 11:59:33 -0400 Subject: [PATCH 250/820] Use the new TemplatePartAttribute --- .../ColorSpectrum/ColorSpectrum.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index a9497b98f1..9d04d1385b 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -20,6 +20,15 @@ namespace Avalonia.Controls.Primitives /// /// A two dimensional spectrum for color selection. /// + [TemplatePart(Name = "PART_ColorNameToolTip", Type = typeof(ToolTip))] + [TemplatePart(Name = "PART_InputTarget", Type = typeof(Canvas))] + [TemplatePart(Name = "PART_LayoutRoot", Type = typeof(Grid))] + [TemplatePart(Name = "PART_SelectionEllipsePanel", Type = typeof(Panel))] + [TemplatePart(Name = "PART_SizingGrid", Type = typeof(Grid))] + [TemplatePart(Name = "PART_SpectrumEllipse", Type = typeof(Ellipse))] + [TemplatePart(Name = "PART_SpectrumRectangle", Type = typeof(Rectangle))] + [TemplatePart(Name = "PART_SpectrumOverlayEllipse", Type = typeof(Ellipse))] + [TemplatePart(Name = "PART_SpectrumOverlayRectangle", Type = typeof(Rectangle))] [PseudoClasses(pcPressed, pcLargeSelector, pcLightSelector)] public partial class ColorSpectrum : TemplatedControl { @@ -108,15 +117,15 @@ namespace Avalonia.Controls.Primitives UnregisterEvents(); + _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); + _inputTarget = e.NameScope.Find("PART_InputTarget"); _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); + _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); _sizingGrid = e.NameScope.Find("PART_SizingGrid"); - _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); _spectrumEllipse = e.NameScope.Find("PART_SpectrumEllipse"); - _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); + _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); - _inputTarget = e.NameScope.Find("PART_InputTarget"); - _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); - _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); + _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); if (_layoutRoot != null) { From 32f8b63764fcbfeda894415e706b3686fe07997b Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:01:17 -0400 Subject: [PATCH 251/820] Remove extra variables leftover from C# conversion --- .../ColorSpectrum/ColorSpectrum.cs | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 9d04d1385b..abd84cc452 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -889,41 +889,33 @@ namespace Avalonia.Controls.Primitives return; } - var layoutRoot = _layoutRoot; - var sizingGrid = _sizingGrid; - var inputTarget = _inputTarget; - var spectrumRectangle = _spectrumRectangle; - var spectrumEllipse = _spectrumEllipse; - var spectrumOverlayRectangle = _spectrumOverlayRectangle; - var spectrumOverlayEllipse = _spectrumOverlayEllipse; - // We want ColorSpectrum to always be a square, so we'll take the smaller of the dimensions // and size the sizing grid to that. - double minDimension = Math.Min(layoutRoot.Bounds.Width, layoutRoot.Bounds.Height); + double minDimension = Math.Min(_layoutRoot.Bounds.Width, _layoutRoot.Bounds.Height); if (minDimension == 0) { return; } - sizingGrid.Width = minDimension; - sizingGrid.Height = minDimension; + _sizingGrid.Width = minDimension; + _sizingGrid.Height = minDimension; - if (sizingGrid.Clip is RectangleGeometry clip) + if (_sizingGrid.Clip is RectangleGeometry clip) { clip.Rect = new Rect(0, 0, minDimension, minDimension); } - inputTarget.Width = minDimension; - inputTarget.Height = minDimension; - spectrumRectangle.Width = minDimension; - spectrumRectangle.Height = minDimension; - spectrumEllipse.Width = minDimension; - spectrumEllipse.Height = minDimension; - spectrumOverlayRectangle.Width = minDimension; - spectrumOverlayRectangle.Height = minDimension; - spectrumOverlayEllipse.Width = minDimension; - spectrumOverlayEllipse.Height = minDimension; + _inputTarget.Width = minDimension; + _inputTarget.Height = minDimension; + _spectrumRectangle.Width = minDimension; + _spectrumRectangle.Height = minDimension; + _spectrumEllipse.Width = minDimension; + _spectrumEllipse.Height = minDimension; + _spectrumOverlayRectangle.Width = minDimension; + _spectrumOverlayRectangle.Height = minDimension; + _spectrumOverlayEllipse.Width = minDimension; + _spectrumOverlayEllipse.Height = minDimension; HsvColor hsvColor = HsvColor; int minHue = MinHue; From a47d9b6487dad0303eec8728f30bc14c25d911c8 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:14:09 -0400 Subject: [PATCH 252/820] Improve comments ColorSpectrumChannels clarifying axis mappings --- .../ColorPicker/ColorSpectrumChannels.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs index 2b3f711634..a31586d175 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs @@ -9,38 +9,65 @@ namespace Avalonia.Controls { /// /// Defines the two HSV color channels displayed by a . - /// Order of the color channels is important. /// + /// + /// Order of the color channels is important and correspond with an X/Y axis in Box + /// shape or a degree/radius in Ring shape. + /// public enum ColorSpectrumChannels { /// /// The Hue and Value channels. /// + /// + /// In Box shape, Hue is mapped to the X-axis and Value is mapped to the Y-axis. + /// In Ring shape, Hue is mapped to degrees and Value is mapped to radius. + /// HueValue, /// /// The Value and Hue channels. /// + /// + /// In Box shape, Value is mapped to the X-axis and Hue is mapped to the Y-axis. + /// In Ring shape, Value is mapped to degrees and Hue is mapped to radius. + /// ValueHue, /// /// The Hue and Saturation channels. /// + /// + /// In Box shape, Hue is mapped to the X-axis and Saturation is mapped to the Y-axis. + /// In Ring shape, Hue is mapped to degrees and Saturation is mapped to radius. + /// HueSaturation, /// /// The Saturation and Hue channels. /// + /// + /// In Box shape, Saturation is mapped to the X-axis and Hue is mapped to the Y-axis. + /// In Ring shape, Saturation is mapped to degrees and Hue is mapped to radius. + /// SaturationHue, /// /// The Saturation and Value channels. /// + /// + /// In Box shape, Saturation is mapped to the X-axis and Value is mapped to the Y-axis. + /// In Ring shape, Saturation is mapped to degrees and Value is mapped to radius. + /// SaturationValue, /// /// The Value and Saturation channels. /// + /// + /// In Box shape, Value is mapped to the X-axis and Saturation is mapped to the Y-axis. + /// In Ring shape, Value is mapped to degrees and Saturation is mapped to radius. + /// ValueSaturation, }; } From 5904057bd200fb8b933eb8ad516d3db0c25532f6 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:41:59 -0400 Subject: [PATCH 253/820] Separate border style setters for easier customization --- .../Controls/ColorSpectrum.xaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index 6a958809a0..8657a072a4 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -73,8 +73,6 @@ --> + + + + From 88370ce91bbbe1d1b786c578a22a3286f6329851 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:49:10 -0400 Subject: [PATCH 255/820] Add Default theme for ColorSpectrum --- .../Controls/ColorSpectrum.xaml | 138 ++++++++++++++++++ src/Avalonia.Themes.Default/DefaultTheme.xaml | 1 + 2 files changed, 139 insertions(+) create mode 100644 src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml new file mode 100644 index 0000000000..128f7038eb --- /dev/null +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 846d45b839..eea5af5932 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -11,6 +11,7 @@ + From 7ddadfd5464e8de508a457314a4c47e2cfdceb9f Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 13:17:41 -0400 Subject: [PATCH 256/820] Remove CornerRadius prop from MenuFlyoutPresenter --- src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs index bcd859100a..6a7da87387 100644 --- a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs +++ b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs @@ -8,15 +8,6 @@ namespace Avalonia.Controls { public class MenuFlyoutPresenter : MenuBase { - public static readonly StyledProperty CornerRadiusProperty = - Border.CornerRadiusProperty.AddOwner(); - - public CornerRadius CornerRadius - { - get => GetValue(CornerRadiusProperty); - set => SetValue(CornerRadiusProperty, value); - } - public MenuFlyoutPresenter() :base(new DefaultMenuInteractionHandler(true)) { From 28f66403d303018376cdbb42e618b24cb9bd1015 Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Mon, 28 Mar 2022 09:55:15 +0300 Subject: [PATCH 257/820] etc --- src/Avalonia.Controls/Control.cs | 9 ++++++++- src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 8c6ac0718a..8bb0774655 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -200,7 +200,6 @@ namespace Avalonia.Controls { base.OnAttachedToVisualTreeCore(e); - InvalidateFlowDirection(); InitializeIfNeeded(); } @@ -210,6 +209,14 @@ namespace Avalonia.Controls base.OnDetachedFromVisualTreeCore(e); } + /// + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + InvalidateFlowDirection(); + } + /// protected override void OnGotFocus(GotFocusEventArgs e) { diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 23016de148..d92919884b 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -285,6 +285,12 @@ namespace Avalonia.Rendering renderTransform = (-offset) * visual.RenderTransform.Value * (offset); } + if (visual.IsMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); + renderTransform *= mirrorMatrix; + } + m = renderTransform * m; if (clipToBounds) From d0a27b6bc49bb33ef46361cabfb7a8f6c535be26 Mon Sep 17 00:00:00 2001 From: Lubomir Tetak Date: Mon, 28 Mar 2022 10:22:48 +0200 Subject: [PATCH 258/820] Suppress static SKFont finalizer to prevent crashes on app exit --- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index af3b570fd7..1c92c7d193 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -35,6 +35,9 @@ namespace Avalonia.Skia var gl = AvaloniaLocator.Current.GetService(); if (gl != null) _skiaGpu = new GlSkiaGpu(gl, maxResourceBytes); + + //TODO: SKFont crashes when disposed in finalizer so we keep it alive + GC.SuppressFinalize(s_font); } public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect); From 5669a640d019ab5425775a80339aa73a32662dcd Mon Sep 17 00:00:00 2001 From: Kibnet Philosoff Date: Wed, 9 Feb 2022 13:13:27 +0300 Subject: [PATCH 259/820] Fix #7567 - TreeView crashes the app when trying to display the same viewmodel twice --- .../Generators/TreeContainerIndex.cs | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs index da13416700..eb60fca367 100644 --- a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs +++ b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs @@ -15,6 +15,7 @@ namespace Avalonia.Controls.Generators /// public class TreeContainerIndex { + private readonly Dictionary> _itemToContainerSet = new Dictionary>(); private readonly Dictionary _itemToContainer = new Dictionary(); private readonly Dictionary _containerToItem = new Dictionary(); @@ -45,14 +46,45 @@ namespace Avalonia.Controls.Generators /// The item container. public void Add(object item, IControl container) { - _itemToContainer.Add(item, container); + _itemToContainer[item] = container; + if (_itemToContainerSet.TryGetValue(item, out var set)) + { + set.Add(container); + } + else + { + _itemToContainerSet.Add(item, new HashSet { container }); + } + _containerToItem.Add(container, item); Materialized?.Invoke( - this, + this, new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0))); } + /// + /// Removes a container from private collections. + /// + /// The item container. + /// The DataContext object + private void RemoveContainer(IControl container, object item) + { + if (_itemToContainerSet.TryGetValue(item, out var set)) + { + set.Remove(container); + if (set.Count == 0) + { + _itemToContainerSet.Remove(item); + _itemToContainer.Remove(item); + } + else + { + _itemToContainer[item] = set.First(); + } + } + } + /// /// Removes a container from the index. /// @@ -61,10 +93,10 @@ namespace Avalonia.Controls.Generators { var item = _containerToItem[container]; _containerToItem.Remove(container); - _itemToContainer.Remove(item); + RemoveContainer(container, item); Dematerialized?.Invoke( - this, + this, new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0))); } @@ -79,7 +111,7 @@ namespace Avalonia.Controls.Generators { var item = _containerToItem[container.ContainerControl]; _containerToItem.Remove(container.ContainerControl); - _itemToContainer.Remove(item); + RemoveContainer(container.ContainerControl, item); } Dematerialized?.Invoke( @@ -97,6 +129,14 @@ namespace Avalonia.Controls.Generators if (item != null) { _itemToContainer.TryGetValue(item, out var result); + if (result == null) + { + _itemToContainerSet.TryGetValue(item, out var set); + if (set?.Count > 0) + { + return set.FirstOrDefault(); + } + } return result; } @@ -113,6 +153,10 @@ namespace Avalonia.Controls.Generators if (container != null) { _containerToItem.TryGetValue(container, out var result); + if (result != null) + { + _itemToContainer[result] = container; + } return result; } From 0236275e09fd02eed3fba058835675d3e3ee8189 Mon Sep 17 00:00:00 2001 From: Tako <53405089+Takoooooo@users.noreply.github.com> Date: Mon, 28 Mar 2022 18:17:56 +0300 Subject: [PATCH 260/820] Update readme.md We don't maintain this build feed anymore so the badge is redundant. --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 7e32dbc321..1cdaf3b8f8 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ [![Telegram](https://raw.githubusercontent.com/Patrolavia/telegram-badge/master/chat.svg)](https://t.me/Avalonia) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
-[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) +[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) ## 📖 About From 5a76c63c478ec21a6b2f8b960f6e6e7623904a56 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 11:36:37 -0400 Subject: [PATCH 261/820] Simplify TemplatePartAttribute usage --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index abd84cc452..62d3b4ce88 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -20,15 +20,15 @@ namespace Avalonia.Controls.Primitives /// /// A two dimensional spectrum for color selection. /// - [TemplatePart(Name = "PART_ColorNameToolTip", Type = typeof(ToolTip))] - [TemplatePart(Name = "PART_InputTarget", Type = typeof(Canvas))] - [TemplatePart(Name = "PART_LayoutRoot", Type = typeof(Grid))] - [TemplatePart(Name = "PART_SelectionEllipsePanel", Type = typeof(Panel))] - [TemplatePart(Name = "PART_SizingGrid", Type = typeof(Grid))] - [TemplatePart(Name = "PART_SpectrumEllipse", Type = typeof(Ellipse))] - [TemplatePart(Name = "PART_SpectrumRectangle", Type = typeof(Rectangle))] - [TemplatePart(Name = "PART_SpectrumOverlayEllipse", Type = typeof(Ellipse))] - [TemplatePart(Name = "PART_SpectrumOverlayRectangle", Type = typeof(Rectangle))] + [TemplatePart("PART_ColorNameToolTip", typeof(ToolTip))] + [TemplatePart("PART_InputTarget", typeof(Canvas))] + [TemplatePart("PART_LayoutRoot", typeof(Grid))] + [TemplatePart("PART_SelectionEllipsePanel", typeof(Panel))] + [TemplatePart("PART_SizingGrid", typeof(Grid))] + [TemplatePart("PART_SpectrumEllipse", typeof(Ellipse))] + [TemplatePart("PART_SpectrumRectangle", typeof(Rectangle))] + [TemplatePart("PART_SpectrumOverlayEllipse", typeof(Ellipse))] + [TemplatePart("PART_SpectrumOverlayRectangle", typeof(Rectangle))] [PseudoClasses(pcPressed, pcLargeSelector, pcLightSelector)] public partial class ColorSpectrum : TemplatedControl { From 4fe7fb71e67264d8635c2fa5383138b2eab7ff12 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 11:44:32 -0400 Subject: [PATCH 262/820] Simplify ColorChangedEventArgs --- .../ColorPicker/ColorChangedEventArgs.cs | 26 +++---------------- .../ColorSpectrum/ColorSpectrum.cs | 6 +---- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs index 93ba1a4db1..b1d15d6b17 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs @@ -17,16 +17,6 @@ namespace Avalonia.Controls /// public class ColorChangedEventArgs : EventArgs { - private Color _OldColor; - private Color _NewColor; - - /// - /// Initializes a new instance of the class. - /// - public ColorChangedEventArgs() - { - } - /// /// Initializes a new instance of the class. /// @@ -34,26 +24,18 @@ namespace Avalonia.Controls /// The new/updated color that triggered the change event. public ColorChangedEventArgs(Color oldColor, Color newColor) { - _OldColor = oldColor; - _NewColor = newColor; + OldColor = oldColor; + NewColor = newColor; } /// /// Gets the old/original color from before the change event. /// - public Color OldColor - { - get => _OldColor; - internal set => _OldColor = value; - } + public Color OldColor { get; private set; } /// /// Gets the new/updated color that triggered the change event. /// - public Color NewColor - { - get => _NewColor; - internal set => _NewColor = value; - } + public Color NewColor { get; private set; } } } diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 62d3b4ce88..11e1437f81 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -499,11 +499,7 @@ namespace Avalonia.Controls.Primitives if (colorChanged || areBothColorsBlack) { - var colorChangedEventArgs = new ColorChangedEventArgs(); - - colorChangedEventArgs.OldColor = _oldColor; - colorChangedEventArgs.NewColor = newColor; - + var colorChangedEventArgs = new ColorChangedEventArgs(_oldColor, newColor); ColorChanged?.Invoke(this, colorChangedEventArgs); if (ColorHelpers.ToDisplayNameExists) From 073b3e4d34706a1198fc7fea3f9d02dc747dcb50 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:05:14 -0400 Subject: [PATCH 263/820] Slightly rework events using OnAttached/DetachedToVisualTree --- .../ColorSpectrum/ColorSpectrum.cs | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 11e1437f81..dfead58c6a 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -115,7 +115,7 @@ namespace Avalonia.Controls.Primitives { base.OnApplyTemplate(e); - UnregisterEvents(); + UnregisterEvents(); // Failsafe _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); _inputTarget = e.NameScope.Find("PART_InputTarget"); @@ -127,14 +127,6 @@ namespace Avalonia.Controls.Primitives _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); - if (_layoutRoot != null) - { - _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => - { - CreateBitmapsAndColorMap(); - }); - } - if (_inputTarget != null) { _inputTarget.PointerEnter += OnInputTargetPointerEnter; @@ -144,10 +136,12 @@ namespace Avalonia.Controls.Primitives _inputTarget.PointerReleased += OnInputTargetPointerReleased; } - if (ColorHelpers.ToDisplayNameExists && - _colorNameToolTip != null) + if (_layoutRoot != null) { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => + { + CreateBitmapsAndColorMap(); + }); } if (_selectionEllipsePanel != null) @@ -158,6 +152,12 @@ namespace Avalonia.Controls.Primitives }); } + if (ColorHelpers.ToDisplayNameExists && + _colorNameToolTip != null) + { + _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + } + // If we haven't yet created our bitmaps, do so now. if (_hsvValues.Count == 0) { @@ -168,6 +168,22 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); } + /// + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + // OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here + } + + /// + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + UnregisterEvents(); + } + /// /// Explicitly unregisters all events connected in OnApplyTemplate(). /// From 7e3cf42c00f6dfe6ea1a3a16af4f2906224cbff6 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:08:05 -0400 Subject: [PATCH 264/820] Rename event handlers that are not overridable methods --- .../ColorSpectrum/ColorSpectrum.cs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index dfead58c6a..c63bcb225b 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -129,11 +129,11 @@ namespace Avalonia.Controls.Primitives if (_inputTarget != null) { - _inputTarget.PointerEnter += OnInputTargetPointerEnter; - _inputTarget.PointerLeave += OnInputTargetPointerLeave; - _inputTarget.PointerPressed += OnInputTargetPointerPressed; - _inputTarget.PointerMoved += OnInputTargetPointerMoved; - _inputTarget.PointerReleased += OnInputTargetPointerReleased; + _inputTarget.PointerEnter += InputTarget_PointerEnter; + _inputTarget.PointerLeave += InputTarget_PointerLeave; + _inputTarget.PointerPressed += InputTarget_PointerPressed; + _inputTarget.PointerMoved += InputTarget_PointerMoved; + _inputTarget.PointerReleased += InputTarget_PointerReleased; } if (_layoutRoot != null) @@ -197,11 +197,11 @@ namespace Avalonia.Controls.Primitives if (_inputTarget != null) { - _inputTarget.PointerEnter -= OnInputTargetPointerEnter; - _inputTarget.PointerLeave -= OnInputTargetPointerLeave; - _inputTarget.PointerPressed -= OnInputTargetPointerPressed; - _inputTarget.PointerMoved -= OnInputTargetPointerMoved; - _inputTarget.PointerReleased -= OnInputTargetPointerReleased; + _inputTarget.PointerEnter -= InputTarget_PointerEnter; + _inputTarget.PointerLeave -= InputTarget_PointerLeave; + _inputTarget.PointerPressed -= InputTarget_PointerPressed; + _inputTarget.PointerMoved -= InputTarget_PointerMoved; + _inputTarget.PointerReleased -= InputTarget_PointerReleased; } } @@ -830,21 +830,24 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); } - private void OnInputTargetPointerEnter(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerEnter(object? sender, PointerEventArgs args) { _isPointerOver = true; UpdatePseudoClasses(); args.Handled = true; } - private void OnInputTargetPointerLeave(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerLeave(object? sender, PointerEventArgs args) { _isPointerOver = false; UpdatePseudoClasses(); args.Handled = true; } - private void OnInputTargetPointerPressed(object? sender, PointerPressedEventArgs args) + /// + private void InputTarget_PointerPressed(object? sender, PointerPressedEventArgs args) { var inputTarget = _inputTarget; @@ -864,7 +867,8 @@ namespace Avalonia.Controls.Primitives args.Handled = true; } - private void OnInputTargetPointerMoved(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerMoved(object? sender, PointerEventArgs args) { if (!_isPointerPressed) { @@ -875,7 +879,8 @@ namespace Avalonia.Controls.Primitives args.Handled = true; } - private void OnInputTargetPointerReleased(object? sender, PointerReleasedEventArgs args) + /// + private void InputTarget_PointerReleased(object? sender, PointerReleasedEventArgs args) { _isPointerPressed = false; _shouldShowLargeSelection = false; From 89b6e4e9d05f95d6ef73f30a777387e72f4d3ff2 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:10:39 -0400 Subject: [PATCH 265/820] If we can't connect in OnAttachedToVisualTree we also can't disconnect --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index c63bcb225b..2ff264baf9 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -180,8 +180,6 @@ namespace Avalonia.Controls.Primitives protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); - - UnregisterEvents(); } /// From 088d63b9128e153ef91c7cfb4015718eb0f309ed Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:17:15 -0400 Subject: [PATCH 266/820] Remove Clip geometry which seems to be unnecessary --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 6 ------ src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml | 6 ++---- src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml | 6 ++---- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 2ff264baf9..45b95ce981 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -915,12 +915,6 @@ namespace Avalonia.Controls.Primitives _sizingGrid.Width = minDimension; _sizingGrid.Height = minDimension; - - if (_sizingGrid.Clip is RectangleGeometry clip) - { - clip.Rect = new Rect(0, 0, minDimension, minDimension); - } - _inputTarget.Width = minDimension; _inputTarget.Height = minDimension; _spectrumRectangle.Width = minDimension; diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index 128f7038eb..c5ea317ce3 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -20,10 +20,8 @@ - - - + VerticalAlignment="Center" + ClipToBounds="True"> - - - + VerticalAlignment="Center" + ClipToBounds="True"> Date: Mon, 28 Mar 2022 12:39:38 -0400 Subject: [PATCH 267/820] Switch from Grid to Panel which is more lightweight --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 18 +++++++++--------- .../Controls/ColorSpectrum.xaml | 16 +++++++++------- .../Controls/ColorSpectrum.xaml | 16 +++++++++------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 45b95ce981..1b04607ef0 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -22,9 +22,9 @@ namespace Avalonia.Controls.Primitives /// [TemplatePart("PART_ColorNameToolTip", typeof(ToolTip))] [TemplatePart("PART_InputTarget", typeof(Canvas))] - [TemplatePart("PART_LayoutRoot", typeof(Grid))] + [TemplatePart("PART_LayoutRoot", typeof(Panel))] [TemplatePart("PART_SelectionEllipsePanel", typeof(Panel))] - [TemplatePart("PART_SizingGrid", typeof(Grid))] + [TemplatePart("PART_SizingPanel", typeof(Panel))] [TemplatePart("PART_SpectrumEllipse", typeof(Ellipse))] [TemplatePart("PART_SpectrumRectangle", typeof(Rectangle))] [TemplatePart("PART_SpectrumOverlayEllipse", typeof(Ellipse))] @@ -52,8 +52,8 @@ namespace Avalonia.Controls.Primitives private IDisposable? _selectionEllipsePanelDisposable; // XAML template parts - private Grid? _layoutRoot; - private Grid? _sizingGrid; + private Panel? _layoutRoot; + private Panel? _sizingPanel; private Rectangle? _spectrumRectangle; private Ellipse? _spectrumEllipse; private Rectangle? _spectrumOverlayRectangle; @@ -119,9 +119,9 @@ namespace Avalonia.Controls.Primitives _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); _inputTarget = e.NameScope.Find("PART_InputTarget"); - _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); + _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); - _sizingGrid = e.NameScope.Find("PART_SizingGrid"); + _sizingPanel = e.NameScope.Find("PART_SizingPanel"); _spectrumEllipse = e.NameScope.Find("PART_SpectrumEllipse"); _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); @@ -893,7 +893,7 @@ namespace Avalonia.Controls.Primitives private async void CreateBitmapsAndColorMap() { if (_layoutRoot == null || - _sizingGrid == null || + _sizingPanel == null || _inputTarget == null || _spectrumRectangle == null || _spectrumEllipse == null || @@ -913,8 +913,8 @@ namespace Avalonia.Controls.Primitives return; } - _sizingGrid.Width = minDimension; - _sizingGrid.Height = minDimension; + _sizingPanel.Width = minDimension; + _sizingPanel.Height = minDimension; _inputTarget.Width = minDimension; _inputTarget.Height = minDimension; _spectrumRectangle.Width = minDimension; diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index c5ea317ce3..7fa703ed18 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -17,11 +17,13 @@ - - + + - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index 0b68aa3744..b138a4ad63 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -17,11 +17,13 @@ - - + + - - + + From e607f8cce8ccced935eded7450751c4973ace6ea Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:55:30 -0400 Subject: [PATCH 268/820] Watch for Bounds and FlowDirection property changes on the control itself --- .../ColorSpectrum/ColorSpectrum.cs | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 1b04607ef0..1b68c686fd 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -48,9 +48,6 @@ namespace Avalonia.Controls.Primitives private bool _shouldShowLargeSelection = false; private List _hsvValues = new List(); - private IDisposable? _layoutRootDisposable; - private IDisposable? _selectionEllipsePanelDisposable; - // XAML template parts private Panel? _layoutRoot; private Panel? _sizingPanel; @@ -136,22 +133,6 @@ namespace Avalonia.Controls.Primitives _inputTarget.PointerReleased += InputTarget_PointerReleased; } - if (_layoutRoot != null) - { - _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => - { - CreateBitmapsAndColorMap(); - }); - } - - if (_selectionEllipsePanel != null) - { - _selectionEllipsePanelDisposable = _selectionEllipsePanel.GetObservable(FlowDirectionProperty).Subscribe(_ => - { - UpdateEllipse(); - }); - } - if (ColorHelpers.ToDisplayNameExists && _colorNameToolTip != null) { @@ -187,12 +168,6 @@ namespace Avalonia.Controls.Primitives /// private void UnregisterEvents() { - _layoutRootDisposable?.Dispose(); - _layoutRootDisposable = null; - - _selectionEllipsePanelDisposable?.Dispose(); - _selectionEllipsePanelDisposable = null; - if (_inputTarget != null) { _inputTarget.PointerEnter -= InputTarget_PointerEnter; @@ -363,7 +338,11 @@ namespace Avalonia.Controls.Primitives /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - if (change.Property == ColorProperty) + if (change.Property == BoundsProperty) + { + CreateBitmapsAndColorMap(); + } + else if (change.Property == ColorProperty) { // If we're in the process of internally updating the color, // then we don't want to respond to the Color property changing. @@ -382,6 +361,10 @@ namespace Avalonia.Controls.Primitives _oldColor = change.OldValue.GetValueOrDefault(); } + else if (change.Property == FlowDirectionProperty) + { + UpdateEllipse(); + } else if (change.Property == HsvColorProperty) { // If we're in the process of internally updating the HSV color, From 680d3084aa5aaf068326137f54075f8259a4f8ee Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:58:01 -0400 Subject: [PATCH 269/820] Rename border controls in template to match convention --- src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml | 8 ++++---- src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index 7fa703ed18..1d127ec115 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -72,14 +72,14 @@ - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index b138a4ad63..c8267b57d9 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -72,14 +72,14 @@ - - - From bcc2be8d645275a41cb83c0f8d15955b56894af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Tue, 29 Mar 2022 01:09:47 +0100 Subject: [PATCH 270/820] Fixed ScrollViewer Padding. --- src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml | 2 +- src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml b/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml index b357446bfa..aab1b76259 100644 --- a/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml @@ -13,7 +13,7 @@ CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" Content="{TemplateBinding Content}" Extent="{TemplateBinding Extent, Mode=TwoWay}" - Margin="{TemplateBinding Padding}" + Padding="{TemplateBinding Padding}" Offset="{TemplateBinding Offset, Mode=TwoWay}" Viewport="{TemplateBinding Viewport, Mode=TwoWay}" IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}"> diff --git a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml index 53a1b721d1..b7addcb61e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml @@ -32,7 +32,7 @@ CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" Content="{TemplateBinding Content}" Extent="{TemplateBinding Extent, Mode=TwoWay}" - Margin="{TemplateBinding Padding}" + Padding="{TemplateBinding Padding}" Offset="{TemplateBinding Offset, Mode=TwoWay}" Viewport="{TemplateBinding Viewport, Mode=TwoWay}" IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}"> From 9250d9340723554b5a05726f2dffc003c26888f3 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 29 Mar 2022 09:44:25 +0200 Subject: [PATCH 271/820] Implement embedded runs Improve text edit navigation Implement IncrementalTabWidth --- samples/ControlCatalog/Pages/PointersPage.cs | 3 +- samples/RenderDemo/MainWindow.xaml | 3 + .../RenderDemo/Pages/TextFormatterPage.axaml | 7 + .../Pages/TextFormatterPage.axaml.cs | 118 +++ samples/RenderDemo/RenderDemo.csproj | 4 + .../HamburgerMenu/HamburgerMenu.xaml | 18 +- .../DataGridTextColumn.cs | 3 +- .../Themes/Default.xaml | 2 +- .../Themes/Fluent.xaml | 2 +- src/Avalonia.Controls/ApiCompatBaseline.txt | 22 +- src/Avalonia.Controls/Control.cs | 114 ++- .../Documents/TextElement.cs | 167 +++- .../Presenters/TextPresenter.cs | 89 +- .../Primitives/TemplatedControl.cs | 75 -- src/Avalonia.Controls/TextBlock.cs | 240 +---- src/Avalonia.Controls/TextBox.cs | 109 ++- src/Avalonia.Controls/Window.cs | 1 - .../Diagnostics/Views/ConsoleView.xaml | 4 +- .../Views/LayoutExplorerView.axaml | 2 +- .../HeadlessPlatformStubs.cs | 7 +- .../Controls/Button.xaml | 4 +- .../Controls/CaptionButtons.xaml | 4 +- .../Controls/CheckBox.xaml | 4 +- .../Controls/DatePicker.xaml | 22 +- .../Controls/RepeatButton.xaml | 4 +- .../Controls/SplitButton.xaml | 46 +- .../Controls/TimePicker.xaml | 16 +- .../Controls/ToggleButton.xaml | 2 +- .../Controls/Button.xaml | 16 +- .../Controls/CalendarItem.xaml | 6 +- .../Controls/CaptionButtons.xaml | 2 +- .../Controls/CheckBox.xaml | 24 +- .../Controls/ComboBox.xaml | 28 +- .../Controls/ComboBoxItem.xaml | 18 +- .../Controls/DataValidationErrors.xaml | 2 +- .../Controls/DatePicker.xaml | 22 +- .../Controls/Expander.xaml | 2 +- .../Controls/ListBox.xaml | 2 +- .../Controls/ListBoxItem.xaml | 14 +- .../Controls/MenuItem.xaml | 16 +- .../Controls/RadioButton.xaml | 10 +- .../Controls/RepeatButton.xaml | 8 +- .../Controls/Slider.xaml | 12 +- .../Controls/SplitButton.xaml | 46 +- .../Controls/TabItem.xaml | 20 +- .../Controls/TabStripItem.xaml | 20 +- .../Controls/TimePicker.xaml | 16 +- .../Controls/ToggleButton.xaml | 24 +- .../Controls/TreeViewItem.xaml | 16 +- .../Controls/WindowNotificationManager.xaml | 2 +- src/Avalonia.Visuals/ApiCompatBaseline.txt | 12 +- src/Avalonia.Visuals/Media/FormattedText.cs | 4 +- .../Media/TextDecorationCollection.cs | 10 + .../Media/TextFormatting/DrawableTextRun.cs | 5 + .../TextFormatting/FormattedTextSource.cs | 2 +- .../TextFormatting/ShapedTextCharacters.cs | 2 + .../Media/TextFormatting/TextBounds.cs | 29 + .../Media/TextFormatting/TextCharacters.cs | 25 +- .../TextCollapsingProperties.cs | 2 +- .../TextFormatting/TextEllipsisHelper.cs | 112 ++- .../Media/TextFormatting/TextFormatterImpl.cs | 243 +++-- .../Media/TextFormatting/TextLayout.cs | 175 +--- .../TextLeadingPrefixCharacterEllipsis.cs | 127 +-- .../Media/TextFormatting/TextLine.cs | 8 + .../Media/TextFormatting/TextLineBreak.cs | 8 +- .../Media/TextFormatting/TextLineImpl.cs | 896 +++++++++++++----- .../TextFormatting/TextParagraphProperties.cs | 8 + .../Media/TextFormatting/TextRunProperties.cs | 2 +- .../Media/TextFormatting/TextShaper.cs | 5 +- .../Media/TextFormatting/TextShaperOptions.cs | 49 + .../TextTrailingCharacterEllipsis.cs | 8 +- .../TextTrailingWordEllipsis.cs | 8 +- .../Platform/ITextShaperImpl.cs | 13 +- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 19 +- .../Media/TextShaperImpl.cs | 17 +- .../TextBoxTests.cs | 29 + .../Xaml/BasicTests.cs | 9 +- .../Media/GlyphRunTests.cs | 9 +- .../TextFormatting/MultiBufferTextSource.cs | 7 +- .../TextFormatting/SingleBufferTextSource.cs | 7 +- .../TextFormatting/TextFormatterTests.cs | 94 +- .../Media/TextFormatting/TextLineTests.cs | 197 +++- .../Media/TextFormatting/TextShaperTests.cs | 24 +- .../HarfBuzzTextShaperImpl.cs | 9 +- .../Avalonia.UnitTests/MockTextShaperImpl.cs | 11 +- 85 files changed, 2332 insertions(+), 1271 deletions(-) create mode 100644 samples/RenderDemo/Pages/TextFormatterPage.axaml create mode 100644 samples/RenderDemo/Pages/TextFormatterPage.axaml.cs create mode 100644 src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs create mode 100644 src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs diff --git a/samples/ControlCatalog/Pages/PointersPage.cs b/samples/ControlCatalog/Pages/PointersPage.cs index 2901013cea..0377993d2c 100644 --- a/samples/ControlCatalog/Pages/PointersPage.cs +++ b/samples/ControlCatalog/Pages/PointersPage.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Documents; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; @@ -131,7 +132,7 @@ public class PointersPage : Decorator { public PointerIntermediatePointsTab() { - this[TextBlock.ForegroundProperty] = Brushes.Black; + this[TextElement.ForegroundProperty] = Brushes.Black; var slider = new Slider { Margin = new Thickness(5), diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index 923b51814f..429c4776d5 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -60,6 +60,9 @@ + + + diff --git a/samples/RenderDemo/Pages/TextFormatterPage.axaml b/samples/RenderDemo/Pages/TextFormatterPage.axaml new file mode 100644 index 0000000000..4edf0852a2 --- /dev/null +++ b/samples/RenderDemo/Pages/TextFormatterPage.axaml @@ -0,0 +1,7 @@ + + diff --git a/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs b/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs new file mode 100644 index 0000000000..92eb2e7dec --- /dev/null +++ b/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs @@ -0,0 +1,118 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; + +namespace RenderDemo.Pages +{ + public class TextFormatterPage : UserControl + { + private TextLine _textLine; + + public TextFormatterPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public override void Render(DrawingContext context) + { + _textLine?.Draw(context, new Point()); + } + + protected override Size MeasureOverride(Size availableSize) + { + var defaultRunProperties = new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black, + baselineAlignment: BaselineAlignment.Center); + var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties); + + var control = new Button { Content = new TextBlock { Text = "ClickMe" } }; + + Content = control; + + var textSource = new CustomTextSource(control, defaultRunProperties); + + control.Measure(Size.Infinity); + + _textLine = + TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); + + return base.MeasureOverride(availableSize); + } + + protected override Size ArrangeOverride(Size finalSize) + { + var currentX = 0d; + + foreach (var textRun in _textLine.TextRuns) + { + if (textRun is ControlRun controlRun) + { + controlRun.Control.Arrange(new Rect(new Point(currentX, 0), controlRun.Size)); + } + + if (textRun is DrawableTextRun drawableTextRun) + { + currentX += drawableTextRun.Size.Width; + } + } + + return finalSize; + } + + private class CustomTextSource : ITextSource + { + private readonly Control _control; + private readonly TextRunProperties _defaultProperties; + private readonly string _text = "<-Hello World->"; + + public CustomTextSource(Control control, TextRunProperties defaultProperties) + { + _control = control; + _defaultProperties = defaultProperties; + } + + public TextRun? GetTextRun(int textSourceIndex) + { + if (textSourceIndex >= _text.Length * 2 + TextRun.DefaultTextSourceLength) + { + return null; + } + + if (textSourceIndex == _text.Length) + { + return new ControlRun(_control, _defaultProperties); + } + + return new TextCharacters(_text.AsMemory(), _defaultProperties); + } + } + + private class ControlRun : DrawableTextRun + { + private readonly Control _control; + + public ControlRun(Control control, TextRunProperties properties) + { + _control = control; + Properties = properties; + } + + public Control Control => _control; + public override Size Size => _control.DesiredSize; + public override double Baseline => 0; + public override TextRunProperties? Properties { get; } + + public override void Draw(DrawingContext drawingContext, Point origin) + { + // noop + } + } + } +} diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index 54d5ca4b3b..3d5aee49e9 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -5,6 +5,10 @@ + + TextFormatterPage.axaml + Code + diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml index e0dfa49a44..1d58c465a0 100644 --- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml +++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml @@ -195,9 +195,9 @@ VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" - TextBlock.FontFamily="{TemplateBinding FontFamily}" - TextBlock.FontSize="{TemplateBinding FontSize}" - TextBlock.FontWeight="{TemplateBinding FontWeight}" /> + TextElement.FontFamily="{TemplateBinding FontFamily}" + TextElement.FontSize="{TemplateBinding FontSize}" + TextElement.FontWeight="{TemplateBinding FontWeight}" /> @@ -216,25 +216,25 @@ Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CornerRadius="{TemplateBinding CornerRadius}" - TextBlock.FontFamily="{TemplateBinding FontFamily}" - TextBlock.FontSize="{TemplateBinding FontSize}" - TextBlock.FontWeight="{TemplateBinding FontWeight}" /> + TextElement.FontFamily="{TemplateBinding FontFamily}" + TextElement.FontSize="{TemplateBinding FontSize}" + TextElement.FontWeight="{TemplateBinding FontWeight}" /> @@ -112,7 +112,7 @@ diff --git a/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml index a9a03c8ed5..b3b5fe7859 100644 --- a/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml @@ -6,7 +6,7 @@ Value="{DynamicResource ThemeBorderLowBrush}" /> - @@ -24,7 +24,7 @@ ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" - TextBlock.Foreground="{TemplateBinding Foreground}" + Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/Controls/SplitButton.xaml b/src/Avalonia.Themes.Default/Controls/SplitButton.xaml index ce20a1a165..9af1c073cd 100644 --- a/src/Avalonia.Themes.Default/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/SplitButton.xaml @@ -56,7 +56,7 @@ @@ -157,10 +157,10 @@ SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter"> - + @@ -169,10 +169,10 @@ SplitButton:pressed /template/ Border#SeparatorBorder"> - + @@ -181,10 +181,10 @@ SplitButton:flyout-open /template/ Border#SeparatorBorder"> - + @@ -193,10 +193,10 @@ SplitButton:disabled /template/ Border#SeparatorBorder"> - + @@ -205,10 +205,10 @@ SplitButton:checked /template/ Border#SeparatorBorder"> - + @@ -216,10 +216,10 @@ SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter"> - + @@ -227,10 +227,10 @@ SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter"> - + @@ -239,10 +239,10 @@ SplitButton:pressed:checked /template/ Border#SeparatorBorder"> - + @@ -251,10 +251,10 @@ SplitButton:checked:flyout-open /template/ Border#SeparatorBorder"> - + @@ -263,9 +263,9 @@ SplitButton:checked:disabled /template/ Border#SeparatorBorder"> - + diff --git a/src/Avalonia.Themes.Default/Controls/TimePicker.xaml b/src/Avalonia.Themes.Default/Controls/TimePicker.xaml index a58fd62a99..5a6eb15f2d 100644 --- a/src/Avalonia.Themes.Default/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Default/Controls/TimePicker.xaml @@ -33,7 +33,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml index a9c8281cf0..0d1dd03c6e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml @@ -61,17 +61,17 @@ @@ -177,19 +177,19 @@ @@ -199,28 +199,28 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml index a958d785fa..80c1103ece 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml @@ -18,14 +18,14 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index 12e148d2f9..05f5decbcf 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -29,7 +29,7 @@ @@ -125,7 +125,7 @@ @@ -49,7 +49,7 @@ @@ -57,7 +57,7 @@ @@ -65,7 +65,7 @@ @@ -73,7 +73,7 @@ @@ -81,7 +81,7 @@ @@ -89,6 +89,6 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 831537f578..efc6205ab1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -53,7 +53,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml index 5ee2dc527f..81aa5008cc 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml @@ -38,14 +38,14 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml index e0dd0180b3..8840397843 100644 --- a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml @@ -32,7 +32,7 @@ @@ -133,10 +133,10 @@ SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter"> - + @@ -145,10 +145,10 @@ SplitButton:pressed /template/ Border#SeparatorBorder"> - + @@ -157,10 +157,10 @@ SplitButton:flyout-open /template/ Border#SeparatorBorder"> - + @@ -169,10 +169,10 @@ SplitButton:disabled /template/ Border#SeparatorBorder"> - + @@ -181,10 +181,10 @@ SplitButton:checked /template/ Border#SeparatorBorder"> - + @@ -192,10 +192,10 @@ SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter"> - + @@ -203,10 +203,10 @@ SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter"> - + @@ -215,10 +215,10 @@ SplitButton:pressed:checked /template/ Border#SeparatorBorder"> - + @@ -227,10 +227,10 @@ SplitButton:checked:flyout-open /template/ Border#SeparatorBorder"> - + @@ -239,9 +239,9 @@ SplitButton:checked:disabled /template/ Border#SeparatorBorder"> - + diff --git a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml index 83411b425b..20039fd71b 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml @@ -19,7 +19,7 @@ - + @@ -37,9 +37,9 @@ Content="{TemplateBinding Header}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" - TextBlock.FontFamily="{TemplateBinding FontFamily}" - TextBlock.FontSize="{TemplateBinding FontSize}" - TextBlock.FontWeight="{TemplateBinding FontWeight}" /> + TextElement.FontFamily="{TemplateBinding FontFamily}" + TextElement.FontSize="{TemplateBinding FontSize}" + TextElement.FontWeight="{TemplateBinding FontWeight}" /> @@ -61,7 +61,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml index 694e9ef579..08fa944107 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml @@ -18,7 +18,7 @@ - + @@ -36,9 +36,9 @@ Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" - TextBlock.FontFamily="{TemplateBinding FontFamily}" - TextBlock.FontSize="{TemplateBinding FontSize}" - TextBlock.FontWeight="{TemplateBinding FontWeight}" /> + TextElement.FontFamily="{TemplateBinding FontFamily}" + TextElement.FontSize="{TemplateBinding FontSize}" + TextElement.FontWeight="{TemplateBinding FontWeight}" /> @@ -69,7 +69,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml index 9aa73fc52e..e39d6c4a73 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml @@ -34,7 +34,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml index e66392113f..4cabe1e9d3 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml @@ -58,7 +58,7 @@ - + @@ -107,7 +107,7 @@ @@ -116,7 +116,7 @@ @@ -125,7 +125,7 @@ @@ -134,7 +134,7 @@ @@ -143,7 +143,7 @@ @@ -152,7 +152,7 @@ @@ -161,7 +161,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml index 2ff5284d0d..8d14c2d972 100644 --- a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml @@ -9,7 +9,7 @@ - + diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index b725993b44..c0905a0929 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -67,6 +67,8 @@ MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialG 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. +CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline' is abstract in the implementation but is missing in the contract. +CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline.get()' is abstract in the implementation but is missing 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. @@ -85,7 +87,7 @@ MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextC MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract. +CannotAddAbstractMembers : Member 'public System.Collections.Generic.List Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract. @@ -108,6 +110,7 @@ CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextForma MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent.get()' is abstract in the implementation but is missing in the contract. +CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextLine.GetTextBounds(System.Int32, System.Int32)' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed.get()' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height.get()' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLine.LineMetrics.get()' does not exist in the implementation but it does exist in the contract. @@ -120,6 +123,7 @@ CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormat CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width.get()' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace.get()' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineBreak..ctor(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextLineBreak.RemainingCharacters.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineMetrics..ctor(Avalonia.Size, System.Double, Avalonia.Media.TextFormatting.TextRange, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLineMetrics.Create(System.Collections.Generic.IEnumerable, Avalonia.Media.TextFormatting.TextRange, System.Double, Avalonia.Media.TextFormatting.TextParagraphProperties)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLineMetrics.Size.get()' does not exist in the implementation but it does exist in the contract. @@ -130,8 +134,6 @@ CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextForma CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph.get()' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection.get()' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment.get()' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Media.TextFormatting.TextShaper.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. CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. @@ -171,9 +173,9 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avaloni InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.GlyphTypeface, System.Double, System.Globalization.CultureInfo, System.SByte)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.TextFormatting.TextShaperOptions)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' is present in the contract but not in the implementation. 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: 177 +Total Issues: 179 diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs index 1cac3243e3..ed17824493 100644 --- a/src/Avalonia.Visuals/Media/FormattedText.cs +++ b/src/Avalonia.Visuals/Media/FormattedText.cs @@ -56,7 +56,7 @@ namespace Avalonia.Media FlowDirection flowDirection, Typeface typeface, double emSize, - IBrush foreground) + IBrush? foreground) { if (culture is null) { @@ -160,7 +160,7 @@ namespace Avalonia.Media /// Foreground brush /// The start index of initial character to apply the change to. /// The number of characters the change should be applied to. - public void SetForegroundBrush(IBrush foregroundBrush, int startIndex, int count) + public void SetForegroundBrush(IBrush? foregroundBrush, int startIndex, int count) { var limit = ValidateRange(startIndex, count); for (var i = startIndex; i < limit;) diff --git a/src/Avalonia.Visuals/Media/TextDecorationCollection.cs b/src/Avalonia.Visuals/Media/TextDecorationCollection.cs index 2dced2252e..2d7bd17b20 100644 --- a/src/Avalonia.Visuals/Media/TextDecorationCollection.cs +++ b/src/Avalonia.Visuals/Media/TextDecorationCollection.cs @@ -10,6 +10,16 @@ namespace Avalonia.Media /// public class TextDecorationCollection : AvaloniaList { + public TextDecorationCollection() + { + + } + + public TextDecorationCollection(IEnumerable textDecorations) : base(textDecorations) + { + + } + /// /// Parses a string. /// diff --git a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs b/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs index 3757a4506a..e63ed421ba 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs @@ -9,6 +9,11 @@ /// Gets the size. /// public abstract Size Size { get; } + + /// + /// Run baseline in ratio relative to run height + /// + public abstract double Baseline { get; } /// /// Draws the at the given origin. diff --git a/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs index 1b0feaa718..9940f2f3f8 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs @@ -30,7 +30,7 @@ namespace Avalonia.Media.TextFormatting if (runText.IsEmpty) { - return new TextEndOfParagraph(); + return null; } var textStyleRun = CreateTextStyleRun(runText, _defaultProperties, _textModifier); diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs index fb85766003..53287a264d 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs @@ -38,6 +38,8 @@ namespace Avalonia.Media.TextFormatting public FontMetrics FontMetrics { get; } + public override double Baseline => -FontMetrics.Ascent; + public override Size Size => GlyphRun.Size; public GlyphRun GlyphRun diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs new file mode 100644 index 0000000000..a0b51671f0 --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace Avalonia.Media.TextFormatting +{ + /// + /// The bounding rectangle of a range of characters + /// + public sealed class TextBounds + { + /// + /// Constructing TextBounds object + /// + internal TextBounds(Rect bounds, FlowDirection flowDirection) + { + Rectangle = bounds; + FlowDirection = flowDirection; + } + + /// + /// Bounds rectangle + /// + public Rect Rectangle { get; } + + /// + /// Text flow direction inside the boundary rectangle + /// + public FlowDirection FlowDirection { get; } + } +} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs index c4b2dfb3a5..ab72601c3e 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs @@ -38,7 +38,7 @@ namespace Avalonia.Media.TextFormatting /// Gets a list of . /// /// The shapeable text characters. - internal IList GetShapeableCharacters(ReadOnlySlice runText, sbyte biDiLevel, + internal IReadOnlyList GetShapeableCharacters(ReadOnlySlice runText, sbyte biDiLevel, ref TextRunProperties? previousProperties) { var shapeableCharacters = new List(2); @@ -72,11 +72,11 @@ namespace Avalonia.Media.TextFormatting var currentTypeface = defaultTypeface; var previousTypeface = previousProperties?.Typeface; - if (TryGetShapeableLength(text, currentTypeface, out var count, out var script)) + if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script)) { if (script == Script.Common && previousTypeface is not null) { - if(TryGetShapeableLength(text, previousTypeface.Value, out var fallbackCount, out _)) + if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _)) { return new ShapeableTextCharacters(text.Take(fallbackCount), defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); @@ -89,7 +89,7 @@ namespace Avalonia.Media.TextFormatting if (previousTypeface is not null) { - if(TryGetShapeableLength(text, previousTypeface.Value, out count, out _)) + if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _)) { return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); @@ -118,7 +118,7 @@ namespace Avalonia.Media.TextFormatting defaultTypeface.Stretch, defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface); - if (matchFound && TryGetShapeableLength(text, currentTypeface, out count, out _)) + if (matchFound && TryGetShapeableLength(text, currentTypeface, defaultTypeface, out count, out _)) { //Fallback found return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), @@ -152,14 +152,19 @@ namespace Avalonia.Media.TextFormatting /// /// The text. /// The typeface that is used to find matching characters. + /// /// The shapeable length. /// /// - protected static bool TryGetShapeableLength(ReadOnlySlice text, Typeface typeface, out int length, + protected static bool TryGetShapeableLength( + ReadOnlySlice text, + Typeface typeface, + Typeface? defaultTypeface, + out int length, out Script script) { length = 0; - script = Script.Unknown; + script = Script.Unknown; if (text.Length == 0) { @@ -167,6 +172,7 @@ namespace Avalonia.Media.TextFormatting } var font = typeface.GlyphTypeface; + var defaultFont = defaultTypeface?.GlyphTypeface; var enumerator = new GraphemeEnumerator(text); @@ -176,6 +182,11 @@ namespace Avalonia.Media.TextFormatting var currentScript = currentGrapheme.FirstCodepoint.Script; + if (currentScript != Script.Common && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) + { + break; + } + //Stop at the first missing glyph if (!currentGrapheme.FirstCodepoint.IsBreakChar && !font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs index a46f9537d0..f677617b14 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs @@ -21,6 +21,6 @@ namespace Avalonia.Media.TextFormatting /// Collapses given text line. /// /// Text line to collapse. - public abstract IReadOnlyList? Collapse(TextLine textLine); + public abstract List? Collapse(TextLine textLine); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs index 2031c2ec99..5d0bb442d7 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs @@ -3,13 +3,11 @@ using Avalonia.Media.TextFormatting.Unicode; namespace Avalonia.Media.TextFormatting { - internal class TextEllipsisHelper + internal static class TextEllipsisHelper { - public static List? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis) + public static List? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis) { - var shapedTextRuns = textLine.TextRuns as List; - - if (shapedTextRuns is null) + if (textLine.TextRuns is not List textRuns || textRuns.Count == 0) { return null; } @@ -22,69 +20,103 @@ namespace Avalonia.Media.TextFormatting if (properties.Width < shapedSymbol.GlyphRun.Size.Width) { - return new List(0); + //Not enough space to fit in the symbol + return new List(0); } var availableWidth = properties.Width - shapedSymbol.Size.Width; - while (runIndex < shapedTextRuns.Count) + while (runIndex < textRuns.Count) { - var currentRun = shapedTextRuns[runIndex]; - - currentWidth += currentRun.Size.Width; + var currentRun = textRuns[runIndex]; - if (currentWidth > availableWidth) + switch (currentRun) { - if (currentRun.TryMeasureCharacters(availableWidth, out var measuredLength)) + case ShapedTextCharacters shapedRun: { - if (isWordEllipsis && measuredLength < textRange.End) + currentWidth += shapedRun.Size.Width; + + if (currentWidth > availableWidth) { - var currentBreakPosition = 0; + if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength)) + { + if (isWordEllipsis && measuredLength < textRange.End) + { + var currentBreakPosition = 0; - var lineBreaker = new LineBreakEnumerator(currentRun.Text); + var lineBreaker = new LineBreakEnumerator(currentRun.Text); - while (currentBreakPosition < measuredLength && lineBreaker.MoveNext()) - { - var nextBreakPosition = lineBreaker.Current.PositionMeasure; + while (currentBreakPosition < measuredLength && lineBreaker.MoveNext()) + { + var nextBreakPosition = lineBreaker.Current.PositionMeasure; - if (nextBreakPosition == 0) - { - break; - } + if (nextBreakPosition == 0) + { + break; + } - if (nextBreakPosition >= measuredLength) - { - break; + if (nextBreakPosition >= measuredLength) + { + break; + } + + currentBreakPosition = nextBreakPosition; + } + + measuredLength = currentBreakPosition; } + } + + collapsedLength += measuredLength; + + var collapsedRuns = new List(textRuns.Count); - currentBreakPosition = nextBreakPosition; + if (collapsedLength > 0) + { + var splitResult = TextFormatterImpl.SplitDrawableRuns(textRuns, collapsedLength); + + collapsedRuns.AddRange(splitResult.First); + + TextLineImpl.SortRuns(collapsedRuns); } - measuredLength = currentBreakPosition; + collapsedRuns.Add(shapedSymbol); + + return collapsedRuns; } - } - collapsedLength += measuredLength; + availableWidth -= currentRun.Size.Width; - var shapedTextCharacters = new List(shapedTextRuns.Count); + + break; + } - if (collapsedLength > 0) + case { } drawableRun: { - var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, collapsedLength); + //The whole run needs to fit into available space + if (currentWidth + drawableRun.Size.Width > availableWidth) + { + var collapsedRuns = new List(textRuns.Count); - shapedTextCharacters.AddRange(splitResult.First); + if (collapsedLength > 0) + { + var splitResult = TextFormatterImpl.SplitDrawableRuns(textRuns, collapsedLength); - TextLineImpl.SortRuns(shapedTextCharacters); - } + collapsedRuns.AddRange(splitResult.First); - shapedTextCharacters.Add(shapedSymbol); + TextLineImpl.SortRuns(collapsedRuns); + } - return shapedTextCharacters; - } + collapsedRuns.Add(shapedSymbol); - availableWidth -= currentRun.Size.Width; + return collapsedRuns; + } + + break; + } + } - collapsedLength += currentRun.GlyphRun.Characters.Length; + collapsedLength += currentRun.TextSourceLength; runIndex++; } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs index 13ed850715..fddd7c0160 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Utilities; @@ -17,20 +15,20 @@ namespace Avalonia.Media.TextFormatting var textWrapping = paragraphProperties.TextWrapping; FlowDirection flowDirection; TextLineBreak? nextLineBreak = null; - List shapedRuns; + List drawableTextRuns; var textRuns = FetchTextRuns(textSource, firstTextSourceIndex, out var textEndOfLine, out var textRange); - if (previousLineBreak?.RemainingCharacters != null) + if (previousLineBreak?.RemainingRuns != null) { flowDirection = previousLineBreak.FlowDirection; - shapedRuns = previousLineBreak.RemainingCharacters.ToList(); + drawableTextRuns = previousLineBreak.RemainingRuns.ToList(); nextLineBreak = previousLineBreak; } else { - shapedRuns = ShapeTextRuns(textRuns, paragraphProperties.FlowDirection,out flowDirection); + drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out flowDirection); if(nextLineBreak == null && textEndOfLine != null) { @@ -44,9 +42,9 @@ namespace Avalonia.Media.TextFormatting { case TextWrapping.NoWrap: { - TextLineImpl.SortRuns(shapedRuns); + TextLineImpl.SortRuns(drawableTextRuns); - textLine = new TextLineImpl(shapedRuns, textRange, paragraphWidth, paragraphProperties, + textLine = new TextLineImpl(drawableTextRuns, textRange, paragraphWidth, paragraphProperties, flowDirection, nextLineBreak); textLine.FinalizeLine(); @@ -56,7 +54,7 @@ namespace Avalonia.Media.TextFormatting case TextWrapping.WrapWithOverflow: case TextWrapping.Wrap: { - textLine = PerformTextWrapping(shapedRuns, textRange, paragraphWidth, paragraphProperties, + textLine = PerformTextWrapping(drawableTextRuns, textRange, paragraphWidth, paragraphProperties, flowDirection, nextLineBreak); break; } @@ -73,7 +71,7 @@ namespace Avalonia.Media.TextFormatting /// The text run's. /// The length to split at. /// The split text runs. - internal static SplitResult> SplitShapedRuns(List textRuns, int length) + internal static SplitResult> SplitDrawableRuns(List textRuns, int length) { var currentLength = 0; @@ -83,13 +81,14 @@ namespace Avalonia.Media.TextFormatting if (currentLength + currentRun.Text.Length < length) { - currentLength += currentRun.Text.Length; + currentLength += currentRun.TextSourceLength; + continue; } var firstCount = currentRun.Text.Length >= 1 ? i + 1 : i; - var first = new List(firstCount); + var first = new List(firstCount); if (firstCount > 1) { @@ -103,7 +102,7 @@ namespace Avalonia.Media.TextFormatting if (currentLength + currentRun.Text.Length == length) { - var second = secondCount > 0 ? new List(secondCount) : null; + var second = secondCount > 0 ? new List(secondCount) : null; if (second != null) { @@ -117,15 +116,20 @@ namespace Avalonia.Media.TextFormatting first.Add(currentRun); - return new SplitResult>(first, second); + return new SplitResult>(first, second); } else { secondCount++; - var second = new List(secondCount); + var second = new List(secondCount); - var split = currentRun.Split(length - currentLength); + if (currentRun is not ShapedTextCharacters shapedTextCharacters) + { + throw new NotSupportedException("Only shaped runs can be split in between."); + } + + var split = shapedTextCharacters.Split(length - currentLength); first.Add(split.First); @@ -136,32 +140,43 @@ namespace Avalonia.Media.TextFormatting second.Add(textRuns[i + j]); } - return new SplitResult>(first, second); + return new SplitResult>(first, second); } } - return new SplitResult>(textRuns, null); + return new SplitResult>(textRuns, null); } /// /// Shape specified text runs with specified paragraph embedding. /// /// The text runs to shape. - /// The paragraph embedding level. + /// The default paragraph properties. /// The resolved flow direction. /// /// A list of shaped text characters. /// - private static List ShapeTextRuns(List textRuns, - FlowDirection flowDirection, out FlowDirection resolvedFlowDirection) + private static List ShapeTextRuns(List textRuns, TextParagraphProperties paragraphProperties, + out FlowDirection resolvedFlowDirection) { - var shapedTextCharacters = new List(); + var flowDirection = paragraphProperties.FlowDirection; + var drawableTextRuns = new List(); var biDiData = new BidiData((sbyte)flowDirection); foreach (var textRun in textRuns) { - biDiData.Append(textRun.Text); + if (textRun.Text.IsEmpty) + { + var text = new char[textRun.TextSourceLength]; + + biDiData.Append(text); + } + else + { + biDiData.Append(textRun.Text); + } + } var biDi = BidiAlgorithm.Instance.Value!; @@ -173,68 +188,90 @@ namespace Avalonia.Media.TextFormatting resolvedFlowDirection = (resolvedEmbeddingLevel & 1) == 0 ? FlowDirection.LeftToRight : FlowDirection.RightToLeft; - var shapeableRuns = new List(textRuns.Count); + var processedRuns = new List(textRuns.Count); foreach (var coalescedRuns in CoalesceLevels(textRuns, biDi.ResolvedLevels)) { - shapeableRuns.AddRange(coalescedRuns); + processedRuns.AddRange(coalescedRuns); } - for (var index = 0; index < shapeableRuns.Count; index++) + for (var index = 0; index < processedRuns.Count; index++) { - var currentRun = shapeableRuns[index]; - var groupedRuns = new List(2) { currentRun }; - var text = currentRun.Text; - var start = currentRun.Text.Start; - var length = currentRun.Text.Length; - var bufferOffset = currentRun.Text.BufferOffset; - - while (index + 1 < shapeableRuns.Count) + var currentRun = processedRuns[index]; + + switch (currentRun) { - var nextRun = shapeableRuns[index + 1]; + case DrawableTextRun drawableRun: + { + drawableTextRuns.Add(drawableRun); - if (currentRun.CanShapeTogether(nextRun)) + break; + } + + case ShapeableTextCharacters shapeableRun: { - groupedRuns.Add(nextRun); + var groupedRuns = new List(2) { shapeableRun }; + var text = currentRun.Text; + var start = currentRun.Text.Start; + var length = currentRun.Text.Length; + var bufferOffset = currentRun.Text.BufferOffset; - length += nextRun.Text.Length; - - if (start > nextRun.Text.Start) + while (index + 1 < processedRuns.Count) { - start = nextRun.Text.Start; - } + if (processedRuns[index + 1] is not ShapeableTextCharacters nextRun) + { + break; + } - if (bufferOffset > nextRun.Text.BufferOffset) - { - bufferOffset = nextRun.Text.BufferOffset; + if (shapeableRun.CanShapeTogether(nextRun)) + { + groupedRuns.Add(nextRun); + + length += nextRun.Text.Length; + + if (start > nextRun.Text.Start) + { + start = nextRun.Text.Start; + } + + if (bufferOffset > nextRun.Text.BufferOffset) + { + bufferOffset = nextRun.Text.BufferOffset; + } + + text = new ReadOnlySlice(text.Buffer, start, length, bufferOffset); + + index++; + + shapeableRun = nextRun; + + continue; + } + + break; } - text = new ReadOnlySlice(text.Buffer, start, length, bufferOffset); - - index++; + var shaperOptions = new TextShaperOptions(currentRun.Properties!.Typeface.GlyphTypeface, + currentRun.Properties.FontRenderingEmSize, + shapeableRun.BidiLevel, currentRun.Properties.CultureInfo, paragraphProperties.DefaultIncrementalTab); - currentRun = nextRun; + drawableTextRuns.AddRange(ShapeTogether(groupedRuns, text, shaperOptions)); - continue; + break; } - - break; } - - shapedTextCharacters.AddRange(ShapeTogether(groupedRuns, text)); } - return shapedTextCharacters; + return drawableTextRuns; } private static IReadOnlyList ShapeTogether( - IReadOnlyList textRuns, ReadOnlySlice text) + IReadOnlyList textRuns, ReadOnlySlice text, TextShaperOptions options) { var shapedRuns = new List(textRuns.Count); var firstRun = textRuns[0]; - var shapedBuffer = TextShaper.Current.ShapeText(text, firstRun.Properties.Typeface.GlyphTypeface, - firstRun.Properties.FontRenderingEmSize, firstRun.Properties.CultureInfo, firstRun.BidiLevel); + var shapedBuffer = TextShaper.Current.ShapeText(text, options); for (var i = 0; i < textRuns.Count; i++) { @@ -256,8 +293,8 @@ namespace Avalonia.Media.TextFormatting /// The text characters to form from. /// The bidi levels. /// - private static IEnumerable> CoalesceLevels( - IReadOnlyList textCharacters, + private static IEnumerable> CoalesceLevels( + IReadOnlyList textCharacters, ReadOnlySlice levels) { if (levels.Length == 0) @@ -275,7 +312,19 @@ namespace Avalonia.Media.TextFormatting for (var i = 0; i < textCharacters.Count; i++) { var j = 0; - currentRun = textCharacters[i]; + currentRun = textCharacters[i] as TextCharacters; + + if (currentRun == null) + { + var drawableRun = textCharacters[i]; + + yield return new[] { drawableRun }; + + levelIndex += drawableRun.TextSourceLength; + + continue; + } + runText = currentRun.Text; for (; j < runText.Length;) @@ -334,14 +383,14 @@ namespace Avalonia.Media.TextFormatting /// /// The formatted text runs. /// - private static List FetchTextRuns(ITextSource textSource, int firstTextSourceIndex, + private static List FetchTextRuns(ITextSource textSource, int firstTextSourceIndex, out TextEndOfLine? endOfLine, out TextRange textRange) { var length = 0; endOfLine = null; - var textRuns = new List(); + var textRuns = new List(); var textRunEnumerator = new TextRunEnumerator(textSource, firstTextSourceIndex); @@ -354,6 +403,17 @@ namespace Avalonia.Media.TextFormatting break; } + if (textRun is TextEndOfLine textEndOfLine) + { + endOfLine = textEndOfLine; + + textRuns.Add(textRun); + + length += textRun.TextSourceLength; + + break; + } + switch (textRun) { case TextCharacters textCharacters: @@ -376,12 +436,14 @@ namespace Avalonia.Media.TextFormatting break; } - case TextEndOfLine textEndOfLine: - endOfLine = textEndOfLine; + case DrawableTextRun drawableTextRun: + { + textRuns.Add(drawableTextRun); break; + } } - length += textRun.Text.Length; + length += textRun.TextSourceLength; } textRange = new TextRange(firstTextSourceIndex, length); @@ -415,7 +477,7 @@ namespace Avalonia.Media.TextFormatting return false; } - private static int MeasureLength(IReadOnlyList textRuns, TextRange textRange, + private static int MeasureLength(IReadOnlyList textRuns, TextRange textRange, double paragraphWidth) { var currentWidth = 0.0; @@ -423,19 +485,42 @@ namespace Avalonia.Media.TextFormatting foreach (var currentRun in textRuns) { - for (var i = 0; i < currentRun.ShapedBuffer.Length; i++) + switch (currentRun) { - var glyphInfo = currentRun.ShapedBuffer[i]; - - if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth) + case ShapedTextCharacters shapedTextCharacters: { - var measuredLength = lastCluster - textRange.Start; + for (var i = 0; i < shapedTextCharacters.ShapedBuffer.Length; i++) + { + var glyphInfo = shapedTextCharacters.ShapedBuffer[i]; - return measuredLength == 0 ? 1 : measuredLength; + if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth) + { + var measuredLength = lastCluster - textRange.Start; + + return measuredLength == 0 ? 1 : measuredLength; + } + + lastCluster = glyphInfo.GlyphCluster; + currentWidth += glyphInfo.GlyphAdvance; + } + + break; } - lastCluster = glyphInfo.GlyphCluster; - currentWidth += glyphInfo.GlyphAdvance; + case { } drawableTextRun: + { + if (currentWidth + drawableTextRun.Size.Width > paragraphWidth) + { + var measuredLength = lastCluster - textRange.Start; + + return measuredLength == 0 ? 1 : measuredLength; + } + + lastCluster += currentRun.TextSourceLength; + currentWidth += currentRun.Size.Width; + + break; + } } } @@ -452,7 +537,7 @@ namespace Avalonia.Media.TextFormatting /// /// The current line break if the line was explicitly broken. /// The wrapped text line. - private static TextLineImpl PerformTextWrapping(List textRuns, TextRange textRange, + private static TextLineImpl PerformTextWrapping(List textRuns, TextRange textRange, double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection, TextLineBreak? currentLineBreak) { @@ -568,7 +653,7 @@ namespace Avalonia.Media.TextFormatting break; } - var splitResult = SplitShapedRuns(textRuns, measuredLength); + var splitResult = SplitDrawableRuns(textRuns, measuredLength); textRange = new TextRange(textRange.Start, measuredLength); @@ -644,7 +729,9 @@ namespace Avalonia.Media.TextFormatting var cultureInfo = textRun.Properties.CultureInfo; - var shapedBuffer = textShaper.ShapeText(textRun.Text, glyphTypeface, fontRenderingEmSize, cultureInfo, (sbyte)flowDirection); + var shaperOptions = new TextShaperOptions(glyphTypeface, fontRenderingEmSize, (sbyte)flowDirection, cultureInfo); + + var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions); return new ShapedTextCharacters(shapedBuffer, textRun.Properties); } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index 0ff127694b..fa4725ad64 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -193,187 +193,34 @@ namespace Avalonia.Media.TextFormatting var result = new List(TextLines.Count); var currentY = 0d; - var currentPosition = 0; - var currentRect = Rect.Empty; foreach (var textLine in TextLines) { //Current line isn't covered. - if (currentPosition + textLine.TextRange.Length <= start) + if (textLine.TextRange.Start + textLine.TextRange.Length <= start) { currentY += textLine.Height; - currentPosition += textLine.TextRange.Length; continue; } - //The whole line is covered. - if (currentPosition >= start && start + length > currentPosition + textLine.TextRange.Length) - { - currentRect = new Rect(textLine.Start, currentY, textLine.WidthIncludingTrailingWhitespace, - textLine.Height); - - result.Add(currentRect); - - currentY += textLine.Height; - currentPosition += textLine.TextRange.Length; - - continue; - } - - var startX = textLine.Start; - - //A portion of the line is covered. - for (var index = 0; index < textLine.TextRuns.Count; index++) - { - var currentRun = (ShapedTextCharacters)textLine.TextRuns[index]; - ShapedTextCharacters? nextRun = null; - - if (index + 1 < textLine.TextRuns.Count) - { - nextRun = (ShapedTextCharacters)textLine.TextRuns[index + 1]; - } - - if (nextRun != null) - { - if (nextRun.Text.Start < currentRun.Text.Start && start + length < currentRun.Text.End) - { - goto skip; - } - - if (currentRun.Text.Start >= start + length) - { - goto skip; - } - - if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < start) - { - goto skip; - } - - if (currentRun.Text.End < start) - { - goto skip; - } - - goto noop; - - skip: - { - startX += currentRun.Size.Width; - - currentPosition = currentRun.Text.Start; - } - - continue; - - noop:{ } - } - - var endOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit( - currentRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(start + length) : - new CharacterHit(start)); + var textBounds = textLine.GetTextBounds(start, length); - var endX = startX + endOffset; - - var startOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit( - currentRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(start) : - new CharacterHit(start + length)); - - startX += startOffset; - - var characterHit = currentRun.GlyphRun.IsLeftToRight ? - currentRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) : - currentRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); - - currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - - if(nextRun != null) - { - if (currentRun.ShapedBuffer.IsLeftToRight == nextRun.ShapedBuffer.IsLeftToRight) - { - endOffset = nextRun.GlyphRun.GetDistanceFromCharacterHit( - nextRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(start + length) : - new CharacterHit(start)); - - index++; - - endX += endOffset; - - currentRun = nextRun; - - if (currentRun.ShapedBuffer.IsLeftToRight) - { - characterHit = nextRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); - - currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - } - } - } - - if (endX < startX) - { - (endX, startX) = (startX, endX); - } - - var width = endX - startX; + foreach (var bounds in textBounds) + { + Rect? last = result.Count > 0 ? result[result.Count - 1] : null; - if (result.Count > 0 && MathUtilities.AreClose(currentRect.Top, currentY) && - MathUtilities.AreClose(currentRect.Right, startX)) - { - result[result.Count - 1] = currentRect.WithWidth(currentRect.Width + width); - } - else - { - currentRect = new Rect(startX, currentY, width, textLine.Height); - - result.Add(currentRect); - } - - if (currentRun.ShapedBuffer.IsLeftToRight) + if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY)) { - if (nextRun != null) - { - if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= start + length) - { - break; - } - - currentPosition = nextRun.Text.End; - } - else - { - if (currentPosition >= start + length) - { - break; - } - } + result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width); } else { - if (currentPosition <= start) - { - break; - } - } - - if (!currentRun.ShapedBuffer.IsLeftToRight && currentPosition != currentRun.Text.Start) - { - endX += currentRun.GlyphRun.Size.Width - endOffset; - } - - startX = endX; + result.Add(bounds.Rectangle.WithY(currentY)); + } } - if (currentPosition == start || currentPosition == start + length) - { - break; - } - - if (textLine.TextRange.Start + textLine.TextRange.Length >= start + length) + if(textLine.TextRange.Start + textLine.TextRange.Length >= start + length) { break; } @@ -541,7 +388,7 @@ namespace Avalonia.Media.TextFormatting var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize, (sbyte)flowDirection); - var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; + var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; var textRange = new TextRange(startingIndex, 1); diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index 74c4573630..838ff82ab2 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -17,7 +17,7 @@ namespace Avalonia.Media.TextFormatting /// Text used as collapsing symbol. /// Length of leading prefix. /// width in which collapsing is constrained to - /// text run properties of ellispis symbol + /// text run properties of ellipsis symbol public TextLeadingPrefixCharacterEllipsis( ReadOnlySlice ellipsis, int prefixLength, @@ -35,16 +35,14 @@ namespace Avalonia.Media.TextFormatting } /// - public sealed override double Width { get; } + public override double Width { get; } /// - public sealed override TextRun Symbol { get; } + public override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine) + public override List? Collapse(TextLine textLine) { - var shapedTextRuns = textLine.TextRuns as List; - - if (shapedTextRuns is null) + if (textLine.TextRuns is not List textRuns || textRuns.Count == 0) { return null; } @@ -55,84 +53,105 @@ namespace Avalonia.Media.TextFormatting if (Width < shapedSymbol.GlyphRun.Size.Width) { - return new List(0); + return new List(0); } // Overview of ellipsis structure // Prefix length run | Ellipsis symbol | Post split run growing from the end | var availableWidth = Width - shapedSymbol.Size.Width; - while (runIndex < shapedTextRuns.Count) + while (runIndex < textRuns.Count) { - var currentRun = shapedTextRuns[runIndex]; - - currentWidth += currentRun.Size.Width; + var currentRun = textRuns[runIndex]; - if (currentWidth > availableWidth) + switch (currentRun) { - currentRun.TryMeasureCharacters(availableWidth, out var measuredLength); - - var shapedTextCharacters = new List(shapedTextRuns.Count); - - if (measuredLength > 0) + case ShapedTextCharacters shapedRun: { - List? preSplitRuns = null; - List? postSplitRuns = null; + currentWidth += currentRun.Size.Width; - if (_prefixLength > 0) + if (currentWidth > availableWidth) { - var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, Math.Min(_prefixLength, measuredLength)); + shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength); - shapedTextCharacters.AddRange(splitResult.First); + var collapsedRuns = new List(textRuns.Count); - TextLineImpl.SortRuns(shapedTextCharacters); + if (measuredLength > 0) + { + List? preSplitRuns = null; + List? postSplitRuns; - preSplitRuns = splitResult.First; - postSplitRuns = splitResult.Second; - } - else - { - postSplitRuns = shapedTextRuns; - } + if (_prefixLength > 0) + { + var splitResult = TextFormatterImpl.SplitDrawableRuns(textRuns, + Math.Min(_prefixLength, measuredLength)); - shapedTextCharacters.Add(shapedSymbol); + collapsedRuns.AddRange(splitResult.First); - if (measuredLength > _prefixLength && postSplitRuns is not null) - { - var availableSuffixWidth = availableWidth; + TextLineImpl.SortRuns(collapsedRuns); - if (preSplitRuns is not null) - { - foreach (var run in preSplitRuns) + preSplitRuns = splitResult.First; + postSplitRuns = splitResult.Second; + } + else { - availableSuffixWidth -= run.Size.Width; + postSplitRuns = textRuns; } - } - for (int i = postSplitRuns.Count - 1; i >= 0; i--) - { - var run = postSplitRuns[i]; + collapsedRuns.Add(shapedSymbol); - if (run.TryMeasureCharactersBackwards(availableSuffixWidth, out int suffixCount, out double suffixWidth)) + if (measuredLength <= _prefixLength || postSplitRuns is null) { - availableSuffixWidth -= suffixWidth; + return collapsedRuns; + } - if (suffixCount > 0) + var availableSuffixWidth = availableWidth; + + if (preSplitRuns is not null) + { + foreach (var run in preSplitRuns) { - var splitSuffix = run.Split(run.TextSourceLength - suffixCount); + availableSuffixWidth -= run.Size.Width; + } + } - shapedTextCharacters.Add(splitSuffix.Second!); + for (var i = postSplitRuns.Count - 1; i >= 0; i--) + { + var run = postSplitRuns[i]; + + switch (run) + { + case ShapedTextCharacters endShapedRun: + { + if (endShapedRun.TryMeasureCharactersBackwards(availableSuffixWidth, + out var suffixCount, out var suffixWidth)) + { + availableSuffixWidth -= suffixWidth; + + if (suffixCount > 0) + { + var splitSuffix = + endShapedRun.Split(run.TextSourceLength - suffixCount); + + collapsedRuns.Add(splitSuffix.Second!); + } + } + + break; + } } } } + else + { + collapsedRuns.Add(shapedSymbol); + } + + return collapsedRuns; } - } - else - { - shapedTextCharacters.Add(shapedSymbol); - } - return shapedTextCharacters; + break; + } } availableWidth -= currentRun.Size.Width; diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs index 130d0e9c39..d163c228b2 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs @@ -189,6 +189,14 @@ namespace Avalonia.Media.TextFormatting /// The after backspacing. public abstract CharacterHit GetBackspaceCaretCharacterHit(CharacterHit characterHit); + /// + /// Get an array of bounding rectangles of a range of characters within a text line. + /// + /// index of first character of specified range + /// number of characters of the specified range + /// an array of bounding rectangles. + public abstract IReadOnlyList GetTextBounds(int firstTextSourceCharacterIndex, int textLength); + /// /// Gets the text line offset x. /// diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs index be9661c2bf..ce35e47fbd 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs @@ -5,11 +5,11 @@ namespace Avalonia.Media.TextFormatting public class TextLineBreak { public TextLineBreak(TextEndOfLine? textEndOfLine = null, FlowDirection flowDirection = FlowDirection.LeftToRight, - IReadOnlyList? remainingCharacters = null) + IReadOnlyList? remainingRuns = null) { TextEndOfLine = textEndOfLine; FlowDirection = flowDirection; - RemainingCharacters = remainingCharacters; + RemainingRuns = remainingRuns; } /// @@ -23,8 +23,8 @@ namespace Avalonia.Media.TextFormatting public FlowDirection FlowDirection { get; } /// - /// Get the remaining shaped characters that were split up by the during the formatting process. + /// Get the remaining runs that were split up by the during the formatting process. /// - public IReadOnlyList? RemainingCharacters { get; } + public IReadOnlyList? RemainingRuns { get; } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index 49bee6e776..fe52fbeef5 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -9,24 +9,32 @@ namespace Avalonia.Media.TextFormatting { private static readonly Comparer s_compareStart = Comparer.Default; - private static readonly Comparison s_compareLogicalOrder = - (a, b) => s_compareStart.Compare(a.Text.Start, b.Text.Start); + private static readonly Comparison s_compareLogicalOrder = + (a, b) => + { + if (a is ShapedTextCharacters && b is ShapedTextCharacters) + { + return s_compareStart.Compare(a.Text.Start, b.Text.Start); + } - private readonly List _textRuns; + return 0; + }; + + private readonly List _textRuns; private readonly double _paragraphWidth; private readonly TextParagraphProperties _paragraphProperties; private TextLineMetrics _textLineMetrics; private readonly FlowDirection _flowDirection; - public TextLineImpl(List textRuns, TextRange textRange, double paragraphWidth, - TextParagraphProperties paragraphProperties, FlowDirection flowDirection = FlowDirection.LeftToRight, + public TextLineImpl(List textRuns, TextRange textRange, double paragraphWidth, + TextParagraphProperties paragraphProperties, FlowDirection flowDirection = FlowDirection.LeftToRight, TextLineBreak? lineBreak = null, bool hasCollapsed = false) { TextRange = textRange; TextLineBreak = lineBreak; HasCollapsed = hasCollapsed; - _textRuns = textRuns; + _textRuns = textRuns; _paragraphWidth = paragraphWidth; _paragraphProperties = paragraphProperties; @@ -88,7 +96,7 @@ namespace Avalonia.Media.TextFormatting foreach (var textRun in _textRuns) { - var offsetY = Baseline - textRun.GlyphRun.BaselineOrigin.Y; + var offsetY = GetBaselineOffset(this, textRun); textRun.Draw(drawingContext, new Point(currentX, currentY + offsetY)); @@ -96,6 +104,30 @@ namespace Avalonia.Media.TextFormatting } } + private static double GetBaselineOffset(TextLine textLine, DrawableTextRun textRun) + { + var baseline = textRun.Baseline; + var baselineAlignment = textRun.Properties?.BaselineAlignment; + + switch (baselineAlignment) + { + case BaselineAlignment.Top: + return 0; + case BaselineAlignment.Center: + return textLine.Height / 2 - textRun.Size.Height / 2; + case BaselineAlignment.Bottom: + return textLine.Height - textRun.Size.Height; + case BaselineAlignment.Baseline: + case BaselineAlignment.TextTop: + case BaselineAlignment.TextBottom: + case BaselineAlignment.Subscript: + case BaselineAlignment.Superscript: + return textLine.Baseline - baseline; + default: + throw new ArgumentOutOfRangeException(nameof(baselineAlignment), baselineAlignment, null); + } + } + /// public override TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList) { @@ -108,47 +140,82 @@ namespace Avalonia.Media.TextFormatting var collapsedRuns = collapsingProperties.Collapse(this); - if (collapsedRuns is List shapedRuns) + if (collapsedRuns is null) { - var collapsedLine = new TextLineImpl(shapedRuns, TextRange, _paragraphWidth, _paragraphProperties, _flowDirection, TextLineBreak, true); + return this; + } - if (shapedRuns.Count > 0) - { - collapsedLine.FinalizeLine(); - } + var collapsedLine = new TextLineImpl(collapsedRuns, TextRange, _paragraphWidth, _paragraphProperties, + _flowDirection, TextLineBreak, true); - return collapsedLine; + if (collapsedRuns.Count > 0) + { + collapsedLine.FinalizeLine(); } - return this; + return collapsedLine; + } /// public override CharacterHit GetCharacterHitFromDistance(double distance) { distance -= Start; - + if (distance <= 0) { + if (_textRuns.Count == 0) + { + return _flowDirection == FlowDirection.LeftToRight ? + new CharacterHit(TextRange.Start) : + new CharacterHit(TextRange.Start, TextRange.Length); + } + // hit happens before the line, return the first position var firstRun = _textRuns[0]; - return firstRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + if (firstRun is ShapedTextCharacters shapedTextCharacters) + { + return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _); + } + + return _flowDirection == FlowDirection.LeftToRight ? + new CharacterHit(TextRange.Start) : + new CharacterHit(TextRange.Start + TextRange.Length); } // process hit that happens within the line var characterHit = new CharacterHit(); - foreach (var run in _textRuns) + foreach (var currentRun in _textRuns) { - characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _); + switch (currentRun) + { + case ShapedTextCharacters shapedRun: + { + characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + break; + } + default: + { + if(distance < currentRun.Size.Width / 2) + { + characterHit = new CharacterHit(currentRun.Text.Start); + } + else + { + characterHit = new CharacterHit(currentRun.Text.Start, currentRun.Text.Length); + } + break; + } + } - if (distance <= run.Size.Width) + if (distance <= currentRun.Size.Width) { break; } - distance -= run.Size.Width; + distance -= currentRun.Size.Width; } return characterHit; @@ -166,83 +233,105 @@ namespace Avalonia.Media.TextFormatting for (var index = 0; index < _textRuns.Count; index++) { var textRun = _textRuns[index]; - var currentRun = textRun.GlyphRun; - - if (lastRun != null) - { - if (!lastRun.IsLeftToRight && currentRun.IsLeftToRight && - currentRun.Characters.Start == characterHit.FirstCharacterIndex && - characterHit.TrailingLength == 0) - { - return currentDistance; - } - } - //Look for a hit in within the current run - if (characterIndex >= textRun.Text.Start && characterIndex <= textRun.Text.End) + switch (textRun) { - var distance = currentRun.GetDistanceFromCharacterHit(characterHit); + case ShapedTextCharacters shapedTextCharacters: + { + var currentRun = shapedTextCharacters.GlyphRun; - return currentDistance + distance; - } + if (lastRun != null) + { + if (!lastRun.IsLeftToRight && currentRun.IsLeftToRight && + currentRun.Characters.Start == characterHit.FirstCharacterIndex && + characterHit.TrailingLength == 0) + { + return currentDistance; + } + } - //Look at the left and right edge of the current run - if (currentRun.IsLeftToRight) - { - if (lastRun == null || lastRun.IsLeftToRight) - { - if (characterIndex <= textRun.Text.Start) - { - return currentDistance; - } - } - else - { - if (characterIndex == textRun.Text.Start) - { - return currentDistance; - } - } + //Look for a hit in within the current run + if (characterIndex >= textRun.Text.Start && characterIndex <= textRun.Text.End) + { + var distance = currentRun.GetDistanceFromCharacterHit(characterHit); - if (characterIndex == textRun.Text.Start + textRun.Text.Length && characterHit.TrailingLength > 0) - { - return currentDistance + currentRun.Size.Width; - } - } - else - { - if (characterIndex == textRun.Text.Start) - { - return currentDistance + currentRun.Size.Width; - } + return currentDistance + distance; + } - var nextRun = index + 1 < _textRuns.Count ? _textRuns[index + 1] : null; + //Look at the left and right edge of the current run + if (currentRun.IsLeftToRight) + { + if (lastRun == null || lastRun.IsLeftToRight) + { + if (characterIndex <= textRun.Text.Start) + { + return currentDistance; + } + } + else + { + if (characterIndex == textRun.Text.Start) + { + return currentDistance; + } + } + + if (characterIndex == textRun.Text.Start + textRun.Text.Length && + characterHit.TrailingLength > 0) + { + return currentDistance + currentRun.Size.Width; + } + } + else + { + if (characterIndex == textRun.Text.Start) + { + return currentDistance + currentRun.Size.Width; + } + + var nextRun = index + 1 < _textRuns.Count ? + _textRuns[index + 1] as ShapedTextCharacters : + null; + + if (nextRun != null) + { + if (characterHit.FirstCharacterIndex == textRun.Text.End && + nextRun.ShapedBuffer.IsLeftToRight) + { + return currentDistance; + } + + if (characterIndex > textRun.Text.End && nextRun.Text.End < textRun.Text.End) + { + return currentDistance; + } + } + else + { + if (characterIndex > textRun.Text.End) + { + return currentDistance; + } + } + } - if (nextRun != null) - { - if (characterHit.FirstCharacterIndex == textRun.Text.End && nextRun.ShapedBuffer.IsLeftToRight) - { - return currentDistance; - } + lastRun = currentRun; - if (characterIndex > textRun.Text.End && nextRun.Text.End < textRun.Text.End) - { - return currentDistance; + break; } - } - else - { - if (characterIndex > textRun.Text.End) + default: { - return currentDistance; + if(characterIndex == textRun.Text.Start) + { + return currentDistance; + } + + break; } - } } //No hit hit found so we add the full width - currentDistance += currentRun.Size.Width; - - lastRun = currentRun; + currentDistance += textRun.Size.Width; } return currentDistance; @@ -251,6 +340,15 @@ namespace Avalonia.Media.TextFormatting /// public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit) { + if (_textRuns.Count == 0) + { + var textPosition = TextRange.Start + TextRange.Length; + + return characterHit.FirstCharacterIndex + characterHit.TrailingLength == textPosition ? + characterHit : + new CharacterHit(textPosition); + } + if (TryFindNextCharacterHit(characterHit, out var nextCharacterHit)) { return nextCharacterHit; @@ -259,11 +357,23 @@ namespace Avalonia.Media.TextFormatting // Can't move, we're after the last character var runIndex = GetRunIndexAtCharacterIndex(TextRange.End, LogicalDirection.Forward); - var textRun = _textRuns[runIndex]; + var currentRun = _textRuns[runIndex]; - characterHit = textRun.GlyphRun.GetNextCaretCharacterHit(characterHit); + switch (currentRun) + { + case ShapedTextCharacters shapedRun: + { + characterHit = shapedRun.GlyphRun.GetNextCaretCharacterHit(characterHit); + break; + } + default: + { + characterHit = new CharacterHit(currentRun.Text.Start, currentRun.Text.Length); + break; + } + } - return characterHit; + return characterHit; } /// @@ -289,7 +399,202 @@ namespace Avalonia.Media.TextFormatting return GetPreviousCaretCharacterHit(characterHit); } - public static void SortRuns(List textRuns) + public override IReadOnlyList GetTextBounds(int firstTextSourceCharacterIndex, int textLength) + { + if (firstTextSourceCharacterIndex + textLength <= TextRange.Start) + { + return Array.Empty(); + } + + var result = new List(TextRuns.Count); + var lastDirection = _flowDirection; + var currentDirection = lastDirection; + var currentPosition = 0; + var currentRect = Rect.Empty; + var startX = Start; + + //A portion of the line is covered. + for (var index = 0; index < TextRuns.Count; index++) + { + var currentRun = TextRuns[index] as DrawableTextRun; + + if (currentRun is null) + { + continue; + } + + TextRun? nextRun = null; + + if (index + 1 < TextRuns.Count) + { + nextRun = TextRuns[index + 1]; + } + + if (nextRun != null) + { + if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) + { + goto skip; + } + + if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) + { + goto skip; + } + + if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) + { + goto skip; + } + + if (currentRun.Text.End < firstTextSourceCharacterIndex) + { + goto skip; + } + + goto noop; + + skip: + { + startX += currentRun.Size.Width; + } + + continue; + + noop: + { + } + } + + + var endX = startX; + var endOffset = 0d; + + switch (currentRun) + { + case ShapedTextCharacters shapedRun: + { + endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( + shapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(firstTextSourceCharacterIndex + textLength) : + new CharacterHit(firstTextSourceCharacterIndex)); + + endX += endOffset; + + var startOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( + shapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(firstTextSourceCharacterIndex) : + new CharacterHit(firstTextSourceCharacterIndex + textLength)); + + startX += startOffset; + + var characterHit = shapedRun.GlyphRun.IsLeftToRight ? + shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) : + shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); + + currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + + currentDirection = shapedRun.ShapedBuffer.IsLeftToRight ? + FlowDirection.LeftToRight : + FlowDirection.RightToLeft; + + if (nextRun is ShapedTextCharacters nextShaped) + { + if (shapedRun.ShapedBuffer.IsLeftToRight == nextShaped.ShapedBuffer.IsLeftToRight) + { + endOffset = nextShaped.GlyphRun.GetDistanceFromCharacterHit( + nextShaped.ShapedBuffer.IsLeftToRight ? + new CharacterHit(firstTextSourceCharacterIndex + textLength) : + new CharacterHit(firstTextSourceCharacterIndex)); + + index++; + + endX += endOffset; + + currentRun = nextShaped; + + if (nextShaped.ShapedBuffer.IsLeftToRight) + { + characterHit = nextShaped.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + + currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + } + } + } + + break; + } + default: + { + if (firstTextSourceCharacterIndex + textLength >= currentRun.Text.Start + currentRun.Text.Length) + { + endX += currentRun.Size.Width; + } + + break; + } + } + + if (endX < startX) + { + (endX, startX) = (startX, endX); + } + + var width = endX - startX; + + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + var textBounds = new TextBounds(currentRect.WithWidth(currentRect.Width + width), currentDirection); + + result[result.Count - 1] = textBounds; + } + else + { + currentRect = new Rect(startX, 0, width, Height); + + result.Add(new TextBounds(currentRect, currentDirection)); + } + + if (currentDirection == FlowDirection.LeftToRight) + { + if (nextRun != null) + { + if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= firstTextSourceCharacterIndex + textLength) + { + break; + } + + currentPosition = nextRun.Text.End; + } + else + { + if (currentPosition >= firstTextSourceCharacterIndex + textLength) + { + break; + } + } + } + else + { + if (currentPosition <= firstTextSourceCharacterIndex) + { + break; + } + + if (currentPosition != currentRun.Text.Start) + { + endX += currentRun.Size.Width - endOffset; + } + } + + lastDirection = currentDirection; + startX = endX; + } + + return result; + } + + public static void SortRuns(List textRuns) { textRuns.Sort(s_compareLogicalOrder); } @@ -303,18 +608,37 @@ namespace Avalonia.Media.TextFormatting return this; } + private static sbyte GetRunBidiLevel(DrawableTextRun run, FlowDirection flowDirection) + { + if (run is ShapedTextCharacters shapedTextCharacters) + { + return shapedTextCharacters.BidiLevel; + } + + var defaultLevel = flowDirection == FlowDirection.LeftToRight ? 0 : 1; + + return (sbyte)defaultLevel; + } + private void BidiReorder() { + if (_textRuns.Count == 0) + { + return; + } + // Build up the collection of ordered runs. var run = _textRuns[0]; - OrderedBidiRun orderedRun = new(run); + + OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _flowDirection)); + var current = orderedRun; for (var i = 1; i < _textRuns.Count; i++) { run = _textRuns[i]; - current.Next = new OrderedBidiRun(run); + current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _flowDirection)); current = current.Next; } @@ -331,7 +655,9 @@ namespace Avalonia.Media.TextFormatting for (var i = 0; i < _textRuns.Count; i++) { - var level = _textRuns[i].BidiLevel; + var currentRun = _textRuns[i]; + + var level = GetRunBidiLevel(currentRun, _flowDirection); if (level > max) { @@ -366,9 +692,9 @@ namespace Avalonia.Media.TextFormatting { if (current.Level >= minLevelToReverse && current.Level % 2 != 0) { - if (!current.Run.IsReversed) + if (current.Run is ShapedTextCharacters { IsReversed: false } shapedTextCharacters) { - current.Run.Reverse(); + shapedTextCharacters.Reverse(); } } @@ -479,36 +805,55 @@ namespace Avalonia.Media.TextFormatting while (runIndex < _textRuns.Count) { - var run = _textRuns[runIndex]; + var currentRun = _textRuns[runIndex]; - var foundCharacterHit = - run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, - out _); + switch (currentRun) + { + case ShapedTextCharacters shapedRun: + { + var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _); - var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength == - TextRange.Start + TextRange.Length; + var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength == + TextRange.Start + TextRange.Length; - if (isAtEnd && !run.GlyphRun.IsLeftToRight) - { - nextCharacterHit = foundCharacterHit; + if (isAtEnd && !shapedRun.GlyphRun.IsLeftToRight) + { + nextCharacterHit = foundCharacterHit; - return true; - } + return true; + } - var characterIndex = codepointIndex - run.Text.Start; + var characterIndex = codepointIndex - shapedRun.Text.Start; - if (characterIndex < 0 && run.ShapedBuffer.IsLeftToRight) - { - foundCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex); - } + if (characterIndex < 0 && shapedRun.ShapedBuffer.IsLeftToRight) + { + foundCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex); + } - nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ? - foundCharacterHit : - new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength); + nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ? + foundCharacterHit : + new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength); - if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex) - { - return true; + if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex) + { + return true; + } + + break; + } + default: + { + var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + + if (textPosition == currentRun.Text.Start) + { + nextCharacterHit = new CharacterHit(currentRun.Text.Start + currentRun.Text.Length); + + return true; + } + + break; + } } runIndex++; @@ -545,25 +890,46 @@ namespace Avalonia.Media.TextFormatting while (runIndex >= 0) { - var run = _textRuns[runIndex]; - - var foundCharacterHit = - run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); + var currentRun = _textRuns[runIndex]; - if (foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength < characterIndex) + switch (currentRun) { - previousCharacterHit = foundCharacterHit; - - return true; - } - - previousCharacterHit = characterHit.TrailingLength != 0 ? - foundCharacterHit : - new CharacterHit(foundCharacterHit.FirstCharacterIndex); + case ShapedTextCharacters shapedRun: + { + var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); - if (previousCharacterHit != characterHit) - { - return true; + if (foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength < characterIndex) + { + previousCharacterHit = foundCharacterHit; + + return true; + } + + var previousPosition = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength; + + if(foundCharacterHit.TrailingLength > 0 && previousPosition == characterIndex) + { + previousCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex); + } + + if (previousCharacterHit != characterHit) + { + return true; + } + + break; + } + default: + { + if(characterIndex == currentRun.Text.Start + currentRun.Text.Length) + { + previousCharacterHit = new CharacterHit(currentRun.Text.Start); + + return true; + } + + break; + } } runIndex--; @@ -581,54 +947,81 @@ namespace Avalonia.Media.TextFormatting private int GetRunIndexAtCharacterIndex(int codepointIndex, LogicalDirection direction) { var runIndex = 0; - ShapedTextCharacters? previousRun = null; + DrawableTextRun? previousRun = null; while (runIndex < _textRuns.Count) { var currentRun = _textRuns[runIndex]; - if (previousRun != null && !previousRun.ShapedBuffer.IsLeftToRight) + switch (currentRun) { - if (currentRun.ShapedBuffer.IsLeftToRight) - { - if (currentRun.Text.Start >= codepointIndex) + case ShapedTextCharacters shapedRun: { - return --runIndex; + if (previousRun is ShapedTextCharacters previousShaped && !previousShaped.ShapedBuffer.IsLeftToRight) + { + if (shapedRun.ShapedBuffer.IsLeftToRight) + { + if (currentRun.Text.Start >= codepointIndex) + { + return --runIndex; + } + } + else + { + if (codepointIndex > currentRun.Text.Start + currentRun.Text.Length) + { + return --runIndex; + } + } + } + + if (direction == LogicalDirection.Forward) + { + if (codepointIndex >= currentRun.Text.Start && codepointIndex <= currentRun.Text.End) + { + return runIndex; + } + } + else + { + if (codepointIndex > currentRun.Text.Start && + codepointIndex <= currentRun.Text.Start + currentRun.Text.Length) + { + return runIndex; + } + } + + if (runIndex + 1 < _textRuns.Count) + { + runIndex++; + previousRun = currentRun; + } + else + { + break; + } + break; } - } - else - { - if (codepointIndex > currentRun.Text.Start + currentRun.Text.Length) + + default: { - return --runIndex; - } - } - } + if (codepointIndex == currentRun.Text.Start) + { + return runIndex; + } - if (direction == LogicalDirection.Forward) - { - if (codepointIndex >= currentRun.Text.Start && codepointIndex <= currentRun.Text.End) - { - return runIndex; - } - } - else - { - if (codepointIndex > currentRun.Text.Start && - codepointIndex <= currentRun.Text.Start + currentRun.Text.Length) - { - return runIndex; - } - } + if (runIndex + 1 < _textRuns.Count) + { + runIndex++; + previousRun = currentRun; + } + else + { + return runIndex; + } - if (runIndex + 1 < _textRuns.Count) - { - runIndex++; - previousRun = currentRun; - } - else - { - break; + break; + } } } @@ -637,6 +1030,8 @@ namespace Avalonia.Media.TextFormatting private TextLineMetrics CreateLineMetrics() { + var start = 0d; + var height = 0d; var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; @@ -646,76 +1041,137 @@ namespace Avalonia.Media.TextFormatting var lineGap = 0d; var fontRenderingEmSize = 0d; - for (var index = 0; index < _textRuns.Count; index++) + var lineHeight = _paragraphProperties.LineHeight; + + if (_textRuns.Count == 0) { - var textRun = _textRuns[index]; + var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; + fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; + var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; + ascent = glyphTypeface.Ascent * scale; + height = double.IsNaN(lineHeight) || MathUtilities.IsZero(lineHeight) ? + descent - ascent + lineGap : + lineHeight; - var fontMetrics = - new FontMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize); + return new TextLineMetrics(false, height, 0, start, -ascent, 0, 0, 0); + } - if (fontRenderingEmSize < textRun.Properties.FontRenderingEmSize) + for (var index = 0; index < _textRuns.Count; index++) + { + switch (_textRuns[index]) { - fontRenderingEmSize = textRun.Properties.FontRenderingEmSize; - - if (ascent > fontMetrics.Ascent) - { - ascent = fontMetrics.Ascent; - } - - if (descent < fontMetrics.Descent) - { - descent = fontMetrics.Descent; - } + case ShapedTextCharacters textRun: + { + var fontMetrics = + new FontMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize); - if (lineGap < fontMetrics.LineGap) - { - lineGap = fontMetrics.LineGap; - } - } + if (fontRenderingEmSize < textRun.Properties.FontRenderingEmSize) + { + fontRenderingEmSize = textRun.Properties.FontRenderingEmSize; + + if (ascent > fontMetrics.Ascent) + { + ascent = fontMetrics.Ascent; + } + + if (descent < fontMetrics.Descent) + { + descent = fontMetrics.Descent; + } + + if (lineGap < fontMetrics.LineGap) + { + lineGap = fontMetrics.LineGap; + } + } - switch (_paragraphProperties.FlowDirection) - { - case FlowDirection.LeftToRight: - { - if (index == _textRuns.Count - 1) + switch (_paragraphProperties.FlowDirection) { - width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; - trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = textRun.GlyphRun.Metrics.NewlineLength; + case FlowDirection.LeftToRight: + { + if (index == _textRuns.Count - 1) + { + width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; + trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; + newLineLength = textRun.GlyphRun.Metrics.NewlineLength; + } + + break; + } + + case FlowDirection.RightToLeft: + { + if (index == _textRuns.Count - 1) + { + var firstRun = _textRuns[0]; + + if (firstRun is ShapedTextCharacters shapedTextCharacters) + { + var offset = shapedTextCharacters.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - + shapedTextCharacters.GlyphRun.Metrics.Width; + + width = widthIncludingWhitespace + + textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset; + + trailingWhitespaceLength = shapedTextCharacters.GlyphRun.Metrics.TrailingWhitespaceLength; + newLineLength = shapedTextCharacters.GlyphRun.Metrics.NewlineLength; + } + } + + break; + } } + widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; + break; } - case FlowDirection.RightToLeft: + case { } drawableTextRun: { - if (index == _textRuns.Count - 1) - { - var firstRun = _textRuns[0]; + widthIncludingWhitespace += drawableTextRun.Size.Width; - var offset = firstRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - - firstRun.GlyphRun.Metrics.Width; - - width = widthIncludingWhitespace + - textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset; + switch (_paragraphProperties.FlowDirection) + { + case FlowDirection.LeftToRight: + { + if (index == _textRuns.Count - 1) + { + width = widthIncludingWhitespace; + trailingWhitespaceLength = 0; + newLineLength = 0; + } + + break; + } + + case FlowDirection.RightToLeft: + { + if (index == _textRuns.Count - 1) + { + width = widthIncludingWhitespace; + trailingWhitespaceLength = 0; + newLineLength = 0; + } + + break; + } + } - trailingWhitespaceLength = firstRun.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = firstRun.GlyphRun.Metrics.NewlineLength; + if (ascent > -drawableTextRun.Size.Height) + { + ascent = -drawableTextRun.Size.Height; } break; } } - - widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; } - var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, + start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, _paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection); - var lineHeight = _paragraphProperties.LineHeight; - - var height = double.IsNaN(lineHeight) || MathUtilities.IsZero(lineHeight) ? + height = double.IsNaN(lineHeight) || MathUtilities.IsZero(lineHeight) ? descent - ascent + lineGap : lineHeight; @@ -725,15 +1181,17 @@ namespace Avalonia.Media.TextFormatting private sealed class OrderedBidiRun { - public OrderedBidiRun(ShapedTextCharacters run) => Run = run; + public OrderedBidiRun(DrawableTextRun run, sbyte level) + { + Run = run; + Level = level; + } - public sbyte Level => Run.BidiLevel; + public sbyte Level { get; } - public ShapedTextCharacters Run { get; } + public DrawableTextRun Run { get; } public OrderedBidiRun? Next { get; set; } - - public void Reverse() => Run.ShapedBuffer.GlyphInfos.Span.Reverse(); } private sealed class BidiRange diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs index b799567a60..82a0ba14d8 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs @@ -63,5 +63,13 @@ { get { return 0; } } + + /// + /// Default Incremental Tab + /// + public virtual double DefaultIncrementalTab + { + get { return 4 * DefaultTextRunProperties.FontRenderingEmSize; } + } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs index 99fcbd805f..86b701cb4b 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs @@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting /// /// Run vertical box alignment /// - public abstract BaselineAlignment BaselineAlignment { get; } + public virtual BaselineAlignment BaselineAlignment => BaselineAlignment.Baseline; public bool Equals(TextRunProperties? other) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs index c982a435c3..615b1553b6 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs @@ -45,10 +45,9 @@ namespace Avalonia.Media.TextFormatting } /// - public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, - CultureInfo? culture, sbyte bidiLevel) + public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { - return _platformImpl.ShapeText(text, typeface, fontRenderingEmSize, culture, bidiLevel); + return _platformImpl.ShapeText(text, options); } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs new file mode 100644 index 0000000000..a7fe92dc9a --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs @@ -0,0 +1,49 @@ +using System.Globalization; + +namespace Avalonia.Media.TextFormatting +{ + /// + /// Options to customize text shaping. + /// + public readonly struct TextShaperOptions + { + public TextShaperOptions( + GlyphTypeface typeface, + double fontRenderingEmSize = 12, + sbyte bidiLevel = 0, + CultureInfo? culture = null, + double incrementalTabWidth = 0) + { + Typeface = typeface; + FontRenderingEmSize = fontRenderingEmSize; + BidLevel = bidiLevel; + Culture = culture; + IncrementalTabWidth = incrementalTabWidth; + } + + /// + /// Get the typeface. + /// + public GlyphTypeface Typeface { get; } + /// + /// Get the font rendering em size. + /// + public double FontRenderingEmSize { get; } + + /// + /// Get the bidi level of the text. + /// + public sbyte BidLevel { get; } + + /// + /// Get the culture. + /// + public CultureInfo? Culture { get; } + + /// + /// Get the incremental tab width. + /// + public double IncrementalTabWidth { get; } + + } +} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index 83acaa021e..670d94e928 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -14,7 +14,7 @@ namespace Avalonia.Media.TextFormatting /// /// Text used as collapsing symbol. /// Width in which collapsing is constrained to. - /// Text run properties of ellispis symbol. + /// Text run properties of ellipsis symbol. public TextTrailingCharacterEllipsis(ReadOnlySlice ellipsis, double width, TextRunProperties textRunProperties) { Width = width; @@ -22,12 +22,12 @@ namespace Avalonia.Media.TextFormatting } /// - public sealed override double Width { get; } + public override double Width { get; } /// - public sealed override TextRun Symbol { get; } + public override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine) + public override List? Collapse(TextLine textLine) { return TextEllipsisHelper.Collapse(textLine, this, false); } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs index ff2e4cf325..dbffbdf060 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -14,7 +14,7 @@ namespace Avalonia.Media.TextFormatting /// /// Text used as collapsing symbol. /// width in which collapsing is constrained to. - /// text run properties of ellispis symbol. + /// text run properties of ellipsis symbol. public TextTrailingWordEllipsis( ReadOnlySlice ellipsis, double width, @@ -26,12 +26,12 @@ namespace Avalonia.Media.TextFormatting } /// - public sealed override double Width { get; } + public override double Width { get; } /// - public sealed override TextRun Symbol { get; } + public override TextRun Symbol { get; } - public override IReadOnlyList? Collapse(TextLine textLine) + public override List? Collapse(TextLine textLine) { return TextEllipsisHelper.Collapse(textLine, this, true); } diff --git a/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs b/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs index aced05c9d8..11be9e3f09 100644 --- a/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs +++ b/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs @@ -1,6 +1,4 @@ -using System.Globalization; -using Avalonia.Media; -using Avalonia.Media.TextFormatting; +using Avalonia.Media.TextFormatting; using Avalonia.Utilities; namespace Avalonia.Platform @@ -14,11 +12,8 @@ namespace Avalonia.Platform /// Shapes the specified region within the text and returns a shaped buffer. /// /// The text. - /// The typeface. - /// The font rendering em size. - /// The culture. - /// The bidi level. + /// Text shaper options to customize the shaping process. /// A shaped glyph run. - ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, CultureInfo? culture, sbyte bidiLevel); - } + ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options); + } } diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index c4d11f4613..a0890262e7 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -13,12 +13,16 @@ namespace Avalonia.Skia { internal class TextShaperImpl : ITextShaperImpl { - public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, - CultureInfo culture, sbyte bidiLevel) + public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { + var typeface = options.Typeface; + var fontRenderingEmSize = options.FontRenderingEmSize; + var bidiLevel = options.BidLevel; + var culture = options.Culture; + using (var buffer = new Buffer()) { - buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length); + buffer.AddUtf16(text.Buffer.Span, text.BufferOffset, text.Length); MergeBreakPair(buffer); @@ -61,6 +65,15 @@ namespace Avalonia.Skia var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); + if(glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t') + { + glyphIndex = typeface.GetGlyph(' '); + + glyphAdvance = options.IncrementalTabWidth > 0 ? + options.IncrementalTabWidth : + 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; + } + var targetInfo = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); shapedBuffer[i] = targetInfo; diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 62cf031f86..59027a663f 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -14,9 +14,13 @@ namespace Avalonia.Direct2D1.Media internal class TextShaperImpl : ITextShaperImpl { - public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, - CultureInfo culture, sbyte bidiLevel) + public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { + var typeface = options.Typeface; + var fontRenderingEmSize = options.FontRenderingEmSize; + var bidiLevel = options.BidLevel; + var culture = options.Culture; + using (var buffer = new Buffer()) { buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length); @@ -62,6 +66,15 @@ internal class TextShaperImpl : ITextShaperImpl var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); + if (glyphIndex == 0 && text[glyphCluster] == '\t') + { + glyphIndex = typeface.GetGlyph(' '); + + glyphAdvance = options.IncrementalTabWidth > 0 ? + options.IncrementalTabWidth : + 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; + } + var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index c0c9e841f4..f15da8e0c5 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -865,6 +865,35 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Should_Fullfill_MaxLines_Contraint() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate(), + Text = "ABC", + MaxLines = 1, + AcceptsReturn= true + }; + + target.Measure(Size.Infinity); + + AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); + + var clipboard = AvaloniaLocator.CurrentMutable.GetService(); + clipboard.SetTextAsync(Environment.NewLine).GetAwaiter().GetResult(); + + RaiseKeyEvent(target, Key.V, KeyModifiers.Control); + clipboard.ClearAsync().GetAwaiter().GetResult(); + + RaiseTextEvent(target, Environment.NewLine); + + Assert.Equal("ABC", target.Text); + } + } + private static TestServices FocusServices => TestServices.MockThreadingInterface.With( focusManager: new FocusManager(), keyboardDevice: () => new KeyboardDevice(), diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index e51fff5416..f12785a7ce 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -16,6 +16,7 @@ using System.ComponentModel; using System.Linq; using System.Xml; using Xunit; +using Avalonia.Controls.Documents; namespace Avalonia.Markup.Xaml.UnitTests.Xaml { @@ -47,12 +48,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml public void Attached_Property_Is_Set() { var xaml = - @""; + @""; var target = AvaloniaRuntimeXamlLoader.Parse(xaml); Assert.NotNull(target); - Assert.Equal(21.0, TextBlock.GetFontSize(target)); + Assert.Equal(21.0, TextElement.GetFontSize(target)); } [Fact] @@ -90,13 +91,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) { var xaml = - @""; + @""; var target = AvaloniaRuntimeXamlLoader.Parse(xaml); target.DataContext = 21.0; - Assert.Equal(21.0, TextBlock.GetFontSize(target)); + Assert.Equal(21.0, TextElement.GetFontSize(target)); } } diff --git a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs index 904f0935c4..3b9caa393e 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs @@ -17,8 +17,9 @@ namespace Avalonia.Skia.UnitTests.Media { using (Start()) { + var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture); var shapedBuffer = - TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default.GlyphTypeface, 10, CultureInfo.CurrentCulture, direction); + TextShaper.Current.ShapeText(text.AsMemory(), options); var glyphRun = CreateGlyphRun(shapedBuffer); @@ -59,8 +60,9 @@ namespace Avalonia.Skia.UnitTests.Media { using (Start()) { + var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture); var shapedBuffer = - TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default.GlyphTypeface, 10, CultureInfo.CurrentCulture, direction); + TextShaper.Current.ShapeText(text.AsMemory(), options); var glyphRun = CreateGlyphRun(shapedBuffer); @@ -103,8 +105,9 @@ namespace Avalonia.Skia.UnitTests.Media { using (Start()) { + var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture); var shapedBuffer = - TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default.GlyphTypeface, 10, CultureInfo.CurrentCulture, direction); + TextShaper.Current.ShapeText(text.AsMemory(), options); var glyphRun = CreateGlyphRun(shapedBuffer); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/MultiBufferTextSource.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/MultiBufferTextSource.cs index 2a20fdd9fe..005bcdf70e 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/MultiBufferTextSource.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/MultiBufferTextSource.cs @@ -20,15 +20,10 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting public TextRun GetTextRun(int textSourceIndex) { - if (textSourceIndex > 50) + if (textSourceIndex >= 50) { return null; } - - if (textSourceIndex == 50) - { - return new TextEndOfParagraph(); - } var index = textSourceIndex / 10; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs index 65c342b065..dee4fe7f77 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs @@ -23,13 +23,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } var runText = _text.Skip(textSourceIndex); - - if (runText.IsEmpty) - { - return new TextEndOfParagraph(); - } - return new TextCharacters(runText, _defaultGenericPropertiesRunProperties); + return runText.IsEmpty ? null : new TextCharacters(runText, _defaultGenericPropertiesRunProperties); } } } diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 7c7fb4783e..35a47524f3 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -508,7 +508,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = formatter.FormatLine(textSource, 0, 33, paragraphProperties); - Assert.NotNull(textLine.TextLineBreak?.RemainingCharacters); + Assert.NotNull(textLine.TextLineBreak?.RemainingRuns); } } @@ -562,6 +562,98 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_FormatLine_With_DrawableRuns() + { + var defaultRunProperties = new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black); + var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties); + var textSource = new CustomTextSource("Hello World ->"); + + using (Start()) + { + var textLine = + TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); + + Assert.Equal(3, textLine.TextRuns.Count); + + Assert.True(textLine.TextRuns[1] is RectangleRun); + } + } + + [Fact] + public void Should_Format_With_EndOfLineRun() + { + using (Start()) + { + var defaultRunProperties = new GenericTextRunProperties(Typeface.Default); + var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties); + var textSource = new EndOfLineTextSource(); + + var textLine = + TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); + + Assert.NotNull(textLine.TextLineBreak); + + Assert.Equal(TextRun.DefaultTextSourceLength, textLine.TextRange.Length); + } + } + + private class EndOfLineTextSource : ITextSource + { + public TextRun? GetTextRun(int textSourceIndex) + { + return new TextEndOfLine(); + } + } + + private class CustomTextSource : ITextSource + { + private readonly string _text; + + public CustomTextSource(string text) + { + _text = text; + } + + public TextRun? GetTextRun(int textSourceIndex) + { + if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length) + { + return null; + } + + if (textSourceIndex == _text.Length) + { + return new RectangleRun(new Rect(0, 0, 50, 50), Brushes.Green); + } + + return new TextCharacters(_text.AsMemory(), + new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black)); + } + } + + private class RectangleRun : DrawableTextRun + { + private readonly Rect _rect; + private readonly IBrush _fill; + + public RectangleRun(Rect rect, IBrush fill) + { + _rect = rect; + _fill = fill; + } + + public override Size Size => _rect.Size; + public override double Baseline => 0; + public override void Draw(DrawingContext drawingContext, Point origin) + { + using (drawingContext.PushPreTransform(Matrix.CreateTranslation(new Vector(origin.X, 0)))) + { + drawingContext.FillRectangle(_fill, _rect); + } + } + } + public static IDisposable Start() { var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 367e6f4bea..fa04184edc 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -277,6 +277,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting firstCharacterHit = previousCharacterHit; + firstCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit); + previousCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit); Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex); @@ -416,24 +418,144 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } [Fact] - public void TextLineBreak_Should_Contain_TextEndOfLine() + public void Should_Get_Next_CharacterHit_For_Drawable_Runs() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new DrawableRunTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + Assert.Equal(4, textLine.TextRuns.Count); + + var currentHit = textLine.GetNextCaretCharacterHit(new CharacterHit(0)); + + Assert.Equal(1, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); + + currentHit = textLine.GetNextCaretCharacterHit(currentHit); + + Assert.Equal(2, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); + + currentHit = textLine.GetNextCaretCharacterHit(currentHit); + + Assert.Equal(3, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); + + currentHit = textLine.GetNextCaretCharacterHit(currentHit); + + Assert.Equal(3, currentHit.FirstCharacterIndex); + Assert.Equal(1, currentHit.TrailingLength); + } + } + + [Fact] + public void Should_Get_Previous_CharacterHit_For_Drawable_Runs() { using (Start()) { - var defaultTextRunProperties = - new GenericTextRunProperties(Typeface.Default); + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new DrawableRunTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + Assert.Equal(4, textLine.TextRuns.Count); + + var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3,1)); - const string text = "0123456789"; + Assert.Equal(3, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); - var source = new SingleBufferTextSource(text, defaultTextRunProperties); + currentHit = textLine.GetPreviousCaretCharacterHit(currentHit); - var textParagraphProperties = new GenericTextParagraphProperties(defaultTextRunProperties); + Assert.Equal(2, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); - var formatter = TextFormatter.Current; + currentHit = textLine.GetPreviousCaretCharacterHit(currentHit); - var textLine = formatter.FormatLine(source, 0, double.PositiveInfinity, textParagraphProperties); + Assert.Equal(1, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); - Assert.NotNull(textLine.TextLineBreak.TextEndOfLine); + currentHit = textLine.GetPreviousCaretCharacterHit(currentHit); + + Assert.Equal(0, currentHit.FirstCharacterIndex); + Assert.Equal(0, currentHit.TrailingLength); + } + } + + [Fact] + public void Should_Get_CharacterHit_From_Distance_For_Drawable_Runs() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new DrawableRunTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var characterHit = textLine.GetCharacterHitFromDistance(50); + + Assert.Equal(3, characterHit.FirstCharacterIndex); + Assert.Equal(1, characterHit.TrailingLength); + + characterHit = textLine.GetCharacterHitFromDistance(32); + + + Assert.Equal(3, characterHit.FirstCharacterIndex); + Assert.Equal(0, characterHit.TrailingLength); + } + } + + private class DrawableRunTextSource : ITextSource + { + const string Text = "A_A_"; + + public TextRun? GetTextRun(int textSourceIndex) + { + switch (textSourceIndex) + { + case 0: + return new TextCharacters(new ReadOnlySlice(Text.AsMemory(), 0, 1), new GenericTextRunProperties(Typeface.Default)); + case 1: + return new CustomDrawableRun(1); + case 2: + return new TextCharacters(new ReadOnlySlice(Text.AsMemory(), 2, 1, 2), new GenericTextRunProperties(Typeface.Default)); + case 3: + return new CustomDrawableRun(3); + default: + return null; + } + } + } + + private class CustomDrawableRun : DrawableTextRun + { + public CustomDrawableRun(int start) + { + Text = new ReadOnlySlice(new char[1], start, DefaultTextSourceLength); + } + + public override ReadOnlySlice Text { get; } + + public override Size Size => new(14, 14); + public override double Baseline => 14; + public override void Draw(DrawingContext drawingContext, Point origin) + { + } } @@ -517,6 +639,63 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting return rects; } + [Fact] + public void Should_Get_TextBounds() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var text = "0123".AsMemory(); + var ltrOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); + var rtlOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 1, CultureInfo.CurrentCulture); + + var textRuns = new List + { + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text), ltrOptions), defaultProperties), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length, text.Length), ltrOptions), defaultProperties), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2, text.Length), rtlOptions), defaultProperties), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 3, text.Length), ltrOptions), defaultProperties) + }; + + + var textSource = new FixedRunsTextSource(textRuns); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var textBounds = textLine.GetTextBounds(0, text.Length * 4); + + Assert.Equal(3, textBounds.Count); + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + } + } + + private class FixedRunsTextSource : ITextSource + { + private readonly IReadOnlyList _textRuns; + + public FixedRunsTextSource(IReadOnlyList textRuns) + { + _textRuns = textRuns; + } + + public TextRun? GetTextRun(int textSourceIndex) + { + foreach (var textRun in _textRuns) + { + if(textRun.Text.Start == textSourceIndex) + { + return textRun; + } + } + + return null; + } + } + private static IDisposable Start() { var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextShaperTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextShaperTests.cs index 57676ad581..94933e334d 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextShaperTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextShaperTests.cs @@ -15,12 +15,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting using (Start()) { var text = "\n\r\n".AsMemory(); - - var shapedBuffer = TextShaper.Current.ShapeText( - text, - Typeface.Default.GlyphTypeface, - 12, - CultureInfo.CurrentCulture, 0); + var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 12,0, CultureInfo.CurrentCulture); + var shapedBuffer = TextShaper.Current.ShapeText(text, options); Assert.Equal(shapedBuffer.Text.Length, text.Length); Assert.Equal(shapedBuffer.GlyphClusters.Count, text.Length); @@ -29,7 +25,21 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, shapedBuffer.GlyphClusters[2]); } } - + + [Fact] + public void Should_Apply_IncrementalTabWidth() + { + using (Start()) + { + var text = "\t".AsMemory(); + var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 12, 0, CultureInfo.CurrentCulture, 100); + var shapedBuffer = TextShaper.Current.ShapeText(text, options); + + Assert.Equal(shapedBuffer.Length, text.Length); + Assert.Equal(100, shapedBuffer.GlyphAdvances[0]); + } + } + private static IDisposable Start() { var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface diff --git a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs index 8ad3284490..5f8854b3ab 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; @@ -12,9 +11,13 @@ namespace Avalonia.UnitTests { public class HarfBuzzTextShaperImpl : ITextShaperImpl { - public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, - CultureInfo culture, sbyte bidiLevel) + public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { + var typeface = options.Typeface; + var fontRenderingEmSize = options.FontRenderingEmSize; + var bidiLevel = options.BidLevel; + var culture = options.Culture; + using (var buffer = new Buffer()) { buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length); diff --git a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs index fc22791102..c4b1e6c154 100644 --- a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs @@ -1,6 +1,4 @@ -using System.Globalization; -using Avalonia.Media; -using Avalonia.Media.TextFormatting; +using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; using Avalonia.Utilities; @@ -9,9 +7,12 @@ namespace Avalonia.UnitTests { public class MockTextShaperImpl : ITextShaperImpl { - public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, - CultureInfo culture, sbyte bidiLevel) + public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { + var typeface = options.Typeface; + var fontRenderingEmSize = options.FontRenderingEmSize; + var bidiLevel = options.BidLevel; + var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); for (var i = 0; i < shapedBuffer.Length;) From b99e1b6b2c0c410b9b093bc26fe382d88f9c84b3 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 29 Mar 2022 11:48:00 +0200 Subject: [PATCH 272/820] Cleanup attached properties --- .../DataGridTextColumn.cs | 23 ++++- src/Avalonia.Controls/Control.cs | 90 ------------------ .../Presenters/TextPresenter.cs | 55 +++++++++++ .../Primitives/TemplatedControl.cs | 91 +++++++++++++++++++ src/Avalonia.Controls/TextBlock.cs | 90 ++++++++++++++++++ .../Controls/Button.xaml | 2 +- .../Controls/CaptionButtons.xaml | 2 +- .../Controls/CheckBox.xaml | 2 +- .../Controls/DatePicker.xaml | 4 +- .../Controls/RepeatButton.xaml | 2 +- .../Controls/CaptionButtons.xaml | 2 +- .../Controls/ComboBox.xaml | 2 +- .../Controls/ComboBoxItem.xaml | 2 +- .../Controls/DatePicker.xaml | 4 +- .../Controls/RadioButton.xaml | 2 +- .../Controls/Slider.xaml | 4 +- .../Controls/TimePicker.xaml | 6 +- 17 files changed, 272 insertions(+), 111 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs index e7c8e67ff0..863910c226 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs @@ -33,7 +33,7 @@ namespace Avalonia.Controls /// Identifies the FontFamily dependency property. /// public static readonly AttachedProperty FontFamilyProperty = - TextBlock.FontFamilyProperty.AddOwner(); + TextElement.FontFamilyProperty.AddOwner(); /// /// Gets or sets the font name. @@ -48,7 +48,7 @@ namespace Avalonia.Controls /// Identifies the FontSize dependency property. /// public static readonly AttachedProperty FontSizeProperty = - TextBlock.FontSizeProperty.AddOwner(); + TextElement.FontSizeProperty.AddOwner(); /// /// Gets or sets the font size. @@ -65,7 +65,7 @@ namespace Avalonia.Controls /// Identifies the FontStyle dependency property. /// public static readonly AttachedProperty FontStyleProperty = - TextBlock.FontStyleProperty.AddOwner(); + TextElement.FontStyleProperty.AddOwner(); /// /// Gets or sets the font style. @@ -80,7 +80,7 @@ namespace Avalonia.Controls /// Identifies the FontWeight dependency property. /// public static readonly AttachedProperty FontWeightProperty = - TextBlock.FontWeightProperty.AddOwner(); + TextElement.FontWeightProperty.AddOwner(); /// /// Gets or sets the font weight or thickness. @@ -91,6 +91,21 @@ namespace Avalonia.Controls set => SetValue(FontWeightProperty, value); } + /// + /// Identifies the FontStretch dependency property. + /// + public static readonly AttachedProperty FontStretchProperty = + TextElement.FontStretchProperty.AddOwner(); + + /// + /// Gets or sets the font weight or thickness. + /// + public FontStretch FontStretch + { + get => GetValue(FontStretchProperty); + set => SetValue(FontStretchProperty, value); + } + /// /// Identifies the Foreground dependency property. /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 3eb1f1c472..45c0d2948d 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -68,42 +68,6 @@ namespace Avalonia.Controls public static readonly AttachedProperty FlowDirectionProperty = AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true); - /// - /// Defines the property. - /// - public static readonly AttachedProperty FontFamilyProperty = - TextElement.FontFamilyProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly AttachedProperty FontSizeProperty = - TextElement.FontSizeProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly AttachedProperty FontStyleProperty = - TextElement.FontStyleProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly AttachedProperty FontWeightProperty = - TextElement.FontWeightProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly AttachedProperty FontStretchProperty = - TextElement.FontStretchProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly AttachedProperty ForegroundProperty = - TextElement.ForegroundProperty.AddOwner(); - private DataTemplates? _dataTemplates; private IControl? _focusAdorner; private AutomationPeer? _automationPeer; @@ -162,60 +126,6 @@ namespace Avalonia.Controls set => SetValue(FlowDirectionProperty, value); } - /// - /// Gets or sets the font family. - /// - public FontFamily FontFamily - { - get { return GetValue(FontFamilyProperty); } - set { SetValue(FontFamilyProperty, value); } - } - - /// - /// Gets or sets the font size. - /// - public double FontSize - { - get { return GetValue(FontSizeProperty); } - set { SetValue(FontSizeProperty, value); } - } - - /// - /// Gets or sets the font style. - /// - public FontStyle FontStyle - { - get { return GetValue(FontStyleProperty); } - set { SetValue(FontStyleProperty, value); } - } - - /// - /// Gets or sets the font weight. - /// - public FontWeight FontWeight - { - get { return GetValue(FontWeightProperty); } - set { SetValue(FontWeightProperty, value); } - } - - /// - /// Gets or sets the font stretch. - /// - public FontStretch FontStretch - { - get { return GetValue(FontStretchProperty); } - set { SetValue(FontStretchProperty, value); } - } - - /// - /// Gets or sets a brush used to paint the text. - /// - public IBrush? Foreground - { - get { return GetValue(ForegroundProperty); } - set { SetValue(ForegroundProperty, value); } - } - /// /// Occurs when the user has completed a context input gesture, such as a right-click. /// diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 5267cc1d6e..ebd0196b80 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -8,6 +8,7 @@ using Avalonia.Utilities; using Avalonia.VisualTree; using Avalonia.Layout; using Avalonia.Media.Immutable; +using Avalonia.Controls.Documents; namespace Avalonia.Controls.Presenters { @@ -116,6 +117,60 @@ namespace Avalonia.Controls.Presenters set => SetAndRaise(TextProperty, ref _text, value); } + /// + /// Gets or sets the font family. + /// + public FontFamily FontFamily + { + get => TextElement.GetFontFamily(this); + set => TextElement.SetFontFamily(this, value); + } + + /// + /// Gets or sets the font size. + /// + public double FontSize + { + get => TextElement.GetFontSize(this); + set => TextElement.SetFontSize(this, value); + } + + /// + /// Gets or sets the font style. + /// + public FontStyle FontStyle + { + get => TextElement.GetFontStyle(this); + set => TextElement.SetFontStyle(this, value); + } + + /// + /// Gets or sets the font weight. + /// + public FontWeight FontWeight + { + get => TextElement.GetFontWeight(this); + set => TextElement.SetFontWeight(this, value); + } + + /// + /// Gets or sets the font stretch. + /// + public FontStretch FontStretch + { + get => TextElement.GetFontStretch(this); + set => TextElement.SetFontStretch(this, value); + } + + /// + /// Gets or sets a brush used to paint the text. + /// + public IBrush? Foreground + { + get => TextElement.GetForeground(this); + set => TextElement.SetForeground(this, value); + } + /// /// Gets or sets the control's text wrapping mode. /// diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 212aaa27d6..795c307c54 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Controls.Documents; using Avalonia.Controls.Templates; using Avalonia.Interactivity; using Avalonia.Logging; @@ -37,6 +38,42 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty FontFamilyProperty = + TextElement.FontFamilyProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontSizeProperty = + TextElement.FontSizeProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontStyleProperty = + TextElement.FontStyleProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontWeightProperty = + TextElement.FontWeightProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontStretchProperty = + TextElement.FontStretchProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ForegroundProperty = + TextElement.ForegroundProperty.AddOwner(); + /// /// Defines the property. /// @@ -119,6 +156,60 @@ namespace Avalonia.Controls.Primitives set { SetValue(CornerRadiusProperty, value); } } + /// + /// Gets or sets the font family used to draw the control's text. + /// + public FontFamily FontFamily + { + get { return GetValue(FontFamilyProperty); } + set { SetValue(FontFamilyProperty, value); } + } + + /// + /// Gets or sets the size of the control's text in points. + /// + public double FontSize + { + get { return GetValue(FontSizeProperty); } + set { SetValue(FontSizeProperty, value); } + } + + /// + /// Gets or sets the font style used to draw the control's text. + /// + public FontStyle FontStyle + { + get { return GetValue(FontStyleProperty); } + set { SetValue(FontStyleProperty, value); } + } + + /// + /// Gets or sets the font weight used to draw the control's text. + /// + public FontWeight FontWeight + { + get { return GetValue(FontWeightProperty); } + set { SetValue(FontWeightProperty, value); } + } + + /// + /// Gets or sets the font stretch used to draw the control's text. + /// + public FontStretch FontStretch + { + get { return GetValue(FontStretchProperty); } + set { SetValue(FontStretchProperty, value); } + } + + /// + /// Gets or sets the brush used to draw the control's text and other foreground elements. + /// + public IBrush? Foreground + { + get { return GetValue(ForegroundProperty); } + set { SetValue(ForegroundProperty, value); } + } + /// /// Gets or sets the padding placed between the border of the control and its content. /// diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 8b8f8bac37..2abaf14419 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -28,6 +28,42 @@ namespace Avalonia.Controls public static readonly StyledProperty PaddingProperty = Decorator.PaddingProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty FontFamilyProperty = + TextElement.FontFamilyProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontSizeProperty = + TextElement.FontSizeProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontStyleProperty = + TextElement.FontStyleProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontWeightProperty = + TextElement.FontWeightProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontStretchProperty = + TextElement.FontStretchProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ForegroundProperty = + TextElement.ForegroundProperty.AddOwner(); + /// /// DependencyProperty for property. /// @@ -174,6 +210,60 @@ namespace Avalonia.Controls [Content] public InlineCollection Inlines { get; } + /// + /// Gets or sets the font family used to draw the control's text. + /// + public FontFamily FontFamily + { + get { return GetValue(FontFamilyProperty); } + set { SetValue(FontFamilyProperty, value); } + } + + /// + /// Gets or sets the size of the control's text in points. + /// + public double FontSize + { + get { return GetValue(FontSizeProperty); } + set { SetValue(FontSizeProperty, value); } + } + + /// + /// Gets or sets the font style used to draw the control's text. + /// + public FontStyle FontStyle + { + get { return GetValue(FontStyleProperty); } + set { SetValue(FontStyleProperty, value); } + } + + /// + /// Gets or sets the font weight used to draw the control's text. + /// + public FontWeight FontWeight + { + get { return GetValue(FontWeightProperty); } + set { SetValue(FontWeightProperty, value); } + } + + /// + /// Gets or sets the font stretch used to draw the control's text. + /// + public FontStretch FontStretch + { + get { return GetValue(FontStretchProperty); } + set { SetValue(FontStretchProperty, value); } + } + + /// + /// Gets or sets the brush used to draw the control's text and other foreground elements. + /// + public IBrush? Foreground + { + get { return GetValue(ForegroundProperty); } + set { SetValue(ForegroundProperty, value); } + } + /// /// Gets or sets the height of each line of content. /// diff --git a/src/Avalonia.Themes.Default/Controls/Button.xaml b/src/Avalonia.Themes.Default/Controls/Button.xaml index a8509f2976..3f8a94b005 100644 --- a/src/Avalonia.Themes.Default/Controls/Button.xaml +++ b/src/Avalonia.Themes.Default/Controls/Button.xaml @@ -18,7 +18,7 @@ Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" RecognizesAccessKey="True" - Foreground="{TemplateBinding Foreground}" + TextElement.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml index af56698ad5..9c1d4d0b1d 100644 --- a/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml @@ -4,7 +4,7 @@ - + - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index c8267b57d9..4208d9665f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -52,7 +52,7 @@ Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + - + - - From a06d287fb956ace8a4c41f5c9069a9d4479adf71 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 20:25:55 -0400 Subject: [PATCH 281/820] Switch ExperimentalAcrylicMaterial to using new HsvColor --- .../Media/ExperimentalAcrylicMaterial.cs | 62 +++---------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs b/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs index cdea5f5fa3..0e485d0db8 100644 --- a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs @@ -142,52 +142,6 @@ namespace Avalonia.Media Color IExperimentalAcrylicMaterial.TintColor => _effectiveTintColor; - private struct HsvColor - { - public float Hue { get; set; } - public float Saturation { get; set; } - public float Value { get; set; } - } - - private static HsvColor RgbToHsv(Color color) - { - var r = color.R / 255.0f; - var g = color.G / 255.0f; - var b = color.B / 255.0f; - var max = Math.Max(r, Math.Max(g, b)); - var min = Math.Min(r, Math.Min(g, b)); - - float h, s, v; - h = v = max; - - var d = max - min; - s = max == 0 ? 0 : d / max; - - if (max == min) - { - h = 0; // achromatic - } - else - { - if (max == r) - { - h = (g - b) / d + (g < b ? 6 : 0); - } - else if (max == g) - { - h = (b - r) / d + 2; - } - else if (max == b) - { - h = (r - g) / d + 4; - } - - h /= 6; - } - - return new HsvColor { Hue = h, Saturation = s, Value = v }; - } - private static Color GetEffectiveTintColor(Color tintColor, double tintOpacity) { // Update tintColor's alpha with the combined opacity value @@ -198,7 +152,7 @@ namespace Avalonia.Media private static double GetTintOpacityModifier(Color tintColor) { - // This method supresses the maximum allowable tint opacity depending on the luminosity and saturation of a color by + // This method suppresses the maximum allowable tint opacity depending on the luminosity and saturation of a color by // compressing the range of allowable values - for example, a user-defined value of 100% will be mapped to 45% for pure // white (100% luminosity), 85% for pure black (0% luminosity), and 90% for pure gray (50% luminosity). The intensity of // the effect increases linearly as luminosity deviates from 50%. After this effect is calculated, we cancel it out @@ -210,22 +164,22 @@ namespace Avalonia.Media const double midPointMaxOpacity = 0.45; // 50% luminosity const double blackMaxOpacity = 0.45; // 0% luminosity - var hsv = RgbToHsv(tintColor); + var hsv = tintColor.ToHsv(); double opacityModifier = midPointMaxOpacity; - if (hsv.Value != midPoint) + if (hsv.V != midPoint) { // Determine maximum suppression amount double lowestMaxOpacity = midPointMaxOpacity; double maxDeviation = midPoint; - if (hsv.Value > midPoint) + if (hsv.V > midPoint) { lowestMaxOpacity = whiteMaxOpacity; // At white (100% hsvV) maxDeviation = 1 - maxDeviation; } - else if (hsv.Value < midPoint) + else if (hsv.V < midPoint) { lowestMaxOpacity = blackMaxOpacity; // At black (0% hsvV) } @@ -233,14 +187,14 @@ namespace Avalonia.Media double maxOpacitySuppression = midPointMaxOpacity - lowestMaxOpacity; // Determine normalized deviation from the midpoint - double deviation = Math.Abs(hsv.Value - midPoint); + double deviation = Math.Abs(hsv.V - midPoint); double normalizedDeviation = deviation / maxDeviation; // If we have saturation, reduce opacity suppression to allow that color to come through more - if (hsv.Saturation > 0) + if (hsv.S > 0) { // Dampen opacity suppression based on how much saturation there is - maxOpacitySuppression *= Math.Max(1 - (hsv.Saturation * 2), 0.0); + maxOpacitySuppression *= Math.Max(1 - (hsv.S * 2), 0.0); } double opacitySuppression = maxOpacitySuppression * normalizedDeviation; From 5e2be4045e549c21407389d6c4302e09cd5d9e96 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 21:31:33 -0400 Subject: [PATCH 282/820] Make sure Color uses Invariant .ToString() converting to hex --- src/Avalonia.Visuals/Media/Color.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index e974bbb100..aa730b3219 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -258,7 +258,7 @@ namespace Avalonia.Media public override string ToString() { uint rgb = ToUint32(); - return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb:x8}"; + return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb.ToString("x8", CultureInfo.InvariantCulture)}"; } /// From 465b3e543972d1f84d7b654256b6a439e75190bd Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 21:35:57 -0400 Subject: [PATCH 283/820] Implement HsvColor.ToString() and TryParse() --- src/Avalonia.Visuals/Media/HsvColor.cs | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 8b2f10d088..23cec7995d 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -4,6 +4,8 @@ // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; +using System.Globalization; +using System.Text; using Avalonia.Utilities; namespace Avalonia.Media @@ -155,6 +157,109 @@ namespace Avalonia.Media return HsvColor.ToRgb(H, S, V, A); } + /// + public override string ToString() + { + var sb = new StringBuilder(); + + // Use a format similar to HSL in HTML/CSS "hsla(0, 100%, 50%, 0.5)" + // + // However: + // - To ensure precision is never lost, allow decimal places + // - To maintain numerical consistency do not use percent + // + // Example: + // + // hsva(hue, saturation, value, alpha) + // hsva(230, 1.0, 0.5, 1.0) + // + // Where: + // + // hue : integer from 0 to 360 + // saturation : double from 0.0 to 1.0 + // (HTML uses percentages) + // value : double from 0.0 to 1.0 + // (HTML uses percentages) + // alpha : double from 0.0 to 1.0 + // (HTML does not use percent for alpha) + + sb.Append("hsva("); + sb.Append(H.ToString(CultureInfo.InvariantCulture)); + sb.Append(", "); + sb.Append(S.ToString(CultureInfo.InvariantCulture)); + sb.Append(", "); + sb.Append(V.ToString(CultureInfo.InvariantCulture)); + sb.Append(", "); + sb.Append(A.ToString(CultureInfo.InvariantCulture)); + sb.Append(')'); + + return sb.ToString(); + } + + /// + /// Parses an HSV color string. + /// + /// The HSV color string to parse. + /// The parsed HSV color. + /// True if parsing was successful; otherwise, false. + public static bool TryParse(string s, out HsvColor hsvColor) + { + hsvColor = default; + + if (s is null) + { + return false; + } + + string workingString = s.Trim(); + + if (workingString.Length == 0 || + workingString.IndexOf(",", StringComparison.Ordinal) < 0) + { + return false; + } + + if (workingString.StartsWith("hsva(", StringComparison.OrdinalIgnoreCase) && + workingString.EndsWith(")", StringComparison.Ordinal) && + workingString.Length > 6) + { + workingString = workingString.Substring(5, workingString.Length - 6); + } + + if (workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && + workingString.EndsWith(")", StringComparison.Ordinal) && + workingString.Length > 5) + { + workingString = workingString.Substring(4, workingString.Length - 5); + } + + string[] components = workingString.Split(','); + + if (components.Length == 3) // HSV + { + if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && + double.TryParse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double saturation) && + double.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double value)) + { + hsvColor = new HsvColor(1.0, hue, saturation, value); + return true; + } + } + else if (components.Length == 4) // HSVA + { + if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && + double.TryParse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double saturation) && + double.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double value) && + double.TryParse(components[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double alpha)) + { + hsvColor = new HsvColor(alpha, hue, saturation, value); + return true; + } + } + + return false; + } + /// /// Creates a new from individual color channel values. /// From 6a49977022e4a5b281dc87cce9ab8e45d038dbc0 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 21:41:45 -0400 Subject: [PATCH 284/820] Fix comments --- src/Avalonia.Visuals/Media/HsvColor.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 23cec7995d..b5a7f883e7 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -175,13 +175,13 @@ namespace Avalonia.Media // // Where: // - // hue : integer from 0 to 360 - // saturation : double from 0.0 to 1.0 - // (HTML uses percentages) - // value : double from 0.0 to 1.0 - // (HTML uses percentages) - // alpha : double from 0.0 to 1.0 - // (HTML does not use percent for alpha) + // hue : double from 0 to 360 + // saturation : double from 0 to 1 + // (HTML uses a percentage) + // value : double from 0 to 1 + // (HTML uses a percentage) + // alpha : double from 0 to 1 + // (HTML does not use a percentage for alpha) sb.Append("hsva("); sb.Append(H.ToString(CultureInfo.InvariantCulture)); From b085b867d1a02e9cb1c05277c119ee49d05288c0 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 23:02:58 -0400 Subject: [PATCH 285/820] Check string length first --- src/Avalonia.Visuals/Media/HsvColor.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index b5a7f883e7..523492b66b 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -219,16 +219,16 @@ namespace Avalonia.Media return false; } - if (workingString.StartsWith("hsva(", StringComparison.OrdinalIgnoreCase) && - workingString.EndsWith(")", StringComparison.Ordinal) && - workingString.Length > 6) + if (workingString.Length > 6 && + workingString.StartsWith("hsva(", StringComparison.OrdinalIgnoreCase) && + workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && - workingString.EndsWith(")", StringComparison.Ordinal) && - workingString.Length > 5) + if (workingString.Length > 5 && + workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && + workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); } From c8299abd014f0928b0c7d3d21d395cab7bbec865 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 23:15:42 -0400 Subject: [PATCH 286/820] Add HsvColor.Parse() method --- src/Avalonia.Visuals/Media/HsvColor.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 523492b66b..3a15488141 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -200,7 +200,27 @@ namespace Avalonia.Media /// Parses an HSV color string. /// /// The HSV color string to parse. - /// The parsed HSV color. + /// The parsed . + public static HsvColor Parse(string s) + { + if (s is null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (TryParse(s, out HsvColor hsvColor)) + { + return hsvColor; + } + + throw new FormatException($"Invalid HSV color string: '{s}'."); + } + + /// + /// Parses an HSV color string. + /// + /// The HSV color string to parse. + /// The parsed . /// True if parsing was successful; otherwise, false. public static bool TryParse(string s, out HsvColor hsvColor) { From 5d43a1e66418e3e4a19d229d63fd2c4a12d1145d Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 30 Mar 2022 17:22:34 +0200 Subject: [PATCH 287/820] Try to fix hit testing for embedded runs --- .../Media/TextFormatting/TextLineImpl.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index fe52fbeef5..09e48f91d8 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -186,6 +186,7 @@ namespace Avalonia.Media.TextFormatting // process hit that happens within the line var characterHit = new CharacterHit(); + var currentPosition = TextRange.Start; foreach (var currentRun in _textRuns) { @@ -200,11 +201,11 @@ namespace Avalonia.Media.TextFormatting { if(distance < currentRun.Size.Width / 2) { - characterHit = new CharacterHit(currentRun.Text.Start); + characterHit = new CharacterHit(currentPosition); } else { - characterHit = new CharacterHit(currentRun.Text.Start, currentRun.Text.Length); + characterHit = new CharacterHit(currentPosition, currentRun.TextSourceLength); } break; } @@ -216,6 +217,7 @@ namespace Avalonia.Media.TextFormatting } distance -= currentRun.Size.Width; + currentPosition += currentRun.TextSourceLength; } return characterHit; @@ -225,8 +227,8 @@ namespace Avalonia.Media.TextFormatting public override double GetDistanceFromCharacterHit(CharacterHit characterHit) { var characterIndex = characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0); - var currentDistance = Start; + var currentPosition = TextRange.Start; GlyphRun? lastRun = null; @@ -321,7 +323,7 @@ namespace Avalonia.Media.TextFormatting } default: { - if(characterIndex == textRun.Text.Start) + if(characterIndex == currentPosition) { return currentDistance; } @@ -332,6 +334,7 @@ namespace Avalonia.Media.TextFormatting //No hit hit found so we add the full width currentDistance += textRun.Size.Width; + currentPosition += textRun.TextSourceLength; } return currentDistance; From 55710e85148a1d5619397753be6a131a34bb98c6 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Wed, 30 Mar 2022 21:12:34 +0300 Subject: [PATCH 288/820] Used ToArray where it benefits over ToList --- src/Avalonia.Animation/Easing/Easing.cs | 2 +- src/Avalonia.Base/Collections/AvaloniaDictionary.cs | 2 +- src/Avalonia.Base/Collections/AvaloniaList.cs | 2 +- src/Avalonia.Base/Data/Core/PropertyPath.cs | 2 +- .../Utilities/AvaloniaResourcesIndex.cs | 2 +- .../GenerateAvaloniaResourcesTask.cs | 2 +- .../Collections/DataGridCollectionView.cs | 2 +- .../ClassicDesktopStyleApplicationLifetime.cs | 2 +- src/Avalonia.Controls/AutoCompleteBox.cs | 4 ++-- .../Generators/ItemContainerGenerator.cs | 12 ++++++------ .../NumericUpDown/NumericUpDown.cs | 2 +- .../ManagedPopupPositionerPopupImplHelper.cs | 5 +++-- .../Primitives/SelectingItemsControl.cs | 10 +++++----- .../Selection/InternalSelectionModel.cs | 4 ++-- src/Avalonia.Controls/TreeView.cs | 2 +- .../Utils/CollectionChangedEventManager.cs | 4 ++-- src/Avalonia.Controls/Window.cs | 6 +++--- .../ViewModels/ControlDetailsViewModel.cs | 6 +++--- src/Avalonia.Input/FocusManager.cs | 2 +- src/Avalonia.Input/TouchDevice.cs | 2 +- .../Animation/CompositePageTransition.cs | 2 +- src/Avalonia.Visuals/Media/GradientStops.cs | 2 +- src/Avalonia.X11/Glx/GlxDisplay.cs | 13 ++++++------- src/Avalonia.X11/X11Globals.cs | 4 ++-- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 5 +++-- 25 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Animation/Easing/Easing.cs index 2f4b93dab1..c721772f3e 100644 --- a/src/Avalonia.Animation/Easing/Easing.cs +++ b/src/Avalonia.Animation/Easing/Easing.cs @@ -39,7 +39,7 @@ namespace Avalonia.Animation.Easings var derivedTypes = typeof(Easing).Assembly.GetTypes() .Where(p => p.Namespace == s_thisType.Namespace) .Where(p => p.IsSubclassOf(s_thisType)) - .Select(p => p).ToList(); + .Select(p => p); foreach (var easingType in derivedTypes) _easingTypes.Add(easingType.Name, easingType); diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index 2fe68e824d..750fb263f5 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -123,7 +123,7 @@ namespace Avalonia.Collections { var e = new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, - old.ToList(), + old.ToArray(), -1); CollectionChanged(this, e); } diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 6414328e90..9972e72dd4 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -222,7 +222,7 @@ namespace Avalonia.Collections { var e = ResetBehavior == ResetBehavior.Reset ? EventArgsCache.ResetCollectionChanged : - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _inner.ToList(), 0); + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _inner.ToArray(), 0); _inner.Clear(); diff --git a/src/Avalonia.Base/Data/Core/PropertyPath.cs b/src/Avalonia.Base/Data/Core/PropertyPath.cs index 665953c4a1..f5b3f92353 100644 --- a/src/Avalonia.Base/Data/Core/PropertyPath.cs +++ b/src/Avalonia.Base/Data/Core/PropertyPath.cs @@ -10,7 +10,7 @@ namespace Avalonia.Data.Core public PropertyPath(IEnumerable elements) { - Elements = elements.ToList(); + Elements = elements.ToArray(); } } diff --git a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs index 09e408cb42..bd3b86f06d 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs @@ -23,7 +23,7 @@ namespace Avalonia.Utilities var assetDoc = XDocument.Load(stream); XNamespace assetNs = assetDoc.Root!.Attribute("xmlns")!.Value; - List entries= + List entries = (from entry in assetDoc.Root.Element(assetNs + "Entries")!.Elements(assetNs + "AvaloniaResourcesIndexEntry") select new AvaloniaResourcesIndexEntry { diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs index ae2bf99d1e..c20b2f656e 100644 --- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs +++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs @@ -105,7 +105,7 @@ namespace Avalonia.Build.Tasks { var typeToXamlIndex = new Dictionary(); - foreach (var s in sources.ToList()) + foreach (var s in sources.ToArray()) { if (s.Path.ToLowerInvariant().EndsWith(".xaml") || s.Path.ToLowerInvariant().EndsWith(".paml") || s.Path.ToLowerInvariant().EndsWith(".axaml")) { diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs index fe6acdc532..906ec661ae 100644 --- a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs +++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs @@ -3946,7 +3946,7 @@ namespace Avalonia.Collections { sort.Initialize(itemType); - if(seq is IOrderedEnumerable orderedEnum) + if (seq is IOrderedEnumerable orderedEnum) { seq = sort.ThenBy(orderedEnum); } diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 76e2d3a161..c59458311c 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -65,7 +65,7 @@ namespace Avalonia.Controls.ApplicationLifetimes /// public Window? MainWindow { get; set; } - public IReadOnlyList Windows => _windows.ToList(); + public IReadOnlyList Windows => _windows.ToArray(); private void HandleWindowClosed(Window window) { diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 930e250334..f47e4a1713 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -2180,7 +2180,7 @@ namespace Avalonia.Controls } // Store a local cached copy of the data - _items = newValue == null ? null : new List(newValue.Cast().ToList()); + _items = newValue == null ? null : new List(newValue.Cast()); // Clear and set the view on the selection adapter ClearView(); @@ -2239,7 +2239,7 @@ namespace Avalonia.Controls ClearView(); if (Items != null) { - _items = new List(Items.Cast().ToList()); + _items = new List(Items.Cast()); } } diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index 79a9e16879..a76dcbe9c8 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -82,7 +82,7 @@ namespace Avalonia.Controls.Generators { var toMove = _containers.Where(x => x.Key >= index) .OrderByDescending(x => x.Key) - .ToList(); + .ToArray(); foreach (var i in toMove) { @@ -111,7 +111,7 @@ namespace Avalonia.Controls.Generators } var toMove = _containers.Where(x => x.Key >= startingIndex) - .OrderBy(x => x.Key).ToList(); + .OrderBy(x => x.Key).ToArray(); foreach (var i in toMove) { @@ -122,9 +122,9 @@ namespace Avalonia.Controls.Generators Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result)); - if (toMove.Count > 0) + if (toMove.Length > 0) { - var containers = toMove.Select(x => x.Value).ToList(); + var containers = toMove.Select(x => x.Value).ToArray(); Recycled?.Invoke(this, new ItemContainerEventArgs(containers[0].Index, containers)); } } @@ -138,10 +138,10 @@ namespace Avalonia.Controls.Generators /// public virtual IEnumerable Clear() { - var result = Containers.ToList(); + var result = Containers.ToArray(); _containers.Clear(); - if (result.Count > 0) + if (result.Length > 0) { Dematerialized?.Invoke(this, new ItemContainerEventArgs(0, result)); } diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index f67377b310..91f21f848f 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -1051,7 +1051,7 @@ namespace Avalonia.Controls var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); var textSpecialCharacters = text.Where(c => !char.IsDigit(c)); // same non-digit characters on currentValueText and new text => remove them on new Text to parse it again. - if (currentValueTextSpecialCharacters.Except(textSpecialCharacters).ToList().Count == 0) + if (!currentValueTextSpecialCharacters.Except(textSpecialCharacters).Any()) { foreach (var character in textSpecialCharacters) { diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs index 91ed5d975d..51a21323d9 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs @@ -23,8 +23,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning public IReadOnlyList Screens => - _parent.Screen.AllScreens.Select(s => new ManagedPopupPositionerScreenInfo( - s.Bounds.ToRect(1), s.WorkingArea.ToRect(1))).ToList(); + _parent.Screen.AllScreens + .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.WorkingArea.ToRect(1))) + .ToArray(); public Rect ParentClientAreaScreenGeometry { diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index c690726e71..cec02c7ae9 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -292,11 +292,11 @@ namespace Avalonia.Controls.Primitives "collection is different to the Items on the control."); } - var oldSelection = _selection?.SelectedItems.ToList(); + var oldSelection = _selection?.SelectedItems.ToArray(); DeinitializeSelectionModel(_selection); _selection = value; - if (oldSelection?.Count > 0) + if (oldSelection?.Length > 0) { RaiseEvent(new SelectionChangedEventArgs( SelectionChangedEvent, @@ -845,8 +845,8 @@ namespace Avalonia.Controls.Primitives { var ev = new SelectionChangedEventArgs( SelectionChangedEvent, - e.DeselectedItems.ToList(), - e.SelectedItems.ToList()); + e.DeselectedItems.ToArray(), + e.SelectedItems.ToArray()); RaiseEvent(ev); } } @@ -988,7 +988,7 @@ namespace Avalonia.Controls.Primitives RaiseEvent(new SelectionChangedEventArgs( SelectionChangedEvent, Array.Empty(), - Selection.SelectedItems.ToList())); + Selection.SelectedItems.ToArray())); } } diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs index 40c6f63ed8..d92ffb0d1a 100644 --- a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs +++ b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs @@ -182,8 +182,8 @@ namespace Avalonia.Controls.Selection try { var items = WritableSelectedItems; - var deselected = e.DeselectedItems.ToList(); - var selected = e.SelectedItems.ToList(); + var deselected = e.DeselectedItems.ToArray(); + var selected = e.SelectedItems.ToArray(); _ignoreSelectedItemsChanges = true; diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 9a276e74d2..1d806913dd 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -849,7 +849,7 @@ namespace Avalonia.Controls /// The desired items. private static void SynchronizeItems(IList items, IEnumerable desired) { - var list = items.Cast().ToList(); + var list = items.Cast(); var toRemove = list.Except(desired).ToList(); var toAdd = desired.Except(list).ToList(); diff --git a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs index 0b1c4fc90e..9d13daa453 100644 --- a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs +++ b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Utils static void Notify( INotifyCollectionChanged incc, NotifyCollectionChangedEventArgs args, - List> listeners) + WeakReference[] listeners) { foreach (var l in listeners) { @@ -132,7 +132,7 @@ namespace Avalonia.Controls.Utils } } - var l = Listeners.ToList(); + var l = Listeners.ToArray(); if (Dispatcher.UIThread.CheckAccess()) { diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index feacc3e63a..700dfa6dd1 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -258,7 +258,7 @@ namespace Avalonia.Controls /// /// Gets a collection of child windows owned by this window. /// - public IReadOnlyList OwnedWindows => _children.Select(x => x.child).ToList(); + public IReadOnlyList OwnedWindows => _children.Select(x => x.child).ToArray(); /// /// Gets or sets a value indicating how the window will size itself to fit its content. @@ -527,7 +527,7 @@ namespace Avalonia.Controls private void CloseInternal() { - foreach (var (child, _) in _children.ToList()) + foreach (var (child, _) in _children.ToArray()) { child.CloseInternal(); } @@ -551,7 +551,7 @@ namespace Avalonia.Controls bool canClose = true; - foreach (var (child, _) in _children.ToList()) + foreach (var (child, _) in _children.ToArray()) { if (child.ShouldCancelClose(args)) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index a1fd8ed028..a1fd425571 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -18,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels internal class ControlDetailsViewModel : ViewModelBase, IDisposable { private readonly IAvaloniaObject _avaloniaObject; - private IDictionary>? _propertyIndex; + private IDictionary? _propertyIndex; private PropertyViewModel? _selectedProperty; private DataGridCollectionView? _propertiesView; private bool _snapshotStyles; @@ -472,9 +472,9 @@ namespace Avalonia.Diagnostics.ViewModels .Concat(GetClrProperties(o, _showImplementedInterfaces)) .OrderBy(x => x, PropertyComparer.Instance) .ThenBy(x => x.Name) - .ToList(); + .ToArray(); - _propertyIndex = properties.GroupBy(x => x.Key).ToDictionary(x => x.Key, x => x.ToList()); + _propertyIndex = properties.GroupBy(x => x.Key).ToDictionary(x => x.Key, x => x.ToArray()); var view = new DataGridCollectionView(properties); view.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group))); diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs index 30301788bc..c7b5b8e27f 100644 --- a/src/Avalonia.Input/FocusManager.cs +++ b/src/Avalonia.Input/FocusManager.cs @@ -73,7 +73,7 @@ namespace Avalonia.Input else if (Current != null) { // If control is null, set focus to the topmost focus scope. - foreach (var scope in GetFocusScopeAncestors(Current).Reverse().ToList()) + foreach (var scope in GetFocusScopeAncestors(Current).Reverse().ToArray()) { if (scope != Scope && _focusScopes.TryGetValue(scope, out var element) && diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs index 12ad182bf8..ed7ec5465c 100644 --- a/src/Avalonia.Input/TouchDevice.cs +++ b/src/Avalonia.Input/TouchDevice.cs @@ -114,7 +114,7 @@ namespace Avalonia.Input { if (_disposed) return; - var values = _pointers.Values.ToList(); + var values = _pointers.Values.ToArray(); _pointers.Clear(); _disposed = true; foreach (var p in values) diff --git a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs b/src/Avalonia.Visuals/Animation/CompositePageTransition.cs index 2a6eae3e38..62119a0051 100644 --- a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs +++ b/src/Avalonia.Visuals/Animation/CompositePageTransition.cs @@ -41,7 +41,7 @@ namespace Avalonia.Animation { var transitionTasks = PageTransitions .Select(transition => transition.Start(from, to, forward, cancellationToken)) - .ToList(); + .ToArray(); return Task.WhenAll(transitionTasks); } } diff --git a/src/Avalonia.Visuals/Media/GradientStops.cs b/src/Avalonia.Visuals/Media/GradientStops.cs index efc11bacd6..c3d0bb5ceb 100644 --- a/src/Avalonia.Visuals/Media/GradientStops.cs +++ b/src/Avalonia.Visuals/Media/GradientStops.cs @@ -17,7 +17,7 @@ namespace Avalonia.Media public IReadOnlyList ToImmutable() { - return this.Select(x => new ImmutableGradientStop(x.Offset, x.Color)).ToList(); + return this.Select(x => new ImmutableGradientStop(x.Offset, x.Color)).ToArray(); } } } diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index fcdc10e999..1e70608168 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/src/Avalonia.X11/Glx/GlxDisplay.cs @@ -9,7 +9,7 @@ namespace Avalonia.X11.Glx unsafe class GlxDisplay { private readonly X11Info _x11; - private readonly List _probeProfiles; + private readonly GlVersion[] _probeProfiles; private readonly IntPtr _fbconfig; private readonly XVisualInfo* _visual; private string[] _displayExtensions; @@ -21,7 +21,7 @@ namespace Avalonia.X11.Glx public GlxDisplay(X11Info x11, IList probeProfiles) { _x11 = x11; - _probeProfiles = probeProfiles.ToList(); + _probeProfiles = probeProfiles.ToArray(); _displayExtensions = Glx.GetExtensions(_x11.Display); var baseAttribs = new[] @@ -76,10 +76,10 @@ namespace Avalonia.X11.Glx if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0) stencilSize = stencil; - var pbuffers = Enumerable.Range(0, 2).Select(_ => Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] - { - GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 - })).ToList(); + var attributes = new[] { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 }; + + Glx.CreatePbuffer(_x11.Display, _fbconfig, attributes); + Glx.CreatePbuffer(_x11.Display, _fbconfig, attributes); XLib.XFlush(_x11.Display); @@ -104,7 +104,6 @@ namespace Avalonia.X11.Glx $"Renderer '{glInterface.Renderer}' is blacklisted by '{item}'"); } } - } IntPtr CreatePBuffer() diff --git a/src/Avalonia.X11/X11Globals.cs b/src/Avalonia.X11/X11Globals.cs index 057693f810..39834a44b3 100644 --- a/src/Avalonia.X11/X11Globals.cs +++ b/src/Avalonia.X11/X11Globals.cs @@ -41,7 +41,7 @@ namespace Avalonia.X11 { _wmName = value; // The collection might change during enumeration - foreach (var s in _subscribers.ToList()) + foreach (var s in _subscribers.ToArray()) s.WmChanged(value); } } @@ -69,7 +69,7 @@ namespace Avalonia.X11 { _isCompositionEnabled = value; // The collection might change during enumeration - foreach (var s in _subscribers.ToList()) + foreach (var s in _subscribers.ToArray()) s.CompositionChanged(value); } } diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 23395dd9b5..6484ae6c54 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -217,8 +217,9 @@ namespace Avalonia.Win32 } public IReadOnlyList Screens => - _hiddenWindow.Screens.All.Select(s => new ManagedPopupPositionerScreenInfo( - s.Bounds.ToRect(1), s.Bounds.ToRect(1))).ToList(); + _hiddenWindow.Screens.All + .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.Bounds.ToRect(1))) + .ToArray(); public Rect ParentClientAreaScreenGeometry { From 55d28216a7708742f714950284aa13099900678d Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 31 Mar 2022 08:17:47 +0200 Subject: [PATCH 289/820] Use ForgroundProperty directly where possible --- .../Views/LayoutExplorerView.axaml | 2 +- .../Controls/Button.xaml | 2 +- .../Controls/CaptionButtons.xaml | 2 +- .../Controls/CheckBox.xaml | 2 +- .../Controls/DatePicker.xaml | 4 +- .../Controls/RepeatButton.xaml | 2 +- .../Controls/SplitButton.xaml | 24 +++++----- .../Controls/TimePicker.xaml | 4 +- .../Controls/Button.xaml | 2 +- .../Controls/CheckBox.xaml | 6 +-- .../Controls/ComboBox.xaml | 18 ++++---- .../Controls/ComboBoxItem.xaml | 2 +- .../Controls/DataValidationErrors.xaml | 2 +- .../Controls/DatePicker.xaml | 4 +- .../Controls/ListBox.xaml | 2 +- .../Controls/MenuItem.xaml | 10 ++--- .../Controls/RadioButton.xaml | 2 +- .../Controls/RepeatButton.xaml | 2 +- .../Controls/Slider.xaml | 6 +-- .../Controls/SplitButton.xaml | 24 +++++----- .../Controls/TabItem.xaml | 4 +- .../Controls/TabStripItem.xaml | 4 +- .../Controls/TimePicker.xaml | 6 +-- .../Controls/ToggleButton.xaml | 2 +- .../Controls/TreeViewItem.xaml | 2 +- .../Media/TextFormatting/TextLineImpl.cs | 45 +++++++++---------- .../Media/TextFormatting/TextLineTests.cs | 4 +- 27 files changed, 94 insertions(+), 95 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml b/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml index a47c6a0b10..c404a5c382 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml @@ -47,7 +47,7 @@ diff --git a/src/Avalonia.Themes.Default/Controls/Button.xaml b/src/Avalonia.Themes.Default/Controls/Button.xaml index 3f8a94b005..a2971c3ff6 100644 --- a/src/Avalonia.Themes.Default/Controls/Button.xaml +++ b/src/Avalonia.Themes.Default/Controls/Button.xaml @@ -3,7 +3,7 @@ - + diff --git a/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml index 9c1d4d0b1d..be6642467f 100644 --- a/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml @@ -1,6 +1,6 @@ diff --git a/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml index 9520857b02..47398966f7 100644 --- a/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml @@ -6,7 +6,7 @@ Value="{DynamicResource ThemeBorderLowBrush}" /> - diff --git a/src/Avalonia.Themes.Default/Controls/SplitButton.xaml b/src/Avalonia.Themes.Default/Controls/SplitButton.xaml index 9af1c073cd..2d65ea2b7b 100644 --- a/src/Avalonia.Themes.Default/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/SplitButton.xaml @@ -56,7 +56,7 @@ @@ -160,7 +160,7 @@ @@ -172,7 +172,7 @@ @@ -184,7 +184,7 @@ @@ -196,7 +196,7 @@ @@ -208,7 +208,7 @@ @@ -219,7 +219,7 @@ @@ -230,7 +230,7 @@ @@ -242,7 +242,7 @@ @@ -254,7 +254,7 @@ @@ -266,6 +266,6 @@ diff --git a/src/Avalonia.Themes.Default/Controls/TimePicker.xaml b/src/Avalonia.Themes.Default/Controls/TimePicker.xaml index 5a6eb15f2d..0a5147e335 100644 --- a/src/Avalonia.Themes.Default/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Default/Controls/TimePicker.xaml @@ -33,7 +33,7 @@ @@ -185,11 +185,11 @@ @@ -203,11 +203,11 @@ @@ -216,11 +216,11 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml index 19e5e29c58..9b8fda0874 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml @@ -18,7 +18,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml index 8840397843..0e8bde43c8 100644 --- a/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml @@ -32,7 +32,7 @@ @@ -136,7 +136,7 @@ @@ -148,7 +148,7 @@ @@ -160,7 +160,7 @@ @@ -172,7 +172,7 @@ @@ -184,7 +184,7 @@ @@ -195,7 +195,7 @@ @@ -206,7 +206,7 @@ @@ -218,7 +218,7 @@ @@ -230,7 +230,7 @@ @@ -242,6 +242,6 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml index 20039fd71b..df8f691490 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml @@ -19,7 +19,7 @@ - + @@ -61,7 +61,7 @@ - - - - - - - - - - - /// - /// The date to display. The default - /// . + /// The date to display. The default is . /// - /// + /// /// The specified date is not in the range defined by - /// + /// /// and - /// . + /// . /// public DateTime DisplayDate { @@ -215,11 +214,11 @@ namespace Avalonia.Controls /// /// /// The specified date is not in the range defined by - /// + /// /// and - /// , + /// , /// or the specified date is in the - /// + /// /// collection. /// public DateTime? SelectedDate @@ -233,9 +232,9 @@ namespace Avalonia.Controls /// /// /// The format that is used to display the selected date. The default is - /// . + /// . /// - /// + /// /// An specified format is not valid. /// public CalendarDatePickerFormat SelectedDateFormat @@ -251,18 +250,16 @@ namespace Avalonia.Controls } /// - /// Gets or sets the text that is displayed by the - /// . + /// Gets or sets the text that is displayed by the . /// /// - /// The text displayed by the - /// . + /// The text displayed by the . /// - /// + /// /// The text entered cannot be parsed to a valid date, and the exception /// is not suppressed. /// - /// + /// /// The text entered parses to a date that is not selectable. /// public string? Text From 6aa31333af4d7df7ae8c5b5cfe9c061cc26d7281 Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Sun, 10 Apr 2022 15:55:20 -0400 Subject: [PATCH 366/820] Update FluentTheme templates --- .../Controls/Button.xaml | 14 ++++++------ .../Controls/CalendarItem.xaml | 6 ++--- .../Controls/CheckBox.xaml | 18 +++++++-------- .../Controls/ComboBox.xaml | 10 ++++----- .../Controls/ComboBoxItem.xaml | 16 +++++++------- .../Controls/DatePicker.xaml | 18 +++++++-------- .../Controls/Expander.xaml | 2 +- .../Controls/ListBoxItem.xaml | 18 +++++++-------- .../Controls/MenuItem.xaml | 6 ++--- .../Controls/RadioButton.xaml | 8 +++---- .../Controls/RepeatButton.xaml | 6 ++--- .../Controls/Slider.xaml | 2 +- .../Controls/SplitButton.xaml | 6 ++--- .../Controls/TabItem.xaml | 6 ++--- .../Controls/TabStripItem.xaml | 6 ++--- .../Controls/TimePicker.xaml | 12 +++++----- .../Controls/ToggleButton.xaml | 22 +++++++++---------- .../Controls/TreeViewItem.xaml | 14 ++++++------ 18 files changed, 95 insertions(+), 95 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index 8b53804d68..f545206a2f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -43,37 +43,37 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml index 0d1dd03c6e..e8e2df03b4 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml @@ -61,17 +61,17 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index a861699675..11d6b9fdfc 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -43,7 +43,7 @@ @@ -165,7 +165,7 @@ Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - TextElement.Foreground="{TemplateBinding Foreground}" + Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" CornerRadius="{TemplateBinding CornerRadius}"/> @@ -212,7 +212,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 24bdbca740..33d502772e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -110,7 +110,7 @@ BorderThickness="0" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" - TextElement.Foreground="{DynamicResource ExpanderForeground}" /> + Foreground="{DynamicResource ExpanderForeground}" /> @@ -49,7 +49,7 @@ @@ -57,7 +57,7 @@ @@ -65,7 +65,7 @@ @@ -73,7 +73,7 @@ @@ -81,7 +81,7 @@ @@ -89,6 +89,6 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 09f11d9c11..33cf6bfdde 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -218,7 +218,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml index fd3e3b0ed6..cd2c02c567 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml @@ -208,7 +208,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml index 059e041e25..f86b67bb6c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml @@ -107,7 +107,7 @@ @@ -116,7 +116,7 @@ @@ -125,7 +125,7 @@ @@ -134,7 +134,7 @@ @@ -143,7 +143,7 @@ @@ -152,7 +152,7 @@ @@ -161,7 +161,7 @@ From b0246965b8a72cc33403093919acfb449bc21c38 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:00:08 -0400 Subject: [PATCH 367/820] Modernize TextBox property accessor code --- src/Avalonia.Controls/TextBox.cs | 94 ++++++++++++++------------------ 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index ce7d2f7e5f..82346599a4 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -239,23 +239,19 @@ namespace Avalonia.Controls public bool AcceptsReturn { - get { return GetValue(AcceptsReturnProperty); } - set { SetValue(AcceptsReturnProperty, value); } + get => GetValue(AcceptsReturnProperty); + set => SetValue(AcceptsReturnProperty, value); } public bool AcceptsTab { - get { return GetValue(AcceptsTabProperty); } - set { SetValue(AcceptsTabProperty, value); } + get => GetValue(AcceptsTabProperty); + set => SetValue(AcceptsTabProperty, value); } public int CaretIndex { - get - { - return _caretIndex; - } - + get => _caretIndex; set { value = CoerceCaretIndex(value); @@ -271,8 +267,8 @@ namespace Avalonia.Controls public bool IsReadOnly { - get { return GetValue(IsReadOnlyProperty); } - set { SetValue(IsReadOnlyProperty, value); } + get => GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); } public char PasswordChar @@ -301,11 +297,7 @@ namespace Avalonia.Controls public int SelectionStart { - get - { - return _selectionStart; - } - + get => _selectionStart; set { value = CoerceCaretIndex(value); @@ -325,11 +317,7 @@ namespace Avalonia.Controls public int SelectionEnd { - get - { - return _selectionEnd; - } - + get => _selectionEnd; set { value = CoerceCaretIndex(value); @@ -349,20 +337,20 @@ namespace Avalonia.Controls public int MaxLength { - get { return GetValue(MaxLengthProperty); } - set { SetValue(MaxLengthProperty, value); } + get => GetValue(MaxLengthProperty); + set => SetValue(MaxLengthProperty, value); } public int MaxLines { - get { return GetValue(MaxLinesProperty); } - set { SetValue(MaxLinesProperty, value); } + get => GetValue(MaxLinesProperty); + set => SetValue(MaxLinesProperty, value); } [Content] public string? Text { - get { return _text; } + get => _text; set { if (!_ignoreTextChanges) @@ -386,7 +374,7 @@ namespace Avalonia.Controls public string SelectedText { - get { return GetSelection(); } + get => GetSelection(); set { if (string.IsNullOrEmpty(value)) @@ -407,8 +395,8 @@ namespace Avalonia.Controls /// public HorizontalAlignment HorizontalContentAlignment { - get { return GetValue(HorizontalContentAlignmentProperty); } - set { SetValue(HorizontalContentAlignmentProperty, value); } + get => GetValue(HorizontalContentAlignmentProperty); + set => SetValue(HorizontalContentAlignmentProperty, value); } /// @@ -416,14 +404,14 @@ namespace Avalonia.Controls /// public VerticalAlignment VerticalContentAlignment { - get { return GetValue(VerticalContentAlignmentProperty); } - set { SetValue(VerticalContentAlignmentProperty, value); } + get => GetValue(VerticalContentAlignmentProperty); + set => SetValue(VerticalContentAlignmentProperty, value); } public TextAlignment TextAlignment { - get { return GetValue(TextAlignmentProperty); } - set { SetValue(TextAlignmentProperty, value); } + get => GetValue(TextAlignmentProperty); + set => SetValue(TextAlignmentProperty, value); } /// @@ -448,26 +436,26 @@ namespace Avalonia.Controls public object InnerLeftContent { - get { return GetValue(InnerLeftContentProperty); } - set { SetValue(InnerLeftContentProperty, value); } + get => GetValue(InnerLeftContentProperty); + set => SetValue(InnerLeftContentProperty, value); } public object InnerRightContent { - get { return GetValue(InnerRightContentProperty); } - set { SetValue(InnerRightContentProperty, value); } + get => GetValue(InnerRightContentProperty); + set => SetValue(InnerRightContentProperty, value); } public bool RevealPassword { - get { return GetValue(RevealPasswordProperty); } - set { SetValue(RevealPasswordProperty, value); } + get => GetValue(RevealPasswordProperty); + set => SetValue(RevealPasswordProperty, value); } public TextWrapping TextWrapping { - get { return GetValue(TextWrappingProperty); } - set { SetValue(TextWrappingProperty, value); } + get => GetValue(TextWrappingProperty); + set => SetValue(TextWrappingProperty, value); } /// @@ -475,8 +463,8 @@ namespace Avalonia.Controls /// public string NewLine { - get { return _newLine; } - set { SetAndRaise(NewLineProperty, ref _newLine, value); } + get => _newLine; + set => SetAndRaise(NewLineProperty, ref _newLine, value); } /// @@ -492,8 +480,8 @@ namespace Avalonia.Controls /// public bool CanCut { - get { return _canCut; } - private set { SetAndRaise(CanCutProperty, ref _canCut, value); } + get => _canCut; + private set => SetAndRaise(CanCutProperty, ref _canCut, value); } /// @@ -501,8 +489,8 @@ namespace Avalonia.Controls /// public bool CanCopy { - get { return _canCopy; } - private set { SetAndRaise(CanCopyProperty, ref _canCopy, value); } + get => _canCopy; + private set => SetAndRaise(CanCopyProperty, ref _canCopy, value); } /// @@ -510,8 +498,8 @@ namespace Avalonia.Controls /// public bool CanPaste { - get { return _canPaste; } - private set { SetAndRaise(CanPasteProperty, ref _canPaste, value); } + get => _canPaste; + private set => SetAndRaise(CanPasteProperty, ref _canPaste, value); } /// @@ -519,13 +507,13 @@ namespace Avalonia.Controls /// public bool IsUndoEnabled { - get { return GetValue(IsUndoEnabledProperty); } - set { SetValue(IsUndoEnabledProperty, value); } + get => GetValue(IsUndoEnabledProperty); + set => SetValue(IsUndoEnabledProperty, value); } public int UndoLimit { - get { return _undoRedoHelper.Limit; } + get => _undoRedoHelper.Limit; set { if (_undoRedoHelper.Limit != value) @@ -1544,7 +1532,7 @@ namespace Avalonia.Controls UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState { - get { return new UndoRedoState(Text, CaretIndex); } + get => new UndoRedoState(Text, CaretIndex); set { Text = value.Text; From b043db169cc02d1018ed02c95ea41231acf4a010 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:15:34 -0400 Subject: [PATCH 368/820] Make sure to call base methods in Button --- src/Avalonia.Controls/Button.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index a4d15bab8d..9a7853cd4b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -309,6 +309,8 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; } + + base.OnKeyUp(e); } /// @@ -393,6 +395,8 @@ namespace Avalonia.Controls /// protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { + base.OnPointerCaptureLost(e); + IsPressed = false; } @@ -407,6 +411,8 @@ namespace Avalonia.Controls /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { + base.OnApplyTemplate(e); + UnregisterFlyoutEvents(Flyout); RegisterFlyoutEvents(Flyout); UpdatePseudoClasses(); From 0329a8daa1ceef647d89f3ccece0ca1e94e544ec Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:18:34 -0400 Subject: [PATCH 369/820] Support opening the calendar flyout when pressing outside the button --- .../Calendar/CalendarDatePicker.cs | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index fbd3f36aea..1f9b457c42 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -331,6 +331,7 @@ namespace Avalonia.Controls base.OnPropertyChanged(change); } + /// protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) { if (property == SelectedDateProperty) @@ -339,6 +340,55 @@ namespace Avalonia.Controls } } + /// + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + e.Handled = true; + + _ignoreButtonClick = _isPopupClosing; + + _isPressed = true; + UpdatePseudoClasses(); + } + } + + /// + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + + if (_isPressed && e.InitialPressMouseButton == MouseButton.Left) + { + e.Handled = true; + + if (!_ignoreButtonClick) + { + HandlePopUp(); + } + else + { + _ignoreButtonClick = false; + } + + _isPressed = false; + UpdatePseudoClasses(); + } + } + + /// + protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) + { + base.OnPointerCaptureLost(e); + + _isPressed = false; + UpdatePseudoClasses(); + } + + /// protected override void OnPointerWheelChanged(PointerWheelEventArgs e) { base.OnPointerWheelChanged(e); @@ -354,6 +404,7 @@ namespace Avalonia.Controls } } + /// protected override void OnGotFocus(GotFocusEventArgs e) { base.OnGotFocus(e); @@ -369,10 +420,14 @@ namespace Avalonia.Controls } } + /// protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); + _isPressed = false; + UpdatePseudoClasses(); + SetSelectedDate(); } @@ -671,7 +726,6 @@ namespace Avalonia.Controls private bool ProcessDatePickerKey(KeyEventArgs e) { - switch (e.Key) { case Key.Enter: From 24a3dc560aaed3f7208bce05b190f4b79a3695fd Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:23:39 -0400 Subject: [PATCH 370/820] Better support MinHeight --- src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index c5a219540b..00f53a563d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -19,6 +19,7 @@ 12 + 32 - + + + + + + From 8997c9d9dfb72478523d2d1da3986795901a529d Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Sun, 10 Apr 2022 18:31:20 -0400 Subject: [PATCH 372/820] Update ApiCompat --- src/Avalonia.Controls/ApiCompatBaseline.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 12afe71f77..fe3ac31734 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -36,6 +36,11 @@ MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontWeightProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.ForegroundProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontSizeProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextAlignmentProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextTrimmingProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextWrappingProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.LineHeightProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.MaxLinesProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.FontFamily Avalonia.Controls.TextBlock.GetFontFamily(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Double Avalonia.Controls.TextBlock.GetFontSize(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.FontStyle Avalonia.Controls.TextBlock.GetFontStyle(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. @@ -89,4 +94,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: 90 +Total Issues: 95 From dbe46697c9e75b3d77afed712321695e679588d0 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 20:21:53 -0400 Subject: [PATCH 373/820] Improve CalendarDatePicker Fluent style --- .../Controls/CalendarDatePicker.xaml | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index e030e14243..592581e975 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -23,7 +23,6 @@ - - - + - + + + + + @@ -31,7 +44,9 @@ - + diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 9dfb34517b..9b67c9b096 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -669,8 +669,6 @@ namespace Avalonia.Controls ItemsProperty.Changed.AddClassHandler((x, e) => x.OnItemsPropertyChanged(e)); CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.OnCanUserResizeColumnsChanged(e)); ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnColumnWidthChanged(e)); - RowBackgroundProperty.Changed.AddClassHandler((x, e) => x.OnRowBackgroundChanged(e)); - AlternatingRowBackgroundProperty.Changed.AddClassHandler((x, e) => x.OnRowBackgroundChanged(e)); FrozenColumnCountProperty.Changed.AddClassHandler((x, e) => x.OnFrozenColumnCountChanged(e)); GridLinesVisibilityProperty.Changed.AddClassHandler((x, e) => x.OnGridLinesVisibilityChanged(e)); HeadersVisibilityProperty.Changed.AddClassHandler((x, e) => x.OnHeadersVisibilityChanged(e)); @@ -1144,14 +1142,6 @@ namespace Avalonia.Controls InvalidateCellsArrange(); } - private void OnRowBackgroundChanged(AvaloniaPropertyChangedEventArgs e) - { - foreach (DataGridRow row in GetAllRows()) - { - row.EnsureBackground(); - } - } - private void OnColumnWidthChanged(AvaloniaPropertyChangedEventArgs e) { var value = (DataGridLength)e.NewValue; diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index a77b482436..4a7137dcda 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -199,7 +199,7 @@ namespace Avalonia.Controls if (change.Property == IsVisibleProperty) { OwningGrid?.OnColumnVisibleStateChanging(this); - var isVisible = (change as AvaloniaPropertyChangedEventArgs).NewValue.Value; + var isVisible = change.NewValue.GetValueOrDefault(); if (_headerCell != null) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs index 922b1d9c08..e7f9a9a6c4 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs @@ -12,7 +12,8 @@ namespace Avalonia.Controls { internal class DataGridColumnCollection : ObservableCollection { - private DataGrid _owningGrid; + private readonly Dictionary _columnsMap = new Dictionary(); + private readonly DataGrid _owningGrid; public DataGridColumnCollection(DataGrid owningGrid) { @@ -124,18 +125,8 @@ namespace Avalonia.Controls internal int VisibleColumnCount { - get - { - int visibleColumnCount = 0; - for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) - { - if (ItemsInternal[columnIndex].IsVisible) - { - visibleColumnCount++; - } - } - return visibleColumnCount; - } + get; + private set; } internal double VisibleEdgedColumnsWidth @@ -287,20 +278,31 @@ namespace Avalonia.Controls { VisibleStarColumnCount = 0; VisibleEdgedColumnsWidth = 0; + VisibleColumnCount = 0; + _columnsMap.Clear(); + for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) { - if (ItemsInternal[columnIndex].IsVisible) + var item = ItemsInternal[columnIndex]; + _columnsMap[columnIndex] = item.DisplayIndex; + if (item.IsVisible) { - ItemsInternal[columnIndex].EnsureWidth(); - if (ItemsInternal[columnIndex].Width.IsStar) + VisibleColumnCount++; + item.EnsureWidth(); + if (item.Width.IsStar) { VisibleStarColumnCount++; } - VisibleEdgedColumnsWidth += ItemsInternal[columnIndex].ActualWidth; + VisibleEdgedColumnsWidth += item.ActualWidth; } } } + internal int GetColumnDisplayIndex(int columnIndex) + { + return _columnsMap.TryGetValue(columnIndex, out var displayIndex) ? displayIndex : -1; + } + internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex) { if (displayIndex < 0 || displayIndex >= ItemsInternal.Count || displayIndex >= DisplayIndexMap.Count) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs index a4577ee952..ea8a121d29 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs @@ -444,12 +444,11 @@ namespace Avalonia.Controls // We need to explicitly collapse the cells of the invisible column because layout only goes through // visible ones - if (!updatedColumn.IsVisible) + ColumnHeaders.InvalidateChildIndex(); + foreach (var row in GetAllRows()) { - foreach (DataGridRow row in GetAllRows()) - { - row.Cells[updatedColumn.Index].IsVisible = false; - } + row.Cells[updatedColumn.Index].IsVisible = updatedColumn.IsVisible; + row.InvalidateCellsIndex(); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index fade597ca1..a3095ad214 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -77,7 +77,7 @@ namespace Avalonia.Controls private set; } - public int Count => GetCount(true); + public int Count => TryGetCount(true, false, out var count) ? count : 0; public bool DataIsPrimitive { @@ -193,22 +193,25 @@ namespace Avalonia.Controls } } - internal bool Any() - { - return GetCount(false) > 0; - } - /// When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead. - private int GetCount(bool allowSlow) + /// If "getAny" is true, method can use Linq.Any() method to speedup. + internal bool TryGetCount(bool allowSlow, bool getAny, out int count) { - return DataSource switch + bool result; + (result, count) = DataSource switch { - ICollection collection => collection.Count, - DataGridCollectionView cv => cv.Count, - IEnumerable enumerable when allowSlow => enumerable.Cast().Count(), - IEnumerable enumerable when !allowSlow => enumerable.Cast().Any() ? 1 : 0, - _ => 0 + ICollection collection => (true, collection.Count), + DataGridCollectionView cv => (true, cv.Count), + IEnumerable enumerable when allowSlow && !getAny => (true, enumerable.Cast().Count()), + IEnumerable enumerable when getAny => (true, enumerable.Cast().Any() ? 1 : 0), + _ => (false, 0) }; + return result; + } + + internal bool Any() + { + return TryGetCount(false, true, out var count) && count > 0; } /// @@ -383,7 +386,7 @@ namespace Avalonia.Controls List propertyNames = TypeHelper.SplitPropertyPath(propertyName); for (int i = 0; i < propertyNames.Count; i++) { - propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out object[] index); + propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out _); if (propertyInfo == null || propertyType.GetIsReadOnly() || propertyInfo.GetIsReadOnly()) { // Either the data type is read-only, the property doesn't exist, or it does exist but is read-only @@ -391,11 +394,10 @@ namespace Avalonia.Controls } // Check if EditableAttribute is defined on the property and if it indicates uneditable - object[] attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true); + var attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true); if (attributes != null && attributes.Length > 0) { - EditableAttribute editableAttribute = attributes[0] as EditableAttribute; - Debug.Assert(editableAttribute != null); + var editableAttribute = (EditableAttribute)attributes[0]; if (!editableAttribute.AllowEdit) { return true; diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index 1efce7c0b8..a6faec752d 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -543,7 +543,6 @@ namespace Avalonia.Controls RootElement = e.NameScope.Find(DATAGRIDROW_elementRoot); if (RootElement != null) { - EnsureBackground(); UpdatePseudoClasses(); } @@ -668,43 +667,9 @@ namespace Avalonia.Controls Slot = -1; } - // Make sure the row's background is set to its correct value. It could be explicity set or inherit - // DataGrid.RowBackground or DataGrid.AlternatingRowBackground - internal void EnsureBackground() + internal void InvalidateCellsIndex() { - // Inherit the DataGrid's RowBackground properties only if this row doesn't explicity have a background set - if (RootElement != null && OwningGrid != null) - { - IBrush newBackground = null; - if (Background == null) - { - if (Index % 2 == 0 || OwningGrid.AlternatingRowBackground == null) - { - // Use OwningGrid.RowBackground if the index is even or if the OwningGrid.AlternatingRowBackground is null - if (OwningGrid.RowBackground != null) - { - newBackground = OwningGrid.RowBackground; - } - } - else - { - // Alternate row - if (OwningGrid.AlternatingRowBackground != null) - { - newBackground = OwningGrid.AlternatingRowBackground; - } - } - } - else - { - newBackground = Background; - } - - if (RootElement.Background != newBackground) - { - RootElement.Background = newBackground; - } - } + _cellsElement?.InvalidateChildIndex(); } internal void EnsureFillerVisibility() diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 1d5c899993..52b67b8921 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -5,6 +5,7 @@ using Avalonia.Collections; using Avalonia.Controls.Utils; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Utilities; using System; @@ -16,7 +17,7 @@ using System.Linq; namespace Avalonia.Controls { - public partial class DataGrid + public partial class DataGrid : IChildIndexProvider { internal bool AreRowBottomGridLinesRequired @@ -123,6 +124,26 @@ namespace Avalonia.Controls } } + internal EventHandler _childIndexChanged; + + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridRow row + ? row.Index + : throw new InvalidOperationException("Invalid DataGrid child"); + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + return DataConnection.TryGetCount(false, true, out count); + } + /// /// Clears the entire selection. Displayed rows are deselected explicitly to visualize /// potential transition effects @@ -811,7 +832,7 @@ namespace Avalonia.Controls if (row.Slot > slotDeleted) { CorrectRowAfterDeletion(row, wasRow); - row.EnsureBackground(); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } } @@ -867,7 +888,7 @@ namespace Avalonia.Controls if (row.Slot >= slotInserted) { DataGrid.CorrectRowAfterInsertion(row, rowInserted); - row.EnsureBackground(); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } } @@ -1485,8 +1506,8 @@ namespace Avalonia.Controls // If the row has been recycled, reapply the BackgroundBrush if (row.IsRecycled) { - row.EnsureBackground(); row.ApplyCellsState(); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } else if (row == EditingRow) { diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs index c5fe9f0cb2..e07c933039 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs @@ -3,12 +3,13 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Utilities; using System; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; -using Avalonia.Controls; -using Avalonia.Controls.Utils; namespace Avalonia.Controls.Primitives { @@ -16,9 +17,10 @@ namespace Avalonia.Controls.Primitives /// Used within the template of a /// to specify the location in the control's visual tree where the cells are to be added. /// - public sealed class DataGridCellsPresenter : Panel + public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider { private double _fillerLeftEdge; + private EventHandler _childIndexChanged; // The desired height needs to be cached due to column virtualization; otherwise, the cells // would grow and shrink as the DataGrid scrolls horizontally @@ -42,6 +44,25 @@ namespace Avalonia.Controls.Primitives set; } + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridCell cell + ? OwningGrid.ColumnsInternal.GetColumnDisplayIndex(cell.ColumnIndex) + : throw new InvalidOperationException("Invalid cell type"); + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + count = OwningGrid.ColumnsInternal.VisibleColumnCount; + return true; + } + /// /// Arranges the content of the . /// @@ -120,6 +141,13 @@ namespace Avalonia.Controls.Primitives } } + protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + { + base.ChildrenChanged(sender, e); + + InvalidateChildIndex(); + } + private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn) { if (cell.IsCurrent) @@ -304,6 +332,11 @@ namespace Avalonia.Controls.Primitives DesiredHeight = 0; } + internal void InvalidateChildIndex() + { + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); + } + private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge) { if (!column.IsVisible) diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs index 4eed119240..108dc8ded7 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs @@ -3,8 +3,10 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using Avalonia.LogicalTree; using Avalonia.Media; using System; +using System.Collections.Specialized; using System.Diagnostics; namespace Avalonia.Controls.Primitives @@ -13,10 +15,11 @@ namespace Avalonia.Controls.Primitives /// Used within the template of a to specify the /// location in the control's visual tree where the column headers are to be added. /// - public sealed class DataGridColumnHeadersPresenter : Panel + public sealed class DataGridColumnHeadersPresenter : Panel, IChildIndexProvider { private Control _dragIndicator; private IControl _dropLocationIndicator; + private EventHandler _childIndexChanged; /// /// Tracks which column is currently being dragged. @@ -104,6 +107,25 @@ namespace Avalonia.Controls.Primitives set; } + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridColumnHeader header + ? OwningGrid.ColumnsInternal.GetColumnDisplayIndex(header.ColumnIndex) + : throw new InvalidOperationException("Invalid cell type"); + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + count = OwningGrid.ColumnsInternal.VisibleColumnCount; + return true; + } + /// /// Arranges the content of the . /// @@ -391,5 +413,17 @@ namespace Avalonia.Controls.Primitives OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height); } + + protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + { + base.ChildrenChanged(sender, e); + + InvalidateChildIndex(); + } + + internal void InvalidateChildIndex() + { + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); + } } } diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs index 308ebc69d4..0b9bd0a564 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs @@ -7,8 +7,8 @@ using System; using System.Diagnostics; using Avalonia.Input; -using Avalonia.Input.GestureRecognizers; using Avalonia.Layout; +using Avalonia.LogicalTree; using Avalonia.Media; namespace Avalonia.Controls.Primitives @@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives /// Used within the template of a to specify the /// location in the control's visual tree where the rows are to be added. /// - public sealed class DataGridRowsPresenter : Panel + public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider { public DataGridRowsPresenter() { @@ -44,6 +44,22 @@ namespace Avalonia.Controls.Primitives } } + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => OwningGrid._childIndexChanged += value; + remove => OwningGrid._childIndexChanged -= value; + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + return ((IChildIndexProvider)OwningGrid).TryGetTotalCount(out count); + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return ((IChildIndexProvider)OwningGrid).GetChildIndex(child); + } + /// /// Arranges the content of the . /// diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 8d4e327f3e..05c3d2dd19 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -103,8 +103,9 @@ + Background="{TemplateBinding Background}" + RowDefinitions="*,Auto,Auto" + ColumnDefinitions="Auto,*"> @@ -118,6 +119,13 @@ + + + + + + diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 0cd72dc91c..114aa9727d 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -166,7 +166,7 @@ namespace Avalonia.Controls if (Presenter is IChildIndexProvider innerProvider) { innerProvider.ChildIndexChanged += PresenterChildIndexChanged; - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); } } diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 482a7fab84..2230b4b0d2 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -147,7 +147,7 @@ namespace Avalonia.Controls throw new NotSupportedException(); } - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); InvalidateMeasureOnChildrenChanged(); } diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index f938c8d437..2821fa8cf0 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -158,7 +158,7 @@ namespace Avalonia.Controls.Presenters { ItemsChanged(e); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); } } diff --git a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs index de41f5292c..afc6c1f5fc 100644 --- a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs +++ b/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs @@ -8,6 +8,8 @@ namespace Avalonia.LogicalTree /// public class ChildIndexChangedEventArgs : EventArgs { + public static new ChildIndexChangedEventArgs Empty { get; } = new ChildIndexChangedEventArgs(); + public ChildIndexChangedEventArgs() { } From f7e8b7658fbc68e127f7dc508e386459bf763637 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 31 Dec 2021 03:43:23 -0500 Subject: [PATCH 400/820] Do not hardcode Margin and VerticalAlignment on data grid text column --- src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs | 5 +---- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 4 ++++ src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs index 863910c226..68736dee7f 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs @@ -19,8 +19,6 @@ namespace Avalonia.Controls /// public class DataGridTextColumn : DataGridBoundColumn { - private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin"; - /// /// Initializes a new instance of the class. /// @@ -178,8 +176,7 @@ namespace Avalonia.Controls { TextBlock textBlockElement = new TextBlock { - [!Layoutable.MarginProperty] = new DynamicResourceExtension(DATAGRID_TextColumnCellTextBlockMarginKey), - VerticalAlignment = VerticalAlignment.Center + Name = "CellTextBlock" }; SyncProperties(textBlockElement); diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 05c3d2dd19..b066702212 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -29,6 +29,10 @@ + + + From 5019ee09853f91b695563b3ea330dcc0f04673a4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 31 Dec 2021 03:46:59 -0500 Subject: [PATCH 401/820] Add missing support of Border properties to the DataGrid templates --- .../Themes/Default.xaml | 135 ++++++---- .../Themes/Fluent.xaml | 255 +++++++++--------- 2 files changed, 213 insertions(+), 177 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index b066702212..0d1fe43eb6 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -12,20 +12,25 @@ - - - - - + + + + + + + @@ -44,35 +49,40 @@ - - - - - - + + + + + + + - + - + - + + @@ -106,19 +116,24 @@ @@ -192,12 +207,18 @@ + ColumnDefinitions="Auto,Auto,Auto,Auto" + RowDefinitions="Auto,*,Auto"> - + @@ -250,9 +271,11 @@ - + CornerRadius="{TemplateBinding CornerRadius}"> diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 6a6ab283ab..8243d9c22d 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -93,54 +93,57 @@ - + + - - - - - + + + + - + - + - - + + + @@ -185,57 +188,58 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + Fill="{TemplateBinding SeparatorBrush}" + IsVisible="{TemplateBinding AreSeparatorsVisible}" /> + + + + + - + @@ -276,35 +280,40 @@ - - - - - - - - + + + + + + + - + DataGridFrozenGrid.IsFrozen="True" /> + + - + + @@ -315,7 +324,7 @@ - + @@ -443,9 +452,12 @@ Width="12" Height="12" Margin="12,0,0,0" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" - Foreground="{TemplateBinding Foreground}" - Focusable="False" /> + CornerRadius="{TemplateBinding CornerRadius}" + Focusable="False" + Foreground="{TemplateBinding Foreground}" /> - - + CornerRadius="{TemplateBinding CornerRadius}"> + From af9f27677d0289b5c45f366f2594f393d2782eb1 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 31 Dec 2021 04:21:11 -0500 Subject: [PATCH 402/820] Fix NRE in DataGrid --- .../DataGridColumns.cs | 2 +- .../DataGridRows.cs | 28 +++---------------- .../Primitives/DataGridRowsPresenter.cs | 19 +++++++++---- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs index ea8a121d29..52f0ad7537 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs @@ -444,7 +444,7 @@ namespace Avalonia.Controls // We need to explicitly collapse the cells of the invisible column because layout only goes through // visible ones - ColumnHeaders.InvalidateChildIndex(); + ColumnHeaders?.InvalidateChildIndex(); foreach (var row in GetAllRows()) { row.Cells[updatedColumn.Index].IsVisible = updatedColumn.IsVisible; diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 52b67b8921..f3afe2c42d 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -17,7 +17,7 @@ using System.Linq; namespace Avalonia.Controls { - public partial class DataGrid : IChildIndexProvider + public partial class DataGrid { internal bool AreRowBottomGridLinesRequired @@ -124,26 +124,6 @@ namespace Avalonia.Controls } } - internal EventHandler _childIndexChanged; - - event EventHandler IChildIndexProvider.ChildIndexChanged - { - add => _childIndexChanged += value; - remove => _childIndexChanged -= value; - } - - int IChildIndexProvider.GetChildIndex(ILogical child) - { - return child is DataGridRow row - ? row.Index - : throw new InvalidOperationException("Invalid DataGrid child"); - } - - bool IChildIndexProvider.TryGetTotalCount(out int count) - { - return DataConnection.TryGetCount(false, true, out count); - } - /// /// Clears the entire selection. Displayed rows are deselected explicitly to visualize /// potential transition effects @@ -832,7 +812,7 @@ namespace Avalonia.Controls if (row.Slot > slotDeleted) { CorrectRowAfterDeletion(row, wasRow); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); + _rowsPresenter?.InvalidateChildIndex(row); } } @@ -888,7 +868,7 @@ namespace Avalonia.Controls if (row.Slot >= slotInserted) { DataGrid.CorrectRowAfterInsertion(row, rowInserted); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); + _rowsPresenter?.InvalidateChildIndex(row); } } @@ -1507,7 +1487,7 @@ namespace Avalonia.Controls if (row.IsRecycled) { row.ApplyCellsState(); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); + _rowsPresenter?.InvalidateChildIndex(row); } else if (row == EditingRow) { diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs index 0b9bd0a564..5d82689eff 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs @@ -19,6 +19,8 @@ namespace Avalonia.Controls.Primitives /// public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider { + private EventHandler _childIndexChanged; + public DataGridRowsPresenter() { AddHandler(Gestures.ScrollGestureEvent, OnScrollGesture); @@ -46,18 +48,25 @@ namespace Avalonia.Controls.Primitives event EventHandler IChildIndexProvider.ChildIndexChanged { - add => OwningGrid._childIndexChanged += value; - remove => OwningGrid._childIndexChanged -= value; + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridRow row + ? row.Index + : throw new InvalidOperationException("Invalid DataGrid child"); } bool IChildIndexProvider.TryGetTotalCount(out int count) { - return ((IChildIndexProvider)OwningGrid).TryGetTotalCount(out count); + return OwningGrid.DataConnection.TryGetCount(false, true, out count); } - int IChildIndexProvider.GetChildIndex(ILogical child) + internal void InvalidateChildIndex(DataGridRow row) { - return ((IChildIndexProvider)OwningGrid).GetChildIndex(child); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } /// From 64cce62e7b34f2b843d3258de640858d6bfe94f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 12 Apr 2022 23:09:46 +0200 Subject: [PATCH 403/820] Benchmark for finding resources. --- .../Styling/ResourceBenchmarks.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs new file mode 100644 index 0000000000..1445fcba05 --- /dev/null +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -0,0 +1,91 @@ +using System; +using Avalonia.Controls; +using Avalonia.Platform; +using Avalonia.PlatformSupport; +using Avalonia.Styling; +using Avalonia.Themes.Fluent; +using Avalonia.UnitTests; +using BenchmarkDotNet.Attributes; +using Moq; + +namespace Avalonia.Benchmarks.Styling +{ + [MemoryDiagnoser] + public class ResourceBenchmarks : IDisposable + { + private readonly Control _searchStart; + private readonly IDisposable _app; + + private static IDisposable CreateApp() + { + var services = new TestServices( + assetLoader: new AssetLoader(), + globalClock: new MockGlobalClock(), + platform: new AppBuilder().RuntimePlatform, + renderInterface: new MockPlatformRenderInterface(), + standardCursorFactory: Mock.Of(), + styler: new Styler(), + theme: () => LoadTheme(), + threadingInterface: new NullThreadingPlatform(), + fontManagerImpl: new MockFontManagerImpl(), + textShaperImpl: new MockTextShaperImpl(), + windowingPlatform: new MockWindowingPlatform()); + + return UnitTestApplication.Start(services); + } + + private static Styles LoadTheme() + { + AssetLoader.RegisterResUriParsers(); + + var hostStyle = new Style(); + + hostStyle.Resources.Add("test", null); + + return new Styles + { + hostStyle, + new FluentTheme(new Uri("avares://Avalonia.Benchmarks")) + }; + } + + public void Dispose() + { + _app.Dispose(); + } + + public ResourceBenchmarks() + { + _searchStart = new Button(); + + _app = CreateApp(); + + Decorator root = new TestRoot(true, null) + { + Renderer = new NullRenderer() + }; + + var current = root; + + for (int i = 0; i < 10; i++) + { + var child = new Decorator(); + + current.Child = child; + + current = child; + } + + current.Child = _searchStart; + } + + [Benchmark(Baseline = true)] + public void FindResource() + { + for (int i = 0; i < 100; ++i) + { + _searchStart.FindResource("test"); + } + } + } +} From 55e681a326673f680f8324fa1bb365be4305f86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 12 Apr 2022 23:15:55 +0200 Subject: [PATCH 404/820] Add more benchmark cases. --- .../Styling/ResourceBenchmarks.cs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index 1445fcba05..babfaef6ab 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -38,14 +38,17 @@ namespace Avalonia.Benchmarks.Styling { AssetLoader.RegisterResUriParsers(); - var hostStyle = new Style(); + var preHost = new Style(); + preHost.Resources.Add("preTheme", null); - hostStyle.Resources.Add("test", null); + var postHost = new Style(); + postHost.Resources.Add("postTheme", null); return new Styles { - hostStyle, - new FluentTheme(new Uri("avares://Avalonia.Benchmarks")) + preHost, + new FluentTheme(new Uri("avares://Avalonia.Benchmarks")), + postHost }; } @@ -79,12 +82,30 @@ namespace Avalonia.Benchmarks.Styling current.Child = _searchStart; } - [Benchmark(Baseline = true)] - public void FindResource() + [Benchmark] + public void FindPreResource() { for (int i = 0; i < 100; ++i) { - _searchStart.FindResource("test"); + _searchStart.FindResource("preTheme"); + } + } + + [Benchmark] + public void FindPostResource() + { + for (int i = 0; i < 100; ++i) + { + _searchStart.FindResource("postTheme"); + } + } + + [Benchmark] + public void FindNotExistingResource() + { + for (int i = 0; i < 100; ++i) + { + _searchStart.FindResource("notPresent"); } } } From fb0da85ad3a998bdf711b42e7b619550e6ff9de3 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Apr 2022 14:39:44 +0200 Subject: [PATCH 405/820] Merge core libraries. (#5831) * Merge core libraries. Everything below `Avalonia.Controls` into `Avalonia.Base`. * Move new files to correct place. * Removed unused dirs/projects, * Removed outdated references from theme assemblies. * Merge unit tests to match new assembly layout. * Fixup test namespaces. * Make directory match namespace. * Move files to match namespace. * Move files to match namespace. * Fix up incorrect namespace. `Avalonia.Visuals.Media.Imaging` -> `Avalonia.Media.Imaging`. * Fix resource URL. * Removed outdated dependencies. * Added missing project reference. * Update test namespaces. * Fix merge error. * Fix merge errors. * Fix bad merge in WindowsInteropTest.csproj. * Fix up merge errors in csprojs. * Remove merged tests from nuke. * Fix up namespace. * Fix compile error. * Fix failing tests. Now that more unit tests are present in Avalonia.Base.UnitTests, general `AvaloniaObject` properties are getting registered. Ignore those. Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com> --- Avalonia.sln | 318 ------------------ Directory.Build.props | 1 + build/CoreLibraries.props | 6 - nukebuild/Build.cs | 6 - .../ControlCatalog.Android.csproj | 40 +++ .../ControlCatalog.iOS.csproj | 49 +++ .../RenderDemo/Pages/PathMeasurementPage.cs | 6 - .../Pages/RenderTargetBitmapPage.cs | 1 - samples/RenderDemo/RenderDemo.csproj | 2 +- .../Avalonia.AndroidTestApplication.csproj | 36 ++ src/Avalonia.Animation/ApiCompatBaseline.txt | 5 - .../Avalonia.Animation.csproj | 15 - .../Properties/AssemblyInfo.cs | 9 - .../Animation}/Animatable.cs | 0 .../Animation}/Animation.cs | 0 .../Animation}/AnimationInstance`1.cs | 0 .../Animation}/AnimatorDrivenTransition.cs | 0 .../Animation}/AnimatorKeyFrame.cs | 0 .../AnimatorTransitionObservable.cs | 0 .../Animation}/Animators/Animator`1.cs | 0 .../Animation/Animators/BaseBrushAnimator.cs | 0 .../Animation}/Animators/BoolAnimator.cs | 0 .../Animation/Animators/BoxShadowAnimator.cs | 0 .../Animation/Animators/BoxShadowsAnimator.cs | 0 .../Animation}/Animators/ByteAnimator.cs | 0 .../Animation/Animators/ColorAnimator.cs | 0 .../Animators/CornerRadiusAnimator.cs | 0 .../Animation}/Animators/DecimalAnimator.cs | 0 .../Animation}/Animators/DoubleAnimator.cs | 0 .../Animation}/Animators/FloatAnimator.cs | 0 .../Animators/GradientBrushAnimator.cs | 0 .../Animation}/Animators/Int16Animator.cs | 0 .../Animation}/Animators/Int32Animator.cs | 0 .../Animation}/Animators/Int64Animator.cs | 0 .../Animation/Animators/PointAnimator.cs | 0 .../Animation/Animators/RectAnimator.cs | 0 .../Animators/RelativePointAnimator.cs | 0 .../Animation/Animators/SizeAnimator.cs | 0 .../Animators/SolidColorBrushAnimator.cs | 0 .../Animation/Animators/ThicknessAnimator.cs | 0 .../Animation/Animators/TransformAnimator.cs | 0 .../Animators/TransformOperationsAnimator.cs | 0 .../Animation}/Animators/UInt16Animator.cs | 0 .../Animation}/Animators/UInt32Animator.cs | 0 .../Animation}/Animators/UInt64Animator.cs | 0 .../Animation/Animators/VectorAnimator.cs | 0 .../Animation}/Clock.cs | 0 .../Animation}/ClockBase.cs | 0 .../Animation/CompositePageTransition.cs | 0 .../Animation/CrossFade.cs | 0 .../Animation}/Cue.cs | 0 .../DisposeAnimationInstanceSubject.cs | 0 .../Animation/Easings}/BackEaseIn.cs | 0 .../Animation/Easings}/BackEaseInOut.cs | 0 .../Animation/Easings}/BackEaseOut.cs | 0 .../Animation/Easings}/BounceEaseIn.cs | 0 .../Animation/Easings}/BounceEaseInOut.cs | 0 .../Animation/Easings}/BounceEaseOut.cs | 0 .../Animation/Easings}/CircularEaseIn.cs | 0 .../Animation/Easings}/CircularEaseInOut.cs | 0 .../Animation/Easings}/CircularEaseOut.cs | 0 .../Animation/Easings}/CubicEaseIn.cs | 0 .../Animation/Easings}/CubicEaseInOut.cs | 0 .../Animation/Easings}/CubicEaseOut.cs | 0 .../Animation/Easings}/Easing.cs | 0 .../Animation/Easings}/EasingTypeConverter.cs | 0 .../Animation/Easings}/ElasticEaseIn.cs | 0 .../Animation/Easings}/ElasticEaseInOut.cs | 0 .../Animation/Easings}/ElasticEaseOut.cs | 0 .../Animation/Easings}/ExponentialEaseIn.cs | 0 .../Easings}/ExponentialEaseInOut.cs | 0 .../Animation/Easings}/ExponentialEaseOut.cs | 0 .../Animation/Easings}/IEasing.cs | 0 .../Animation/Easings}/LinearEasing.cs | 0 .../Animation/Easings}/QuadraticEaseIn.cs | 0 .../Animation/Easings}/QuadraticEaseInOut.cs | 0 .../Animation/Easings}/QuadraticEaseOut.cs | 0 .../Animation/Easings}/QuarticEaseIn.cs | 0 .../Animation/Easings}/QuarticEaseInOut.cs | 0 .../Animation/Easings}/QuarticEaseOut.cs | 0 .../Animation/Easings}/QuinticEaseIn.cs | 0 .../Animation/Easings}/QuinticEaseInOut.cs | 0 .../Animation/Easings}/QuinticEaseOut.cs | 0 .../Animation/Easings}/SineEaseIn.cs | 0 .../Animation/Easings}/SineEaseInOut.cs | 0 .../Animation/Easings}/SineEaseOut.cs | 0 .../Animation/Easings}/SplineEasing.cs | 0 .../Animation}/FillMode.cs | 0 .../Animation}/IAnimation.cs | 0 .../Animation}/IAnimationSetter.cs | 0 .../Animation}/IAnimator.cs | 0 .../Animation}/IClock.cs | 0 .../Animation}/IGlobalClock.cs | 0 .../Animation/IPageTransition.cs | 0 .../Animation}/ITransition.cs | 0 .../Animation}/IterationCount.cs | 0 .../Animation}/IterationCountTypeConverter.cs | 0 .../Animation}/KeyFrame.cs | 0 .../Animation}/KeyFrames.cs | 0 .../Animation}/KeySpline.cs | 0 .../Animation}/KeySplineTypeConverter.cs | 0 .../Animation/PageSlide.cs | 0 .../Animation}/PlayState.cs | 0 .../Animation}/PlaybackDirection.cs | 0 .../Animation/RenderLoopClock.cs | 0 .../Animation}/Transition.cs | 0 .../Animation}/TransitionInstance.cs | 0 .../Animation}/TransitionObservableBase.cs | 0 .../Animation}/Transitions.cs | 0 .../Transitions/BoxShadowsTransition.cs | 0 .../Animation/Transitions/BrushTransition.cs | 0 .../Animation/Transitions/ColorTransition.cs | 0 .../Transitions/CornerRadiusTransition.cs | 0 .../Transitions/DoubleTransition.cs | 0 .../Animation}/Transitions/FloatTransition.cs | 0 .../Transitions/IntegerTransition.cs | 0 .../Animation/Transitions/PointTransition.cs | 0 .../Transitions/RelativePointTransition.cs | 0 .../Animation/Transitions/SizeTransition.cs | 0 .../Transitions/ThicknessTransition.cs | 0 .../TransformOperationsTransition.cs | 0 .../Animation/Transitions/VectorTransition.cs | 0 .../Animation}/Utils/BounceEaseUtils.cs | 0 .../Animation}/Utils/EasingUtils.cs | 0 .../Assets/BiDi.trie | Bin .../Assets/GraphemeBreak.trie | Bin .../Assets/UnicodeData.trie | Bin src/Avalonia.Base/Avalonia.Base.csproj | 3 + .../AvaloniaPropertyExtensions.cs | 0 .../ClassBindingManager.cs | 0 .../CombinedGeometry.cs | 0 .../Controls/ChildNameScope.cs | 2 +- .../Controls/Classes.cs | 0 .../Controls/INameScope.cs | 0 .../Controls/IPseudoClasses.cs | 0 .../Controls/IResourceDictionary.cs | 0 .../Controls/IResourceHost.cs | 0 .../Controls/IResourceNode.cs | 0 .../Controls/IResourceProvider.cs | 0 .../Controls/ISetInheritanceParent.cs | 0 .../Controls/ISetLogicalParent.cs | 0 .../Metadata/PseudoClassesAttribute.cs | 0 .../Metadata/TemplatePartAttribute.cs | 0 .../Controls/NameScope.cs | 2 +- .../Controls/NameScopeEventArgs.cs | 0 .../Controls/NameScopeExtensions.cs | 0 .../Controls/NameScopeLocator.cs | 0 .../Controls/PseudoClassesExtensions.cs | 0 .../Controls/ResourceDictionary.cs | 0 .../Controls/ResourceNodeExtensions.cs | 0 .../Controls/ResourcesChangedEventArgs.cs | 0 .../CornerRadius.cs | 0 .../Diagnostics/StyleDiagnostics.cs | 0 .../Diagnostics/StyledElementExtensions.cs | 0 .../GeometryCollection.cs | 0 .../Media => Avalonia.Base}/GeometryGroup.cs | 0 .../IDataContextProvider.cs | 0 .../INamed.cs | 0 .../IStyledElement.cs | 0 .../Input}/AccessKeyHandler.cs | 0 .../Input}/Cursor.cs | 0 .../Input}/DataFormats.cs | 0 .../Input}/DataObject.cs | 0 .../Input}/DragDrop.cs | 0 .../Input}/DragDropDevice.cs | 0 .../Input}/DragDropEffects.cs | 0 .../Input}/DragEventArgs.cs | 0 .../Input}/FocusManager.cs | 0 .../GestureRecognizerCollection.cs | 0 .../GestureRecognizers/IGestureRecognizer.cs | 0 .../ScrollGestureRecognizer.cs | 0 .../Input}/Gestures.cs | 0 .../Input}/GotFocusEventArgs.cs | 0 .../Input}/IAccessKeyHandler.cs | 0 .../Input}/IClickableControl.cs | 0 .../Input}/ICloseable.cs | 0 .../Input}/ICommandSource.cs | 0 .../Input}/ICustomKeyboardNavigation.cs | 0 .../Input}/IDataObject.cs | 0 .../Input}/IFocusManager.cs | 0 .../Input}/IFocusScope.cs | 0 .../Input}/IInputDevice.cs | 0 .../Input}/IInputElement.cs | 0 .../Input}/IInputManager.cs | 0 .../Input}/IInputRoot.cs | 0 .../Input}/IKeyboardDevice.cs | 0 .../Input}/IKeyboardNavigationHandler.cs | 0 .../Input}/IMainMenu.cs | 0 .../Input}/IMouseDevice.cs | 0 .../Input}/INavigableContainer.cs | 0 .../Input}/IPointer.cs | 0 .../Input}/IPointerDevice.cs | 0 .../Input}/InputElement.cs | 0 .../Input}/InputExtensions.cs | 0 .../Input}/InputManager.cs | 0 .../Input}/InputMethod.cs | 0 .../Input}/Key.cs | 0 .../Input}/KeyBinding.cs | 0 .../Input}/KeyEventArgs.cs | 0 .../Input}/KeyGesture.cs | 0 .../Input}/KeyboardDevice.cs | 0 .../Input}/KeyboardNavigation.cs | 0 .../Input}/KeyboardNavigationHandler.cs | 0 .../Input}/KeyboardNavigationMode.cs | 0 .../Input}/MouseDevice.cs | 0 .../Input}/Navigation/FocusExtensions.cs | 0 .../Input}/Navigation/TabNavigation.cs | 0 .../Input}/NavigationDirection.cs | 0 .../Input}/NavigationMethod.cs | 0 .../Input}/Platform/IClipboard.cs | 0 .../Input}/Platform/IPlatformDragSource.cs | 0 .../Platform/PlatformHotkeyConfiguration.cs | 0 .../Input}/Pointer.cs | 0 .../Input}/PointerDeltaEventArgs.cs | 0 .../Input}/PointerEventArgs.cs | 0 .../Input}/PointerPoint.cs | 0 .../Input}/PointerWheelEventArgs.cs | 0 .../Input}/Raw/IDragDropDevice.cs | 0 .../Input}/Raw/RawDragEvent.cs | 0 .../Input}/Raw/RawDragEventType.cs | 0 .../Input}/Raw/RawInputEventArgs.cs | 0 .../Input}/Raw/RawKeyEventArgs.cs | 0 .../Input}/Raw/RawMouseWheelEventArgs.cs | 0 .../Input}/Raw/RawPointerEventArgs.cs | 0 .../Input}/Raw/RawPointerGestureEventArgs.cs | 0 .../Input}/Raw/RawSizeEventArgs.cs | 0 .../Input}/Raw/RawTextInputEventArgs.cs | 0 .../Input}/Raw/RawTouchEventArgs.cs | 0 .../Input}/ScrollGestureEventArgs.cs | 0 .../Input}/TappedEventArgs.cs | 0 .../TextInput/ITextInputMethodClient.cs | 0 .../Input}/TextInput/ITextInputMethodImpl.cs | 0 .../Input}/TextInput/InputMethodManager.cs | 0 .../Input}/TextInput/TextInputContentType.cs | 0 ...TextInputMethodClientRequestedEventArgs.cs | 0 .../Input}/TextInput/TextInputOptions.cs | 0 .../TextInput/TransformTrackingHelper.cs | 0 .../Input}/TextInputEventArgs.cs | 0 .../Input}/TouchDevice.cs | 0 .../Input}/VectorEventArgs.cs | 0 .../Interactivity}/EventRoute.cs | 0 .../Interactivity}/IInteractive.cs | 0 .../Interactivity}/Interactive.cs | 0 .../Interactivity}/InteractiveExtensions.cs | 0 .../Interactivity}/RoutedEvent.cs | 0 .../Interactivity}/RoutedEventArgs.cs | 0 .../Interactivity}/RoutedEventRegistry.cs | 0 .../Layout}/AttachedLayout.cs | 0 .../EffectiveViewportChangedEventArgs.cs | 0 .../Layout}/ElementManager.cs | 0 .../Layout}/FlowLayoutAlgorithm.cs | 0 .../Layout}/IEmbeddedLayoutRoot.cs | 0 .../Layout}/IFlowLayoutAlgorithmDelegates.cs | 0 .../Layout}/ILayoutManager.cs | 0 .../Layout}/ILayoutRoot.cs | 0 .../Layout}/ILayoutable.cs | 0 .../Layout}/LayoutContext.cs | 0 .../Layout}/LayoutContextAdapter.cs | 0 .../Layout}/LayoutExtensions.cs | 0 .../Layout}/LayoutHelper.cs | 0 .../Layout}/LayoutManager.cs | 0 .../Layout}/LayoutQueue.cs | 0 .../Layout}/Layoutable.cs | 0 .../Layout}/NonVirtualizingLayout.cs | 0 .../Layout}/NonVirtualizingLayoutContext.cs | 0 .../Layout}/NonVirtualizingStackLayout.cs | 0 .../Layout}/Orientation.cs | 0 .../Layout}/OrientationBasedMeasures.cs | 0 .../Layout}/StackLayout.cs | 0 .../Layout}/StackLayoutState.cs | 0 .../Layout}/UniformGridLayout.cs | 0 .../Layout}/UniformGridLayoutState.cs | 0 .../Layout}/Utils/ListUtils.cs | 0 .../Layout}/VirtualLayoutContextAdapter.cs | 0 .../Layout}/VirtualizingLayout.cs | 0 .../Layout}/VirtualizingLayoutContext.cs | 0 .../Layout}/WrapLayout/UvBounds.cs | 0 .../Layout}/WrapLayout/UvMeasure.cs | 0 .../Layout}/WrapLayout/WrapItem.cs | 0 .../Layout}/WrapLayout/WrapLayout.cs | 0 .../Layout}/WrapLayout/WrapLayoutState.cs | 0 .../LogicalTree/ChildIndexChangedEventArgs.cs | 0 .../LogicalTree/ControlLocator.cs | 0 .../LogicalTree/IChildIndexProvider.cs | 0 .../LogicalTree/ILogical.cs | 0 .../LogicalTree/ILogicalRoot.cs | 0 .../LogicalTree/LogicalExtensions.cs | 0 .../LogicalTreeAttachmentEventArgs.cs | 0 .../Matrix.cs | 0 .../Media/AcrylicBackgroundSource.cs | 0 .../Media/AlignmentX.cs | 0 .../Media/AlignmentY.cs | 0 .../Media/ArcSegment.cs | 0 .../Media/BaselineAlignment.cs | 0 .../Media/BezierSegment .cs | 0 .../Media/BoxShadow.cs | 0 .../Media/BoxShadows.cs | 0 .../Media/Brush.cs | 0 .../Media/BrushConverter.cs | 0 .../Media/BrushExtensions.cs | 0 .../Media/BrushMappingMode.cs | 0 .../Media/Brushes.cs | 0 .../Media/CharacterHit.cs | 0 .../Media/Color.cs | 0 .../Media/Colors.cs | 0 .../Media/ConicGradientBrush.cs | 0 .../Media/DashStyle.cs | 0 .../Media/Drawing.cs | 0 .../Media/DrawingContext.cs | 2 +- .../Media/DrawingGroup.cs | 0 .../Media/DrawingImage.cs | 4 +- .../Media/EllipseGeometry.cs | 0 .../Media/ExperimentalAcrylicMaterial.cs | 0 .../Media/FillRule.cs | 0 .../Media/FlowDirection.cs | 0 .../Media/FontFallback.cs | 0 .../Media/FontFamily.cs | 0 .../Media/FontManager.cs | 0 .../Media/FontManagerOptions.cs | 0 .../Media/FontStretch.cs | 0 .../Media/FontStyle.cs | 0 .../Media/FontWeight.cs | 0 .../Media/Fonts/FamilyNameCollection.cs | 0 .../Media/Fonts/FontFamilyKey.cs | 0 .../Media/Fonts/FontFamilyLoader.cs | 0 .../Media/FormattedText.cs | 0 .../Media/Geometry.cs | 0 .../Media/GeometryDrawing.cs | 0 .../Media/GlyphRun.cs | 0 .../Media/GlyphRunDrawing.cs | 0 .../Media/GlyphRunMetrics.cs | 0 .../Media/GlyphTypeface.cs | 0 .../Media/GradientBrush.cs | 0 .../Media/GradientSpreadMethod.cs | 0 .../Media/GradientStop.cs | 0 .../Media/GradientStops.cs | 0 .../Media/HslColor.cs | 0 .../Media/HsvColor.cs | 0 .../Media/IAffectsRender.cs | 0 .../Media/IBrush.cs | 0 .../Media/IConicGradientBrush.cs | 0 .../Media/IDashStyle.cs | 0 .../Media/IExperimentalAcrylicMaterial.cs | 0 .../Media/IGradientBrush.cs | 0 .../Media/IGradientStop.cs | 0 .../Media/IImage.cs | 3 +- .../Media/IImageBrush.cs | 0 .../Media/ILinearGradientBrush.cs | 0 .../Media/IMutableBrush.cs | 0 .../IMutableExperimentalAcrylicMaterial.cs | 0 .../Media/IMutableTransform.cs | 0 .../Media/IPen.cs | 0 .../Media/IRadialGradientBrush.cs | 0 .../Media/ISolidColorBrush.cs | 0 .../Media/ITileBrush.cs | 2 +- .../Media/ITransform.cs | 0 .../Media/IVisualBrush.cs | 0 .../Media/ImageBrush.cs | 0 .../Media/ImageDrawing.cs | 0 .../Media/Imaging/Bitmap.cs | 1 - .../Media/Imaging/BitmapBlendingMode.cs | 2 +- .../Media/Imaging/BitmapInterpolationMode.cs | 2 +- .../Media/Imaging/CroppedBitmap.cs | 2 +- .../Media/Imaging/IBitmap.cs | 0 .../Media/Imaging/RenderTargetBitmap.cs | 0 .../Media/Imaging/WriteableBitmap.cs | 2 - .../Immutable/ImmutableConicGradientBrush.cs | 0 .../Media/Immutable/ImmutableDashStyle.cs | 0 .../Media/Immutable/ImmutableGradientBrush.cs | 0 .../Media/Immutable/ImmutableGradientStop.cs | 0 .../Media/Immutable/ImmutableImageBrush.cs | 1 - .../Immutable/ImmutableLinearGradientBrush.cs | 0 .../Media/Immutable/ImmutablePen.cs | 0 .../Immutable/ImmutableRadialGradientBrush.cs | 0 .../Immutable/ImmutableSolidColorBrush.cs | 0 .../Immutable/ImmutableTextDecoration.cs | 0 .../Media/Immutable/ImmutableTileBrush.cs | 2 +- .../Media/Immutable/ImmutableTransform.cs | 0 .../Media/Immutable/ImmutableVisualBrush.cs | 4 +- .../ImmutableExperimentalAcrylicMaterial.cs | 0 .../Media/KnownColors.cs | 0 .../Media/LineGeometry.cs | 0 .../Media/LineSegment.cs | 0 .../Media/LinearGradientBrush.cs | 0 .../Media/MaterialExtensions.cs | 0 .../Media/MatrixTransform.cs | 0 .../Media/MediaExtensions.cs | 0 .../Media/PathFigure.cs | 0 .../Media/PathGeometry.cs | 0 .../Media/PathGeometryCollections.cs | 0 .../Media/PathMarkupParser.cs | 0 .../Media/PathSegment.cs | 0 .../Media/Pen.cs | 0 .../Media/PenLineCap.cs | 0 .../Media/PenLineJoin.cs | 0 .../Media/PolyLineSegment.cs | 0 .../Media/PolylineGeometry.cs | 0 .../Media/PreciseEllipticArcHelper.cs | 0 .../Media/QuadraticBezierSegment .cs | 0 .../Media/RadialGradientBrush.cs | 0 .../Media/RectangleGeometry.cs | 0 .../Media/RenderOptions.cs | 2 +- .../Media/RotateTransform.cs | 0 .../Media/ScaleTransform.cs | 0 .../Media/SkewTransform.cs | 0 .../Media/SolidColorBrush.cs | 0 .../Media/StreamGeometry.cs | 0 .../Media/StreamGeometryContext.cs | 0 .../Media/Stretch.cs | 0 .../Media/StretchDirection.cs | 0 .../Media/SweepDirection.cs | 0 .../Media/TextAlignment.cs | 0 .../Media/TextCollapsingCreateInfo.cs | 0 .../Media/TextDecoration.cs | 0 .../Media/TextDecorationCollection.cs | 0 .../Media/TextDecorationLocation.cs | 0 .../Media/TextDecorationUnit.cs | 0 .../Media/TextDecorations.cs | 0 .../Media/TextFormatting/DrawableTextRun.cs | 0 .../Media/TextFormatting/FontMetrics.cs | 0 .../TextFormatting/FormattedTextSource.cs | 0 .../GenericTextParagraphProperties.cs | 0 .../GenericTextRunProperties.cs | 0 .../Media/TextFormatting/ITextSource.cs | 0 .../Media/TextFormatting/LogicalDirection.cs | 0 .../TextFormatting/ShapeableTextCharacters.cs | 0 .../Media/TextFormatting/ShapedBuffer.cs | 0 .../TextFormatting/ShapedTextCharacters.cs | 0 .../Media/TextFormatting/SplitResult.cs | 0 .../Media/TextFormatting/TextBounds.cs | 0 .../Media/TextFormatting/TextCharacters.cs | 0 .../TextCollapsingProperties.cs | 0 .../TextFormatting/TextEllipsisHelper.cs | 0 .../Media/TextFormatting/TextEndOfLine.cs | 0 .../TextFormatting/TextEndOfParagraph.cs | 0 .../Media/TextFormatting/TextFormatter.cs | 0 .../Media/TextFormatting/TextFormatterImpl.cs | 0 .../Media/TextFormatting/TextLayout.cs | 0 .../TextLeadingPrefixCharacterEllipsis.cs | 0 .../Media/TextFormatting/TextLine.cs | 0 .../Media/TextFormatting/TextLineBreak.cs | 0 .../Media/TextFormatting/TextLineImpl.cs | 0 .../Media/TextFormatting/TextLineMetrics.cs | 0 .../TextFormatting/TextParagraphProperties.cs | 0 .../Media/TextFormatting/TextRange.cs | 0 .../Media/TextFormatting/TextRun.cs | 0 .../Media/TextFormatting/TextRunProperties.cs | 0 .../Media/TextFormatting/TextShaper.cs | 0 .../Media/TextFormatting/TextShaperOptions.cs | 0 .../TextTrailingCharacterEllipsis.cs | 0 .../TextTrailingWordEllipsis.cs | 0 .../TextFormatting/Unicode/BiDiAlgorithm.cs | 0 .../Media/TextFormatting/Unicode/BiDiClass.cs | 0 .../Media/TextFormatting/Unicode/BiDiData.cs | 0 .../Unicode/BiDiPairedBracketType.cs | 0 .../Unicode/BinaryReaderExtensions.cs | 0 .../Media/TextFormatting/Unicode/Codepoint.cs | 0 .../Unicode/CodepointEnumerator.cs | 0 .../TextFormatting/Unicode/GeneralCategory.cs | 0 .../Media/TextFormatting/Unicode/Grapheme.cs | 0 .../Unicode/GraphemeBreakClass.cs | 0 .../Unicode/GraphemeEnumerator.cs | 0 .../Media/TextFormatting/Unicode/LineBreak.cs | 0 .../TextFormatting/Unicode/LineBreakClass.cs | 0 .../Unicode/LineBreakEnumerator.cs | 0 .../Unicode/LineBreakPairTable.cs | 0 .../Unicode/PropertyValueAliasHelper.cs | 0 .../Media/TextFormatting/Unicode/Script.cs | 0 .../TextFormatting/Unicode/UnicodeData.cs | 0 .../TextFormatting/Unicode/UnicodeTrie.cs | 0 .../Unicode/UnicodeTrieBuilder.Constants.cs | 0 .../Unicode/UnicodeTrieBuilder.cs | 0 .../Media/TextHitTestResult.cs | 0 .../Media/TextLeadingPrefixTrimming.cs | 0 .../Media/TextNoneTrimming.cs | 0 .../Media/TextTrailingTrimming.cs | 0 .../Media/TextTrimming.cs | 0 .../Media/TextWrapping.cs | 0 .../Media/TileBrush.cs | 2 +- .../Media/Transform.cs | 0 .../Media/TransformConverter.cs | 0 .../Media/TransformExtensions.cs | 0 .../Media/TransformGroup.cs | 0 .../Transformation/InterpolationUtilities.cs | 0 .../Transformation/TransformOperation.cs | 0 .../Transformation/TransformOperations.cs | 0 .../Media/Transformation/TransformParser.cs | 0 .../Media/TranslateTransform.cs | 0 .../Media/Typeface.cs | 0 .../Media/UnicodeRange.cs | 0 .../Media/VisualBrush.cs | 0 .../Media => Avalonia.Base}/PixelPoint.cs | 0 .../Media => Avalonia.Base}/PixelRect.cs | 0 .../Media => Avalonia.Base}/PixelSize.cs | 0 .../Media => Avalonia.Base}/PixelVector.cs | 0 .../Platform/AlphaFormat.cs | 0 .../Platform/IBitmapImpl.cs | 0 .../Platform/ICursorFactory.cs | 0 .../Platform/ICursorImpl.cs | 0 .../Platform/IDrawingContextImpl.cs | 2 +- .../IDrawingContextWithAcrylicLikeSupport.cs | 0 .../Platform/IFontManagerImpl.cs | 0 .../Platform/IGeometryContext.cs | 0 .../Platform/IGeometryImpl.cs | 0 .../Platform/IGlyphRunImpl.cs | 0 .../Platform/IGlyphTypefaceImpl.cs | 0 .../Platform/ILockedFramebuffer.cs | 0 .../Platform/IModuleEnvironmentChecker.cs | 0 .../Platform/IPlatformRenderInterface.cs | 2 +- .../Platform/IPlatformSettings.cs | 0 .../Platform/IRenderTarget.cs | 0 .../Platform/IRenderTargetBitmapImpl.cs | 0 .../Platform/IStreamGeometryContextImpl.cs | 0 .../Platform/IStreamGeometryImpl.cs | 0 .../Platform/ITextShaperImpl.cs | 0 .../Platform/ITransformedGeometryImpl.cs | 0 .../Platform/IWriteableBitmapImpl.cs | 0 .../Platform/LockedFramebuffer.cs | 0 .../Platform/PathGeometryContext.cs | 0 .../Platform/PixelFormat.cs | 0 .../Point.cs | 0 .../Points.cs | 0 src/Avalonia.Base/Properties/AssemblyInfo.cs | 29 +- .../Rect.cs | 0 .../RelativePoint.cs | 0 .../RelativeRect.cs | 0 .../RenderTargetCorruptedException.cs | 0 .../Rendering/DefaultRenderTimer.cs | 0 .../Rendering/DeferredRenderer.cs | 0 .../Rendering/DirtyRects.cs | 0 .../Rendering/DirtyVisuals.cs | 0 .../Rendering/DisplayDirtyRect.cs | 0 .../Rendering/DisplayDirtyRects.cs | 0 .../Rendering/ICustomSimpleHitTest.cs | 0 .../Rendering/IDeferredRendererLock.cs | 0 .../Rendering/IRenderLoop.cs | 0 .../Rendering/IRenderLoopTask.cs | 0 .../Rendering/IRenderRoot.cs | 0 .../Rendering/IRenderTimer.cs | 0 .../Rendering/IRenderer.cs | 0 .../Rendering/IRendererFactory.cs | 0 .../Rendering/IVisualBrushInitialize.cs | 0 .../Rendering/IVisualBrushRenderer.cs | 0 .../Rendering/ImmediateRenderer.cs | 0 .../Rendering/ManagedDeferredRendererLock.cs | 0 .../Rendering/RenderLayer.cs | 0 .../Rendering/RenderLayers.cs | 0 .../Rendering/RenderLoop.cs | 0 .../Rendering/RendererBase.cs | 0 .../SceneGraph/BitmapBlendModeNode.cs | 2 +- .../SceneGraph/BrushDrawOperation.cs | 0 .../Rendering/SceneGraph/ClipNode.cs | 0 .../SceneGraph/CustomDrawOperation.cs | 0 .../SceneGraph/DeferredDrawingContextImpl.cs | 2 +- .../Rendering/SceneGraph/DrawOperation.cs | 0 .../Rendering/SceneGraph/EllipseNode.cs | 0 .../SceneGraph/ExperimentalAcrylicNode.cs | 0 .../SceneGraph/GeometryBoundsHelper.cs | 0 .../Rendering/SceneGraph/GeometryClipNode.cs | 0 .../Rendering/SceneGraph/GeometryNode.cs | 0 .../Rendering/SceneGraph/GlyphRunNode.cs | 0 .../Rendering/SceneGraph/IDrawOperation.cs | 0 .../Rendering/SceneGraph/ISceneBuilder.cs | 0 .../Rendering/SceneGraph/IVisualNode.cs | 0 .../Rendering/SceneGraph/ImageNode.cs | 2 +- .../Rendering/SceneGraph/LineBoundsHelper.cs | 0 .../Rendering/SceneGraph/LineNode.cs | 0 .../Rendering/SceneGraph/OpacityMaskNode.cs | 0 .../Rendering/SceneGraph/OpacityNode.cs | 0 .../Rendering/SceneGraph/RectangleNode.cs | 0 .../Rendering/SceneGraph/Scene.cs | 0 .../Rendering/SceneGraph/SceneBuilder.cs | 0 .../Rendering/SceneGraph/SceneLayer.cs | 0 .../Rendering/SceneGraph/SceneLayers.cs | 0 .../Rendering/SceneGraph/VisualNode.cs | 0 .../Rendering/SceneInvalidatedEventArgs.cs | 0 .../Rendering/SleepLoopRenderTimer.cs | 0 .../Rendering/UiThreadRenderTimer.cs | 0 .../Utilities/TileBrushCalculator.cs | 0 .../Rendering/ZIndexComparer.cs | 0 .../RoundedRect.cs | 0 src/Avalonia.Base/Settings.StyleCop | 1 - .../Size.cs | 0 .../StyledElement.cs | 0 .../StyledElementExtensions.cs | 0 .../Styling/Activators/AndActivator.cs | 0 .../Styling/Activators/AndActivatorBuilder.cs | 0 .../Styling/Activators/IStyleActivator.cs | 0 .../Styling/Activators/IStyleActivatorSink.cs | 0 .../Styling/Activators/NotActivator.cs | 0 .../Styling/Activators/NthChildActivator.cs | 0 .../Styling/Activators/OrActivator.cs | 0 .../Styling/Activators/OrActivatorBuilder.cs | 0 .../Activators/PropertyEqualsActivator.cs | 0 .../Styling/Activators/StyleActivatorBase.cs | 0 .../Styling/Activators/StyleClassActivator.cs | 0 .../Styling/ChildSelector.cs | 0 .../Styling/DescendentSelector.cs | 0 .../Styling/IGlobalStyles.cs | 0 .../Styling/ISetter.cs | 0 .../Styling/ISetterInstance.cs | 0 .../Styling/ISetterValue.cs | 0 .../Styling/IStyle.cs | 0 .../Styling/IStyleHost.cs | 0 .../Styling/IStyleInstance.cs | 0 .../Styling/IStyleable.cs | 0 .../Styling/IStyler.cs | 0 .../Styling/ITemplate.cs | 0 .../Styling/ITemplatedControl.cs | 0 .../Styling/NotSelector.cs | 0 .../Styling/NthChildSelector.cs | 0 .../Styling/NthLastChildSelector.cs | 0 .../Styling/OrSelector.cs | 0 .../Styling/PropertyEqualsSelector.cs | 0 .../Styling/PropertySetterBindingInstance.cs | 0 .../Styling/PropertySetterInstance.cs | 0 .../Styling/PropertySetterLazyInstance.cs | 0 .../Styling/Selector.cs | 0 .../Styling/SelectorMatch.cs | 0 .../Styling/Selectors.cs | 0 .../Styling/Setter.cs | 0 .../Styling/Style.cs | 0 .../Styling/StyleInstance.cs | 0 .../Styling/Styler.cs | 0 .../Styling/Styles.cs | 0 .../Styling/TemplateSelector.cs | 0 .../Styling/TypeNameAndClassSelector.cs | 0 .../Thickness.cs | 0 .../Utilities/ArrayBuilder.cs | 0 .../Utilities/ArraySlice.cs | 0 .../Utilities/BinarySearchExtension.cs | 0 .../Utilities/FrugalList.cs | 0 .../Utilities/MappedArraySlice.cs | 0 .../Utilities/ReadOnlySlice.cs | 0 .../Utilities/Span.cs | 0 .../Utilities/ValueSpan.cs | 0 .../Vector.cs | 0 .../Visual.cs | 0 .../VisualExtensions.cs | 0 .../VisualTree/IHostedVisualTreeRoot.cs | 0 .../VisualTree/IVisual.cs | 0 .../VisualTree/IVisualTreeHost.cs | 0 .../VisualTree/IVisualWithRoundRectClip.cs | 0 .../VisualTree/TransformedBounds.cs | 0 .../VisualTree/VisualExtensions.cs | 0 .../VisualTree/VisualLocator.cs | 0 .../VisualTreeAttachmentEventArgs.cs | 0 .../Avalonia.Build.Tasks.csproj | 20 +- .../Avalonia.Controls.DataGrid.csproj | 6 - .../Avalonia.Controls.csproj | 6 - .../Avalonia.DesignerSupport.csproj | 6 - .../Avalonia.Diagnostics.csproj | 6 - .../HeadlessPlatformRenderInterface.cs | 2 +- src/Avalonia.Input/ApiCompatBaseline.txt | 30 -- src/Avalonia.Input/Avalonia.Input.csproj | 19 -- src/Avalonia.Input/Properties/AssemblyInfo.cs | 10 - .../ApiCompatBaseline.txt | 1 - .../Avalonia.Interactivity.csproj | 15 - src/Avalonia.Layout/ApiCompatBaseline.txt | 1 - src/Avalonia.Layout/Avalonia.Layout.csproj | 14 - .../Properties/AssemblyInfo.cs | 7 - src/Avalonia.OpenGL/Avalonia.OpenGL.csproj | 1 - .../Avalonia.Remote.Protocol.csproj | 2 +- src/Avalonia.Styling/ApiCompatBaseline.txt | 4 - src/Avalonia.Styling/Avalonia.Styling.csproj | 14 - .../Properties/AssemblyInfo.cs | 9 - .../Avalonia.Themes.Default.csproj | 8 +- .../Avalonia.Themes.Fluent.csproj | 8 +- src/Avalonia.Visuals/ApiCompatBaseline.txt | 188 ----------- src/Avalonia.Visuals/Avalonia.Visuals.csproj | 23 -- .../Properties/AssemblyInfo.cs | 15 - src/Avalonia.X11/X11IconLoader.cs | 5 +- .../Avalonia.Markup.Xaml.csproj | 6 - .../Avalonia.Markup/Avalonia.Markup.csproj | 2 - src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 2 +- src/Skia/Avalonia.Skia/ImmutableBitmap.cs | 2 +- .../Avalonia.Skia/PlatformRenderInterface.cs | 2 +- src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs | 2 +- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 2 +- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 2 +- .../Media/DrawingContextImpl.cs | 4 +- .../Media/ImageBrushImpl.cs | 2 +- .../Media/Imaging/WicBitmapImpl.cs | 12 +- .../Media/Imaging/WriteableWicBitmapImpl.cs | 2 +- .../Avalonia.Designer.HostApp.csproj | 6 - .../Avalonia.Animation.UnitTests.csproj | 28 -- .../Properties/AssemblyInfo.cs | 7 - .../Animation}/AnimatableTests.cs | 6 +- .../Animation}/AnimationIterationTests.cs | 10 +- .../Animation}/BrushTransitionTests.cs | 7 +- .../Animation}/KeySplineTests.cs | 7 +- .../Animation}/TestClock.cs | 6 +- .../Animation}/TransitionsTests.cs | 4 +- .../Avalonia.Base.UnitTests.csproj | 5 + .../AvaloniaPropertyRegistryTests.cs | 5 +- .../CornerRadiusTests.cs | 5 +- .../DataAnnotationsValidationPluginTests.cs | 2 +- .../Data/DefaultValueConverterTests.cs | 2 +- .../Input}/GesturesTests.cs | 3 +- .../Input}/InputElement_Enabled.cs | 2 +- .../Input}/InputElement_Focus.cs | 3 +- .../Input}/KeyGestureTests.cs | 3 +- .../Input}/KeyboardDeviceTests.cs | 3 +- .../Input}/KeyboardNavigationTests_Custom.cs | 3 +- .../Input}/KeyboardNavigationTests_Tab.cs | 3 +- .../Input}/MouseDeviceTests.cs | 10 +- .../Input}/PointerTests.cs | 3 +- .../Input}/TouchDeviceTests.cs | 0 .../Interactivity}/InteractiveTests.cs | 3 +- .../RoutedEventRegistryTests.cs | 3 +- .../Layout}/ArrangeTests.cs | 3 +- .../Layout}/FullLayoutTests.cs | 4 +- .../Layout}/LayoutHelperTests.cs | 3 +- .../Layout}/LayoutManagerTests.cs | 3 +- .../Layout}/LayoutQueueTests.cs | 3 +- .../Layout}/LayoutTestControl.cs | 3 +- .../Layout}/LayoutTestRoot.cs | 3 +- .../Layout}/LayoutableTests.cs | 3 +- ...ayoutableTests_EffectiveViewportChanged.cs | 3 +- .../Layout}/MeasureTests.cs | 2 +- .../NonVirtualizingStackLayoutTests.cs | 3 +- .../Media/BoxShadowTests.cs | 2 +- .../Media/BrushTests.cs | 2 +- .../Media/ColorTests.cs | 2 +- .../Media/FontFamilyTests.cs | 2 +- .../Media/FontManagerTests.cs | 2 +- .../Media/Fonts/FamilyNameCollectionTests.cs | 2 +- .../Media/Fonts/FontFamilyKeyTests.cs | 2 +- .../Media/Fonts/FontFamilyLoaderTests.cs | 7 +- .../Media/GeometryGroupTests.cs | 0 .../Media/GeometryTests.cs | 2 +- .../Media/GlyphRunTests.cs | 2 +- .../Media/ImageBrushTests.cs | 7 +- .../Media/LinearGradientBrushTests.cs | 5 +- .../Media/MatrixTests.cs | 2 +- .../Media/PathMarkupParserTests.cs | 11 +- .../Media/PathSegmentTests.cs | 2 +- .../Media/PenTests.cs | 2 +- .../Media/PixelRectTests.cs | 7 +- .../Media/RectTests.cs | 7 +- .../Media/RectangleGeometryTests.cs | 5 +- .../Media/SolidColorBrushTests.cs | 5 +- .../Media/TextDecorationTests.cs | 2 +- .../TextFormatting/BiDiAlgorithmTests.cs | 0 .../BiDiClassTestDataGenerator.cs | 1 + .../Media/TextFormatting/BiDiClassTests.cs | 0 .../TextFormatting/BiDiTestDataGenerator.cs | 1 + .../Media/TextFormatting/BreakPairTable.txt | 0 .../GraphemeBreakClassTrieGenerator.cs | 2 +- .../GraphemeBreakClassTrieGeneratorTests.cs | 2 +- .../LineBreakEnumuratorTests.cs | 2 +- .../Media/TextFormatting/TestDataGenerator.cs | 2 +- .../TextFormatting/UnicodeDataGenerator.cs | 2 +- .../UnicodeDataGeneratorTests.cs | 2 +- .../TextFormatting/UnicodeEnumsGenerator.cs | 2 +- .../Media/TransformOperationsTests.cs | 2 +- .../Media/TypefaceTests.cs | 2 +- .../Media/UnicodeRangeSegmentTests.cs | 0 .../Media/UnicodeRangeTests.cs | 0 .../RectTests.cs | 2 +- .../RelativePointTests.cs | 3 +- .../RelativeRectComparer.cs | 2 +- .../RelativeRectTests.cs | 3 +- .../RenderTests_Culling.cs | 8 +- .../Rendering/CustomHitTestBorder.cs | 3 +- .../Rendering/DeferredRendererTests.cs | 7 +- .../DeferredRendererTests_HitTesting.cs | 3 +- .../Rendering/ImmediateRendererTests.cs | 2 +- .../ImmediateRendererTests_HitTesting.cs | 2 +- .../Rendering/RenderLoopTests.cs | 4 +- .../DeferredDrawingContextImplTests.cs | 6 +- .../SceneGraph/DrawOperationTests.cs | 4 +- .../Rendering/SceneGraph/EllipseNodeTests.cs | 0 .../Rendering/SceneGraph/LineNodeTests.cs | 0 .../Rendering/SceneGraph/SceneBuilderTests.cs | 19 +- .../SceneGraph/SceneBuilderTests_Layers.cs | 10 +- .../Rendering/SceneGraph/SceneLayersTests.cs | 5 +- .../Rendering/SceneGraph/SceneTests.cs | 5 +- .../Rendering/SceneGraph/VisualNodeTests.cs | 5 +- .../Utilities/TileBrushCalculatorTests.cs | 5 +- .../SizeTests.cs | 2 +- .../Styling}/ResourceDictionaryTests.cs | 2 +- .../Styling}/SelectorTests_Child.cs | 4 +- .../Styling}/SelectorTests_Class.cs | 8 +- .../Styling}/SelectorTests_Descendent.cs | 4 +- .../Styling}/SelectorTests_Multiple.cs | 4 +- .../Styling}/SelectorTests_Name.cs | 3 +- .../Styling}/SelectorTests_Not.cs | 4 +- .../Styling}/SelectorTests_NthChild.cs | 3 +- .../Styling}/SelectorTests_NthLastChild.cs | 3 +- .../Styling}/SelectorTests_OfType.cs | 3 +- .../Styling}/SelectorTests_Or.cs | 3 +- .../Styling}/SelectorTests_PropertyEquals.cs | 4 +- .../Styling}/SelectorTests_Template.cs | 11 +- .../Styling}/SetterTests.cs | 3 +- .../Styling}/StyleActivatorExtensions.cs | 2 +- .../Styling}/StyleTests.cs | 3 +- .../Styling}/StyledElementTests.cs | 16 +- .../Styling}/StyledElementTests_Resources.cs | 4 +- .../Styling}/StylesTests.cs | 4 +- .../Styling}/TestObservable.cs | 2 +- .../Styling}/TestObserver.cs | 2 +- .../Styling}/TestSelectors.cs | 2 +- .../Styling}/TestSubject.cs | 2 +- .../TestVisual.cs | 3 +- .../ThicknessTests.cs | 5 +- .../Utilities/ReadOnlySpanTests.cs | 2 +- .../VectorTests.cs | 5 +- .../VisualExtensionsTests.cs | 2 +- .../VisualTests.cs | 6 +- .../VisualTree/MockRenderInterface.cs | 4 +- .../VisualTree/TransformedBoundsTests.cs | 14 +- .../VisualExtensions_GetVisualsAt.cs | 2 +- .../Avalonia.Benchmarks.csproj | 6 - .../NullDrawingContextImpl.cs | 2 +- .../NullRenderingPlatform.cs | 2 +- .../Avalonia.Controls.UnitTests.csproj | 6 - .../Avalonia.DesignerSupport.TestApp.csproj | 6 - .../Avalonia.Direct2D1.RenderTests.csproj | 6 - .../Avalonia.Direct2D1.UnitTests.csproj | 6 - .../Avalonia.Input.UnitTests.csproj | 27 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.Interactivity.UnitTests.csproj | 27 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.Layout.UnitTests.csproj | 29 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.LeakTests.csproj | 6 - .../Avalonia.Markup.UnitTests.csproj | 6 - .../Avalonia.Markup.Xaml.UnitTests.csproj | 6 - .../Avalonia.Skia.RenderTests.csproj | 6 - .../Avalonia.Skia.UnitTests.csproj | 6 - .../Avalonia.Styling.UnitTests.csproj | 30 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.UnitTests.csproj | 6 - .../MockPlatformRenderInterface.cs | 2 +- .../Avalonia.Visuals.UnitTests.csproj | 40 --- .../Properties/AssemblyInfo.cs | 7 - 837 files changed, 437 insertions(+), 1366 deletions(-) delete mode 100644 src/Avalonia.Animation/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Animation/Avalonia.Animation.csproj delete mode 100644 src/Avalonia.Animation/Properties/AssemblyInfo.cs rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animatable.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animation.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimationInstance`1.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimatorDrivenTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimatorKeyFrame.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimatorTransitionObservable.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Animator`1.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/BaseBrushAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/BoolAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/BoxShadowAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/BoxShadowsAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/ByteAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/ColorAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/CornerRadiusAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/DecimalAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/DoubleAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/FloatAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/GradientBrushAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Int16Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Int32Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Int64Animator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/PointAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/RectAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/RelativePointAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/SizeAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/SolidColorBrushAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/ThicknessAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/TransformAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/TransformOperationsAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/UInt16Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/UInt32Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/UInt64Animator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/VectorAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Clock.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/ClockBase.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/CompositePageTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/CrossFade.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Cue.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/DisposeAnimationInstanceSubject.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BackEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BackEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BackEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BounceEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BounceEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BounceEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CircularEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CircularEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CircularEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CubicEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CubicEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CubicEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/Easing.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/EasingTypeConverter.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ElasticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ElasticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ElasticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ExponentialEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ExponentialEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ExponentialEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/IEasing.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/LinearEasing.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuadraticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuadraticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuadraticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuarticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuarticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuarticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuinticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuinticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuinticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SineEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SineEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SineEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SplineEasing.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/FillMode.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IAnimation.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IAnimationSetter.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IClock.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IGlobalClock.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/IPageTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/ITransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IterationCount.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IterationCountTypeConverter.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeyFrame.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeyFrames.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeySpline.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeySplineTypeConverter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/PageSlide.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/PlayState.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/PlaybackDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/RenderLoopClock.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/TransitionInstance.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/TransitionObservableBase.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/BoxShadowsTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/BrushTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/ColorTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/CornerRadiusTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions/DoubleTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions/FloatTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions/IntegerTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/PointTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/RelativePointTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/SizeTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/ThicknessTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/TransformOperationsTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/VectorTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Utils/BounceEaseUtils.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Utils/EasingUtils.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Assets/BiDi.trie (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Assets/GraphemeBreak.trie (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Assets/UnicodeData.trie (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/AvaloniaPropertyExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/ClassBindingManager.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/CombinedGeometry.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ChildNameScope.cs (99%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/Classes.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/INameScope.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IPseudoClasses.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceDictionary.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceHost.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceNode.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceProvider.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ISetInheritanceParent.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ISetLogicalParent.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/Metadata/PseudoClassesAttribute.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/Metadata/TemplatePartAttribute.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScope.cs (99%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScopeEventArgs.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScopeExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScopeLocator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/PseudoClassesExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ResourceDictionary.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ResourceNodeExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ResourcesChangedEventArgs.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/CornerRadius.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Diagnostics/StyleDiagnostics.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Diagnostics/StyledElementExtensions.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/GeometryCollection.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/GeometryGroup.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/IDataContextProvider.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/INamed.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/IStyledElement.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/AccessKeyHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Cursor.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DataFormats.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DataObject.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragDrop.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragDropDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragDropEffects.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/FocusManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GestureRecognizers/GestureRecognizerCollection.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GestureRecognizers/IGestureRecognizer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GestureRecognizers/ScrollGestureRecognizer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Gestures.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GotFocusEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IAccessKeyHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IClickableControl.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ICloseable.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ICommandSource.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ICustomKeyboardNavigation.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IDataObject.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IFocusManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IFocusScope.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputElement.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputRoot.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IKeyboardDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IKeyboardNavigationHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IMainMenu.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IMouseDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/INavigableContainer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IPointer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IPointerDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputElement.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputExtensions.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputMethod.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Key.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyBinding.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyGesture.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardNavigation.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardNavigationHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardNavigationMode.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/MouseDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Navigation/FocusExtensions.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Navigation/TabNavigation.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/NavigationDirection.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/NavigationMethod.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Platform/IClipboard.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Platform/IPlatformDragSource.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Platform/PlatformHotkeyConfiguration.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Pointer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerDeltaEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerPoint.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerWheelEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/IDragDropDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawDragEvent.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawDragEventType.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawInputEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawKeyEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawMouseWheelEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawPointerEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawPointerGestureEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawSizeEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawTextInputEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawTouchEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ScrollGestureEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TappedEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/ITextInputMethodClient.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/ITextInputMethodImpl.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/InputMethodManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TextInputContentType.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TextInputMethodClientRequestedEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TextInputOptions.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TransformTrackingHelper.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInputEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TouchDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/VectorEventArgs.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/EventRoute.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/IInteractive.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/Interactive.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/InteractiveExtensions.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/RoutedEvent.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/RoutedEventArgs.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/RoutedEventRegistry.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/AttachedLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/EffectiveViewportChangedEventArgs.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ElementManager.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/FlowLayoutAlgorithm.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/IEmbeddedLayoutRoot.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/IFlowLayoutAlgorithmDelegates.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ILayoutManager.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ILayoutRoot.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ILayoutable.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutContext.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutContextAdapter.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutExtensions.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutHelper.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutManager.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutQueue.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/Layoutable.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/NonVirtualizingLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/NonVirtualizingLayoutContext.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/NonVirtualizingStackLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/Orientation.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/OrientationBasedMeasures.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/StackLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/StackLayoutState.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/UniformGridLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/UniformGridLayoutState.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/Utils/ListUtils.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/VirtualLayoutContextAdapter.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/VirtualizingLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/VirtualizingLayoutContext.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/UvBounds.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/UvMeasure.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/WrapItem.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/WrapLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/WrapLayoutState.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ChildIndexChangedEventArgs.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ControlLocator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/IChildIndexProvider.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ILogical.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ILogicalRoot.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/LogicalExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/LogicalTreeAttachmentEventArgs.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Matrix.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/AcrylicBackgroundSource.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/AlignmentX.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/AlignmentY.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ArcSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BaselineAlignment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BezierSegment .cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BoxShadow.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BoxShadows.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Brush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BrushConverter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BrushExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BrushMappingMode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Brushes.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/CharacterHit.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Color.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Colors.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ConicGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DashStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Drawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DrawingContext.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DrawingGroup.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DrawingImage.cs (96%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/EllipseGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FillRule.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FlowDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontFallback.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontFamily.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontManager.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontManagerOptions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontStretch.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontWeight.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Fonts/FamilyNameCollection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Fonts/FontFamilyKey.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Fonts/FontFamilyLoader.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FormattedText.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Geometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GeometryDrawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphRun.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphRunDrawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphRunMetrics.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphTypeface.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientSpreadMethod.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientStop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientStops.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/HslColor.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/HsvColor.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IAffectsRender.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IConicGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IDashStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IGradientStop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IImage.cs (93%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IImageBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ILinearGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IMutableBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IMutableExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IMutableTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IPen.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IRadialGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ISolidColorBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ITileBrush.cs (97%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ITransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IVisualBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ImageBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ImageDrawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/Bitmap.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/BitmapBlendingMode.cs (97%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/BitmapInterpolationMode.cs (93%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/CroppedBitmap.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/IBitmap.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/RenderTargetBitmap.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/WriteableBitmap.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableConicGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableDashStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableGradientStop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableImageBrush.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableLinearGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutablePen.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableRadialGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableSolidColorBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableTextDecoration.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableTileBrush.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableVisualBrush.cs (94%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ImmutableExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/KnownColors.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/LineGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/LineSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/LinearGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/MaterialExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/MatrixTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/MediaExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathFigure.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathGeometryCollections.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathMarkupParser.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Pen.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PenLineCap.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PenLineJoin.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PolyLineSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PolylineGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PreciseEllipticArcHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/QuadraticBezierSegment .cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RadialGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RectangleGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RenderOptions.cs (97%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RotateTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ScaleTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/SkewTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/SolidColorBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/StreamGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/StreamGeometryContext.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Stretch.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/StretchDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/SweepDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextAlignment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextCollapsingCreateInfo.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecoration.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorationCollection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorationLocation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorationUnit.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorations.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/DrawableTextRun.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/FontMetrics.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/FormattedTextSource.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/GenericTextParagraphProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/GenericTextRunProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ITextSource.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/LogicalDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ShapeableTextCharacters.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ShapedBuffer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ShapedTextCharacters.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/SplitResult.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextBounds.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextCharacters.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextCollapsingProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextEllipsisHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextEndOfLine.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextEndOfParagraph.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextFormatter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextFormatterImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLayout.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLine.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLineBreak.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLineImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLineMetrics.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextParagraphProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextRange.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextRun.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextRunProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextShaper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextShaperOptions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextTrailingCharacterEllipsis.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextTrailingWordEllipsis.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiAlgorithm.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiClass.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiData.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/Codepoint.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/CodepointEnumerator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/GeneralCategory.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/Grapheme.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/GraphemeBreakClass.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/GraphemeEnumerator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreak.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreakClass.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreakEnumerator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreakPairTable.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/Script.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeData.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeTrie.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextHitTestResult.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextLeadingPrefixTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextNoneTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextTrailingTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextWrapping.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TileBrush.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TransformConverter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TransformExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TransformGroup.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/InterpolationUtilities.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/TransformOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/TransformOperations.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/TransformParser.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TranslateTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Typeface.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/UnicodeRange.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/VisualBrush.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelPoint.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelRect.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelSize.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelVector.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/AlphaFormat.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IBitmapImpl.cs (100%) rename src/{Avalonia.Input => Avalonia.Base}/Platform/ICursorFactory.cs (100%) rename src/{Avalonia.Input => Avalonia.Base}/Platform/ICursorImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IDrawingContextImpl.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IDrawingContextWithAcrylicLikeSupport.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IFontManagerImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGeometryContext.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGeometryImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGlyphRunImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGlyphTypefaceImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/ILockedFramebuffer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IModuleEnvironmentChecker.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IPlatformRenderInterface.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IPlatformSettings.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IRenderTarget.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IRenderTargetBitmapImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IStreamGeometryContextImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IStreamGeometryImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/ITextShaperImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/ITransformedGeometryImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IWriteableBitmapImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/LockedFramebuffer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/PathGeometryContext.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/PixelFormat.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Point.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Points.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rect.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RelativePoint.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RelativeRect.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RenderTargetCorruptedException.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DefaultRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DeferredRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DirtyRects.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DirtyVisuals.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DisplayDirtyRect.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DisplayDirtyRects.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ICustomSimpleHitTest.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IDeferredRendererLock.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderLoop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderLoopTask.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderRoot.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRendererFactory.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IVisualBrushInitialize.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IVisualBrushRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ImmediateRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ManagedDeferredRendererLock.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RenderLayer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RenderLayers.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RenderLoop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RendererBase.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/BitmapBlendModeNode.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/BrushDrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ClipNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/CustomDrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/DeferredDrawingContextImpl.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/DrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/EllipseNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ExperimentalAcrylicNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GeometryBoundsHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GeometryClipNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GeometryNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GlyphRunNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/IDrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ISceneBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/IVisualNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ImageNode.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/LineBoundsHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/LineNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/OpacityMaskNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/OpacityNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/RectangleNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/Scene.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/SceneBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/SceneLayer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/SceneLayers.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/VisualNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneInvalidatedEventArgs.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SleepLoopRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/UiThreadRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/Utilities/TileBrushCalculator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ZIndexComparer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RoundedRect.cs (100%) delete mode 100644 src/Avalonia.Base/Settings.StyleCop rename src/{Avalonia.Visuals => Avalonia.Base}/Size.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/StyledElement.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/StyledElementExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/AndActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/AndActivatorBuilder.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/IStyleActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/IStyleActivatorSink.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/NotActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/NthChildActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/OrActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/OrActivatorBuilder.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/PropertyEqualsActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/StyleActivatorBase.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/StyleClassActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ChildSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/DescendentSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IGlobalStyles.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ISetter.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ISetterInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ISetterValue.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyle.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyleHost.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyleInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyleable.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyler.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ITemplate.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ITemplatedControl.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/NotSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/NthChildSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/NthLastChildSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/OrSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertyEqualsSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertySetterBindingInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertySetterInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertySetterLazyInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Selector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/SelectorMatch.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Selectors.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Setter.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Style.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/StyleInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Styler.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Styles.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/TemplateSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/TypeNameAndClassSelector.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Thickness.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ArrayBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ArraySlice.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/BinarySearchExtension.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/FrugalList.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/MappedArraySlice.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ReadOnlySlice.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/Span.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ValueSpan.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Vector.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Visual.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IHostedVisualTreeRoot.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IVisual.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IVisualTreeHost.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IVisualWithRoundRectClip.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/TransformedBounds.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/VisualExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/VisualLocator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTreeAttachmentEventArgs.cs (100%) delete mode 100644 src/Avalonia.Input/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Input/Avalonia.Input.csproj delete mode 100644 src/Avalonia.Input/Properties/AssemblyInfo.cs delete mode 100644 src/Avalonia.Interactivity/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Interactivity/Avalonia.Interactivity.csproj delete mode 100644 src/Avalonia.Layout/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Layout/Avalonia.Layout.csproj delete mode 100644 src/Avalonia.Layout/Properties/AssemblyInfo.cs delete mode 100644 src/Avalonia.Styling/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Styling/Avalonia.Styling.csproj delete mode 100644 src/Avalonia.Styling/Properties/AssemblyInfo.cs delete mode 100644 src/Avalonia.Visuals/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Visuals/Avalonia.Visuals.csproj delete mode 100644 src/Avalonia.Visuals/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj delete mode 100644 tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/AnimatableTests.cs (99%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/AnimationIterationTests.cs (97%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/BrushTransitionTests.cs (93%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/KeySplineTests.cs (97%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/TestClock.cs (90%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/TransitionsTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/CornerRadiusTests.cs (93%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/GesturesTests.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/InputElement_Enabled.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/InputElement_Focus.cs (99%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyGestureTests.cs (97%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyboardDeviceTests.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyboardNavigationTests_Custom.cs (99%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyboardNavigationTests_Tab.cs (99%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/MouseDeviceTests.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/PointerTests.cs (95%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/TouchDeviceTests.cs (100%) rename tests/{Avalonia.Interactivity.UnitTests => Avalonia.Base.UnitTests/Interactivity}/InteractiveTests.cs (99%) rename tests/{Avalonia.Interactivity.UnitTests => Avalonia.Base.UnitTests/Interactivity}/RoutedEventRegistryTests.cs (95%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/ArrangeTests.cs (98%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/FullLayoutTests.cs (98%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutHelperTests.cs (92%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutManagerTests.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutQueueTests.cs (98%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutTestControl.cs (93%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutTestRoot.cs (94%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutableTests.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutableTests_EffectiveViewportChanged.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/MeasureTests.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/NonVirtualizingStackLayoutTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/BoxShadowTests.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/BrushTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/ColorTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/FontFamilyTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/FontManagerTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/Fonts/FamilyNameCollectionTests.cs (91%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/Fonts/FontFamilyKeyTests.cs (95%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/Fonts/FontFamilyLoaderTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/GeometryGroupTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/GeometryTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/GlyphRunTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/ImageBrushTests.cs (78%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/LinearGradientBrushTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/MatrixTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PathMarkupParserTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PathSegmentTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PenTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PixelRectTests.cs (90%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/RectTests.cs (74%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/RectangleGeometryTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/SolidColorBrushTests.cs (82%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextDecorationTests.cs (95%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiAlgorithmTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiClassTestDataGenerator.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiClassTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiTestDataGenerator.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BreakPairTable.txt (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/LineBreakEnumuratorTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/TestDataGenerator.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/UnicodeDataGenerator.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/UnicodeDataGeneratorTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/UnicodeEnumsGenerator.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TransformOperationsTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TypefaceTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/UnicodeRangeSegmentTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/UnicodeRangeTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RectTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RelativePointTests.cs (89%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RelativeRectComparer.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RelativeRectTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RenderTests_Culling.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/CustomHitTestBorder.cs (84%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/DeferredRendererTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/DeferredRendererTests_HitTesting.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/ImmediateRendererTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/ImmediateRendererTests_HitTesting.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/RenderLoopTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/DrawOperationTests.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/EllipseNodeTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/LineNodeTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneBuilderTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneBuilderTests_Layers.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneLayersTests.cs (89%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneTests.cs (90%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/VisualNodeTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/Utilities/TileBrushCalculatorTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/SizeTests.cs (92%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/ResourceDictionaryTests.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Child.cs (97%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Class.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Descendent.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Multiple.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Name.cs (96%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Not.cs (97%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_NthChild.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_NthLastChild.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_OfType.cs (95%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Or.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_PropertyEquals.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Template.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SetterTests.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyleActivatorExtensions.cs (97%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyleTests.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyledElementTests.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyledElementTests_Resources.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StylesTests.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestObservable.cs (90%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestObserver.cs (96%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestSelectors.cs (90%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestSubject.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/TestVisual.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/ThicknessTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Utilities/ReadOnlySpanTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VectorTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualExtensionsTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTree/MockRenderInterface.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTree/TransformedBoundsTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTree/VisualExtensions_GetVisualsAt.cs (98%) delete mode 100644 tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj delete mode 100644 tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj delete mode 100644 tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj delete mode 100644 tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj delete mode 100644 tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj delete mode 100644 tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs diff --git a/Avalonia.sln b/Avalonia.sln index 5043f474c2..a216229a5b 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -5,48 +5,26 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Visuals", "src\Avalonia.Visuals\Avalonia.Visuals.csproj", "{EB582467-6ABB-43A1-B052-E981BA910E3A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout", "src\Avalonia.Layout\Avalonia.Layout.csproj", "{42472427-4774-4C81-8AFF-9F27B8E31721}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{B39A8919-9F95-48FE-AD7B-76E08B509888}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input", "src\Avalonia.Input\Avalonia.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity", "src\Avalonia.Interactivity\Avalonia.Interactivity.csproj", "{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Styling", "src\Avalonia.Styling\Avalonia.Styling.csproj", "{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Default", "src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation", "src\Avalonia.Animation\Avalonia.Animation.csproj", "{D211E587-D8BC-45B9-95A4-F297C8FA5200}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Styling.UnitTests", "tests\Avalonia.Styling.UnitTests\Avalonia.Styling.UnitTests.csproj", "{47ECDF59-DEF8-4C53-87B1-2098A3429059}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.UnitTests", "tests\Avalonia.Controls.UnitTests\Avalonia.Controls.UnitTests.csproj", "{5CCB5571-7C30-4E7D-967D-0E2158EBD91F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Visuals.UnitTests", "tests\Avalonia.Visuals.UnitTests\Avalonia.Visuals.UnitTests.csproj", "{76716382-3159-460E-BDA6-C5715CF606D7}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base.UnitTests", "tests\Avalonia.Base.UnitTests\Avalonia.Base.UnitTests.csproj", "{2905FF23-53FB-45E6-AA49-6AF47A172056}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout.UnitTests", "tests\Avalonia.Layout.UnitTests\Avalonia.Layout.UnitTests.csproj", "{DB070A10-BF39-4752-8456-86E9D5928478}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity.UnitTests", "tests\Avalonia.Interactivity.UnitTests\Avalonia.Interactivity.UnitTests.csproj", "{08478EF5-44E8-42E9-92D6-15E00EC038D8}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input.UnitTests", "tests\Avalonia.Input.UnitTests\Avalonia.Input.UnitTests.csproj", "{AC18926A-E784-40FE-B09D-BB0FE2B599F0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}" @@ -185,8 +163,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{41B02319-965D-4945-8005-C1A3D1224165}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}" @@ -279,54 +255,6 @@ Global {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhone.Build.0 = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhone.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhone.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -375,54 +303,6 @@ Global {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhone.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhone.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -447,30 +327,6 @@ Global {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhone.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -519,54 +375,6 @@ Global {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhone.Build.0 = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhone.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhone.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -591,30 +399,6 @@ Global {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhone.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhone.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -639,54 +423,6 @@ Global {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhone.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhone.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhone.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -711,30 +447,6 @@ Global {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhone.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhone.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -1641,30 +1353,6 @@ Global {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -2272,14 +1960,9 @@ Global GlobalSection(NestedProjects) = preSolution {811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888} - {47ECDF59-DEF8-4C53-87B1-2098A3429059} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {76716382-3159-460E-BDA6-C5715CF606D7} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2905FF23-53FB-45E6-AA49-6AF47A172056} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {DB070A10-BF39-4752-8456-86E9D5928478} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {08478EF5-44E8-42E9-92D6-15E00EC038D8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {AC18926A-E784-40FE-B09D-BB0FE2B599F0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C} @@ -2313,7 +1996,6 @@ Global {E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} - {AF227847-E65C-4BE9-BCE9-B551357788E0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} diff --git a/Directory.Build.props b/Directory.Build.props index 835decc672..97781b7517 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,5 +5,6 @@ false false + False diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index 6bf69603c0..314d38190a 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -1,14 +1,8 @@ - - - - - - diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 72d90abbf3..9fcb9d6b7f 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -214,17 +214,11 @@ partial class Build : NukeBuild .DependsOn(Compile) .Executes(() => { - RunCoreTest("Avalonia.Animation.UnitTests"); RunCoreTest("Avalonia.Base.UnitTests"); RunCoreTest("Avalonia.Controls.UnitTests"); RunCoreTest("Avalonia.Controls.DataGrid.UnitTests"); - RunCoreTest("Avalonia.Input.UnitTests"); - RunCoreTest("Avalonia.Interactivity.UnitTests"); - RunCoreTest("Avalonia.Layout.UnitTests"); RunCoreTest("Avalonia.Markup.UnitTests"); RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); - RunCoreTest("Avalonia.Styling.UnitTests"); - RunCoreTest("Avalonia.Visuals.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); }); diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 9777bb46c3..8d2ead054c 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -41,7 +41,47 @@ + + {7b92af71-6287-4693-9dcb-bd5b6e927e23} + Avalonia.Android + + + {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + Avalonia.Base + + + {d2221c82-4a25-4583-9b43-d791e3f6820c} + Avalonia.Controls + + + {7062ae20-5dcc-4442-9645-8195bdece63e} + Avalonia.Diagnostics + + + {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} + Avalonia.Themes.Default + + + {3e53a01a-b331-47f3-b828-4a5717e77a24} + Avalonia.Markup.Xaml + + + {6417e941-21bc-467b-a771-0de389353ce6} + Avalonia.Markup + + + {7d2d3083-71dd-4cc9-8907-39a0d86fb322} + Avalonia.Skia + + + {d0a739b9-3c68-4ba6-a328-41606954b6bd} + ControlCatalog + + + + + diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 12d1d5645e..d4f10b3580 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -13,4 +13,53 @@ + + + + + + + + + {4488ad85-1495-4809-9aa4-ddfe0a48527e} + Avalonia.iOS + + + {3E53A01A-B331-47F3-B828-4A5717E77A24} + Avalonia.Markup.Xaml + + + {6417E941-21BC-467B-A771-0DE389353CE6} + Avalonia.Markup + + + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} + Avalonia.Base + + + {D2221C82-4A25-4583-9B43-D791E3F6820C} + Avalonia.Controls + + + {7062AE20-5DCC-4442-9645-8195BDECE63E} + Avalonia.Diagnostics + + + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} + Avalonia.Themes.Default + + + {7d2d3083-71dd-4cc9-8907-39a0d86fb322} + Avalonia.Skia + + + {d0a739b9-3c68-4ba6-a328-41606954b6bd} + ControlCatalog + + + + + + + diff --git a/samples/RenderDemo/Pages/PathMeasurementPage.cs b/samples/RenderDemo/Pages/PathMeasurementPage.cs index 212377deae..cc5125609c 100644 --- a/samples/RenderDemo/Pages/PathMeasurementPage.cs +++ b/samples/RenderDemo/Pages/PathMeasurementPage.cs @@ -1,15 +1,9 @@ -using System; -using System.Diagnostics; -using System.Drawing.Drawing2D; -using System.Security.Cryptography; using Avalonia; using Avalonia.Controls; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Media.Immutable; -using Avalonia.Threading; -using Avalonia.Visuals.Media.Imaging; namespace RenderDemo.Pages { diff --git a/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs b/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs index f263786ab7..f365b59c20 100644 --- a/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs +++ b/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs @@ -5,7 +5,6 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; -using Avalonia.Visuals.Media.Imaging; namespace RenderDemo.Pages { diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index 3d5aee49e9..18a4ee5662 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -4,7 +4,7 @@ net6.0 - + TextFormatterPage.axaml Code diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index db0bb01410..ee41b9f354 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -40,4 +40,40 @@ + + + {7b92af71-6287-4693-9dcb-bd5b6e927e23} + Avalonia.Android + + + {3e53a01a-b331-47f3-b828-4a5717e77a24} + Avalonia.Markup.Xaml + + + {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + Avalonia.Base + + + {d2221c82-4a25-4583-9b43-d791e3f6820c} + Avalonia.Controls + + + {7062ae20-5dcc-4442-9645-8195bdece63e} + Avalonia.Diagnostics + + + {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} + Avalonia.Themes.Default + + + {7d2d3083-71dd-4cc9-8907-39a0d86fb322} + Avalonia.Skia + + + + + + + + diff --git a/src/Avalonia.Animation/ApiCompatBaseline.txt b/src/Avalonia.Animation/ApiCompatBaseline.txt deleted file mode 100644 index 973698f872..0000000000 --- a/src/Avalonia.Animation/ApiCompatBaseline.txt +++ /dev/null @@ -1,5 +0,0 @@ -Compat issues with assembly Avalonia.Animation: -InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock, System.Threading.CancellationToken)' is present in the implementation but not in the contract. -Total Issues: 3 diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj deleted file mode 100644 index 80726f2884..0000000000 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - diff --git a/src/Avalonia.Animation/Properties/AssemblyInfo.cs b/src/Avalonia.Animation/Properties/AssemblyInfo.cs deleted file mode 100644 index 6b539b075b..0000000000 --- a/src/Avalonia.Animation/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Avalonia.Metadata; -using System.Runtime.CompilerServices; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Animators")] - -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Animation.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Base/Animation/Animatable.cs similarity index 100% rename from src/Avalonia.Animation/Animatable.cs rename to src/Avalonia.Base/Animation/Animatable.cs diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Base/Animation/Animation.cs similarity index 100% rename from src/Avalonia.Animation/Animation.cs rename to src/Avalonia.Base/Animation/Animation.cs diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Base/Animation/AnimationInstance`1.cs similarity index 100% rename from src/Avalonia.Animation/AnimationInstance`1.cs rename to src/Avalonia.Base/Animation/AnimationInstance`1.cs diff --git a/src/Avalonia.Animation/AnimatorDrivenTransition.cs b/src/Avalonia.Base/Animation/AnimatorDrivenTransition.cs similarity index 100% rename from src/Avalonia.Animation/AnimatorDrivenTransition.cs rename to src/Avalonia.Base/Animation/AnimatorDrivenTransition.cs diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs similarity index 100% rename from src/Avalonia.Animation/AnimatorKeyFrame.cs rename to src/Avalonia.Base/Animation/AnimatorKeyFrame.cs diff --git a/src/Avalonia.Animation/AnimatorTransitionObservable.cs b/src/Avalonia.Base/Animation/AnimatorTransitionObservable.cs similarity index 100% rename from src/Avalonia.Animation/AnimatorTransitionObservable.cs rename to src/Avalonia.Base/Animation/AnimatorTransitionObservable.cs diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Base/Animation/Animators/Animator`1.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Animator`1.cs rename to src/Avalonia.Base/Animation/Animators/Animator`1.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/BaseBrushAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BaseBrushAnimator.cs diff --git a/src/Avalonia.Animation/Animators/BoolAnimator.cs b/src/Avalonia.Base/Animation/Animators/BoolAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/BoolAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BoolAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BoxShadowAnimator.cs b/src/Avalonia.Base/Animation/Animators/BoxShadowAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/BoxShadowAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BoxShadowAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BoxShadowsAnimator.cs b/src/Avalonia.Base/Animation/Animators/BoxShadowsAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/BoxShadowsAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BoxShadowsAnimator.cs diff --git a/src/Avalonia.Animation/Animators/ByteAnimator.cs b/src/Avalonia.Base/Animation/Animators/ByteAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/ByteAnimator.cs rename to src/Avalonia.Base/Animation/Animators/ByteAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs b/src/Avalonia.Base/Animation/Animators/ColorAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs rename to src/Avalonia.Base/Animation/Animators/ColorAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/CornerRadiusAnimator.cs b/src/Avalonia.Base/Animation/Animators/CornerRadiusAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/CornerRadiusAnimator.cs rename to src/Avalonia.Base/Animation/Animators/CornerRadiusAnimator.cs diff --git a/src/Avalonia.Animation/Animators/DecimalAnimator.cs b/src/Avalonia.Base/Animation/Animators/DecimalAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/DecimalAnimator.cs rename to src/Avalonia.Base/Animation/Animators/DecimalAnimator.cs diff --git a/src/Avalonia.Animation/Animators/DoubleAnimator.cs b/src/Avalonia.Base/Animation/Animators/DoubleAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/DoubleAnimator.cs rename to src/Avalonia.Base/Animation/Animators/DoubleAnimator.cs diff --git a/src/Avalonia.Animation/Animators/FloatAnimator.cs b/src/Avalonia.Base/Animation/Animators/FloatAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/FloatAnimator.cs rename to src/Avalonia.Base/Animation/Animators/FloatAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs rename to src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs diff --git a/src/Avalonia.Animation/Animators/Int16Animator.cs b/src/Avalonia.Base/Animation/Animators/Int16Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Int16Animator.cs rename to src/Avalonia.Base/Animation/Animators/Int16Animator.cs diff --git a/src/Avalonia.Animation/Animators/Int32Animator.cs b/src/Avalonia.Base/Animation/Animators/Int32Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Int32Animator.cs rename to src/Avalonia.Base/Animation/Animators/Int32Animator.cs diff --git a/src/Avalonia.Animation/Animators/Int64Animator.cs b/src/Avalonia.Base/Animation/Animators/Int64Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Int64Animator.cs rename to src/Avalonia.Base/Animation/Animators/Int64Animator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/PointAnimator.cs b/src/Avalonia.Base/Animation/Animators/PointAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/PointAnimator.cs rename to src/Avalonia.Base/Animation/Animators/PointAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/RectAnimator.cs b/src/Avalonia.Base/Animation/Animators/RectAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/RectAnimator.cs rename to src/Avalonia.Base/Animation/Animators/RectAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs b/src/Avalonia.Base/Animation/Animators/RelativePointAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs rename to src/Avalonia.Base/Animation/Animators/RelativePointAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/SizeAnimator.cs b/src/Avalonia.Base/Animation/Animators/SizeAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/SizeAnimator.cs rename to src/Avalonia.Base/Animation/Animators/SizeAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs rename to src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/ThicknessAnimator.cs b/src/Avalonia.Base/Animation/Animators/ThicknessAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/ThicknessAnimator.cs rename to src/Avalonia.Base/Animation/Animators/ThicknessAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Base/Animation/Animators/TransformAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs rename to src/Avalonia.Base/Animation/Animators/TransformAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformOperationsAnimator.cs b/src/Avalonia.Base/Animation/Animators/TransformOperationsAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/TransformOperationsAnimator.cs rename to src/Avalonia.Base/Animation/Animators/TransformOperationsAnimator.cs diff --git a/src/Avalonia.Animation/Animators/UInt16Animator.cs b/src/Avalonia.Base/Animation/Animators/UInt16Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/UInt16Animator.cs rename to src/Avalonia.Base/Animation/Animators/UInt16Animator.cs diff --git a/src/Avalonia.Animation/Animators/UInt32Animator.cs b/src/Avalonia.Base/Animation/Animators/UInt32Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/UInt32Animator.cs rename to src/Avalonia.Base/Animation/Animators/UInt32Animator.cs diff --git a/src/Avalonia.Animation/Animators/UInt64Animator.cs b/src/Avalonia.Base/Animation/Animators/UInt64Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/UInt64Animator.cs rename to src/Avalonia.Base/Animation/Animators/UInt64Animator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/VectorAnimator.cs b/src/Avalonia.Base/Animation/Animators/VectorAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/VectorAnimator.cs rename to src/Avalonia.Base/Animation/Animators/VectorAnimator.cs diff --git a/src/Avalonia.Animation/Clock.cs b/src/Avalonia.Base/Animation/Clock.cs similarity index 100% rename from src/Avalonia.Animation/Clock.cs rename to src/Avalonia.Base/Animation/Clock.cs diff --git a/src/Avalonia.Animation/ClockBase.cs b/src/Avalonia.Base/Animation/ClockBase.cs similarity index 100% rename from src/Avalonia.Animation/ClockBase.cs rename to src/Avalonia.Base/Animation/ClockBase.cs diff --git a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs b/src/Avalonia.Base/Animation/CompositePageTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/CompositePageTransition.cs rename to src/Avalonia.Base/Animation/CompositePageTransition.cs diff --git a/src/Avalonia.Visuals/Animation/CrossFade.cs b/src/Avalonia.Base/Animation/CrossFade.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/CrossFade.cs rename to src/Avalonia.Base/Animation/CrossFade.cs diff --git a/src/Avalonia.Animation/Cue.cs b/src/Avalonia.Base/Animation/Cue.cs similarity index 100% rename from src/Avalonia.Animation/Cue.cs rename to src/Avalonia.Base/Animation/Cue.cs diff --git a/src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs b/src/Avalonia.Base/Animation/DisposeAnimationInstanceSubject.cs similarity index 100% rename from src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs rename to src/Avalonia.Base/Animation/DisposeAnimationInstanceSubject.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseIn.cs b/src/Avalonia.Base/Animation/Easings/BackEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BackEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/BackEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/BackEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BackEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/BackEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseOut.cs b/src/Avalonia.Base/Animation/Easings/BackEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BackEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/BackEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/BounceEaseIn.cs b/src/Avalonia.Base/Animation/Easings/BounceEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BounceEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/BounceEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/BounceEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/BounceEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BounceEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/BounceEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/BounceEaseOut.cs b/src/Avalonia.Base/Animation/Easings/BounceEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BounceEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/BounceEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/CircularEaseIn.cs b/src/Avalonia.Base/Animation/Easings/CircularEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CircularEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/CircularEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/CircularEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/CircularEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CircularEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/CircularEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/CircularEaseOut.cs b/src/Avalonia.Base/Animation/Easings/CircularEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CircularEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/CircularEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/CubicEaseIn.cs b/src/Avalonia.Base/Animation/Easings/CubicEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CubicEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/CubicEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/CubicEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/CubicEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CubicEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/CubicEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/CubicEaseOut.cs b/src/Avalonia.Base/Animation/Easings/CubicEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CubicEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/CubicEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/Easing.cs rename to src/Avalonia.Base/Animation/Easings/Easing.cs diff --git a/src/Avalonia.Animation/Easing/EasingTypeConverter.cs b/src/Avalonia.Base/Animation/Easings/EasingTypeConverter.cs similarity index 100% rename from src/Avalonia.Animation/Easing/EasingTypeConverter.cs rename to src/Avalonia.Base/Animation/Easings/EasingTypeConverter.cs diff --git a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/ElasticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ElasticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/ElasticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/ElasticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ElasticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/ElasticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/ElasticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ElasticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/ElasticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs b/src/Avalonia.Base/Animation/Easings/ExponentialEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ExponentialEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/ExponentialEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/ExponentialEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/ExponentialEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs b/src/Avalonia.Base/Animation/Easings/ExponentialEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ExponentialEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/ExponentialEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/IEasing.cs b/src/Avalonia.Base/Animation/Easings/IEasing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/IEasing.cs rename to src/Avalonia.Base/Animation/Easings/IEasing.cs diff --git a/src/Avalonia.Animation/Easing/LinearEasing.cs b/src/Avalonia.Base/Animation/Easings/LinearEasing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/LinearEasing.cs rename to src/Avalonia.Base/Animation/Easings/LinearEasing.cs diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/QuadraticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuadraticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/QuadraticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/QuadraticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/QuadraticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/QuadraticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuadraticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/QuadraticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/QuarticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/QuarticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuarticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/QuarticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/QuarticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuarticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/QuarticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/QuarticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/QuarticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuarticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/QuarticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/QuinticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/QuinticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuinticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/QuinticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/QuinticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuinticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/QuinticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/QuinticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/QuinticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuinticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/QuinticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/SineEaseIn.cs b/src/Avalonia.Base/Animation/Easings/SineEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SineEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/SineEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/SineEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/SineEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SineEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/SineEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/SineEaseOut.cs b/src/Avalonia.Base/Animation/Easings/SineEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SineEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/SineEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/SplineEasing.cs b/src/Avalonia.Base/Animation/Easings/SplineEasing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SplineEasing.cs rename to src/Avalonia.Base/Animation/Easings/SplineEasing.cs diff --git a/src/Avalonia.Animation/FillMode.cs b/src/Avalonia.Base/Animation/FillMode.cs similarity index 100% rename from src/Avalonia.Animation/FillMode.cs rename to src/Avalonia.Base/Animation/FillMode.cs diff --git a/src/Avalonia.Animation/IAnimation.cs b/src/Avalonia.Base/Animation/IAnimation.cs similarity index 100% rename from src/Avalonia.Animation/IAnimation.cs rename to src/Avalonia.Base/Animation/IAnimation.cs diff --git a/src/Avalonia.Animation/IAnimationSetter.cs b/src/Avalonia.Base/Animation/IAnimationSetter.cs similarity index 100% rename from src/Avalonia.Animation/IAnimationSetter.cs rename to src/Avalonia.Base/Animation/IAnimationSetter.cs diff --git a/src/Avalonia.Animation/IAnimator.cs b/src/Avalonia.Base/Animation/IAnimator.cs similarity index 100% rename from src/Avalonia.Animation/IAnimator.cs rename to src/Avalonia.Base/Animation/IAnimator.cs diff --git a/src/Avalonia.Animation/IClock.cs b/src/Avalonia.Base/Animation/IClock.cs similarity index 100% rename from src/Avalonia.Animation/IClock.cs rename to src/Avalonia.Base/Animation/IClock.cs diff --git a/src/Avalonia.Animation/IGlobalClock.cs b/src/Avalonia.Base/Animation/IGlobalClock.cs similarity index 100% rename from src/Avalonia.Animation/IGlobalClock.cs rename to src/Avalonia.Base/Animation/IGlobalClock.cs diff --git a/src/Avalonia.Visuals/Animation/IPageTransition.cs b/src/Avalonia.Base/Animation/IPageTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/IPageTransition.cs rename to src/Avalonia.Base/Animation/IPageTransition.cs diff --git a/src/Avalonia.Animation/ITransition.cs b/src/Avalonia.Base/Animation/ITransition.cs similarity index 100% rename from src/Avalonia.Animation/ITransition.cs rename to src/Avalonia.Base/Animation/ITransition.cs diff --git a/src/Avalonia.Animation/IterationCount.cs b/src/Avalonia.Base/Animation/IterationCount.cs similarity index 100% rename from src/Avalonia.Animation/IterationCount.cs rename to src/Avalonia.Base/Animation/IterationCount.cs diff --git a/src/Avalonia.Animation/IterationCountTypeConverter.cs b/src/Avalonia.Base/Animation/IterationCountTypeConverter.cs similarity index 100% rename from src/Avalonia.Animation/IterationCountTypeConverter.cs rename to src/Avalonia.Base/Animation/IterationCountTypeConverter.cs diff --git a/src/Avalonia.Animation/KeyFrame.cs b/src/Avalonia.Base/Animation/KeyFrame.cs similarity index 100% rename from src/Avalonia.Animation/KeyFrame.cs rename to src/Avalonia.Base/Animation/KeyFrame.cs diff --git a/src/Avalonia.Animation/KeyFrames.cs b/src/Avalonia.Base/Animation/KeyFrames.cs similarity index 100% rename from src/Avalonia.Animation/KeyFrames.cs rename to src/Avalonia.Base/Animation/KeyFrames.cs diff --git a/src/Avalonia.Animation/KeySpline.cs b/src/Avalonia.Base/Animation/KeySpline.cs similarity index 100% rename from src/Avalonia.Animation/KeySpline.cs rename to src/Avalonia.Base/Animation/KeySpline.cs diff --git a/src/Avalonia.Animation/KeySplineTypeConverter.cs b/src/Avalonia.Base/Animation/KeySplineTypeConverter.cs similarity index 100% rename from src/Avalonia.Animation/KeySplineTypeConverter.cs rename to src/Avalonia.Base/Animation/KeySplineTypeConverter.cs diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Base/Animation/PageSlide.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/PageSlide.cs rename to src/Avalonia.Base/Animation/PageSlide.cs diff --git a/src/Avalonia.Animation/PlayState.cs b/src/Avalonia.Base/Animation/PlayState.cs similarity index 100% rename from src/Avalonia.Animation/PlayState.cs rename to src/Avalonia.Base/Animation/PlayState.cs diff --git a/src/Avalonia.Animation/PlaybackDirection.cs b/src/Avalonia.Base/Animation/PlaybackDirection.cs similarity index 100% rename from src/Avalonia.Animation/PlaybackDirection.cs rename to src/Avalonia.Base/Animation/PlaybackDirection.cs diff --git a/src/Avalonia.Visuals/Animation/RenderLoopClock.cs b/src/Avalonia.Base/Animation/RenderLoopClock.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/RenderLoopClock.cs rename to src/Avalonia.Base/Animation/RenderLoopClock.cs diff --git a/src/Avalonia.Animation/Transition.cs b/src/Avalonia.Base/Animation/Transition.cs similarity index 100% rename from src/Avalonia.Animation/Transition.cs rename to src/Avalonia.Base/Animation/Transition.cs diff --git a/src/Avalonia.Animation/TransitionInstance.cs b/src/Avalonia.Base/Animation/TransitionInstance.cs similarity index 100% rename from src/Avalonia.Animation/TransitionInstance.cs rename to src/Avalonia.Base/Animation/TransitionInstance.cs diff --git a/src/Avalonia.Animation/TransitionObservableBase.cs b/src/Avalonia.Base/Animation/TransitionObservableBase.cs similarity index 100% rename from src/Avalonia.Animation/TransitionObservableBase.cs rename to src/Avalonia.Base/Animation/TransitionObservableBase.cs diff --git a/src/Avalonia.Animation/Transitions.cs b/src/Avalonia.Base/Animation/Transitions.cs similarity index 100% rename from src/Avalonia.Animation/Transitions.cs rename to src/Avalonia.Base/Animation/Transitions.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs b/src/Avalonia.Base/Animation/Transitions/BoxShadowsTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs rename to src/Avalonia.Base/Animation/Transitions/BoxShadowsTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs b/src/Avalonia.Base/Animation/Transitions/BrushTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs rename to src/Avalonia.Base/Animation/Transitions/BrushTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs b/src/Avalonia.Base/Animation/Transitions/ColorTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs rename to src/Avalonia.Base/Animation/Transitions/ColorTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs b/src/Avalonia.Base/Animation/Transitions/CornerRadiusTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs rename to src/Avalonia.Base/Animation/Transitions/CornerRadiusTransition.cs diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Base/Animation/Transitions/DoubleTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/DoubleTransition.cs rename to src/Avalonia.Base/Animation/Transitions/DoubleTransition.cs diff --git a/src/Avalonia.Animation/Transitions/FloatTransition.cs b/src/Avalonia.Base/Animation/Transitions/FloatTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/FloatTransition.cs rename to src/Avalonia.Base/Animation/Transitions/FloatTransition.cs diff --git a/src/Avalonia.Animation/Transitions/IntegerTransition.cs b/src/Avalonia.Base/Animation/Transitions/IntegerTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/IntegerTransition.cs rename to src/Avalonia.Base/Animation/Transitions/IntegerTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs b/src/Avalonia.Base/Animation/Transitions/PointTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs rename to src/Avalonia.Base/Animation/Transitions/PointTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs b/src/Avalonia.Base/Animation/Transitions/RelativePointTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs rename to src/Avalonia.Base/Animation/Transitions/RelativePointTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs b/src/Avalonia.Base/Animation/Transitions/SizeTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs rename to src/Avalonia.Base/Animation/Transitions/SizeTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs b/src/Avalonia.Base/Animation/Transitions/ThicknessTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs rename to src/Avalonia.Base/Animation/Transitions/ThicknessTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/TransformOperationsTransition.cs b/src/Avalonia.Base/Animation/Transitions/TransformOperationsTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/TransformOperationsTransition.cs rename to src/Avalonia.Base/Animation/Transitions/TransformOperationsTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs b/src/Avalonia.Base/Animation/Transitions/VectorTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs rename to src/Avalonia.Base/Animation/Transitions/VectorTransition.cs diff --git a/src/Avalonia.Animation/Utils/BounceEaseUtils.cs b/src/Avalonia.Base/Animation/Utils/BounceEaseUtils.cs similarity index 100% rename from src/Avalonia.Animation/Utils/BounceEaseUtils.cs rename to src/Avalonia.Base/Animation/Utils/BounceEaseUtils.cs diff --git a/src/Avalonia.Animation/Utils/EasingUtils.cs b/src/Avalonia.Base/Animation/Utils/EasingUtils.cs similarity index 100% rename from src/Avalonia.Animation/Utils/EasingUtils.cs rename to src/Avalonia.Base/Animation/Utils/EasingUtils.cs diff --git a/src/Avalonia.Visuals/Assets/BiDi.trie b/src/Avalonia.Base/Assets/BiDi.trie similarity index 100% rename from src/Avalonia.Visuals/Assets/BiDi.trie rename to src/Avalonia.Base/Assets/BiDi.trie diff --git a/src/Avalonia.Visuals/Assets/GraphemeBreak.trie b/src/Avalonia.Base/Assets/GraphemeBreak.trie similarity index 100% rename from src/Avalonia.Visuals/Assets/GraphemeBreak.trie rename to src/Avalonia.Base/Assets/GraphemeBreak.trie diff --git a/src/Avalonia.Visuals/Assets/UnicodeData.trie b/src/Avalonia.Base/Assets/UnicodeData.trie similarity index 100% rename from src/Avalonia.Visuals/Assets/UnicodeData.trie rename to src/Avalonia.Base/Assets/UnicodeData.trie diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 637b86d759..5b79d3cd7b 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -5,6 +5,9 @@ Avalonia True + + + diff --git a/src/Avalonia.Visuals/AvaloniaPropertyExtensions.cs b/src/Avalonia.Base/AvaloniaPropertyExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/AvaloniaPropertyExtensions.cs rename to src/Avalonia.Base/AvaloniaPropertyExtensions.cs diff --git a/src/Avalonia.Styling/ClassBindingManager.cs b/src/Avalonia.Base/ClassBindingManager.cs similarity index 100% rename from src/Avalonia.Styling/ClassBindingManager.cs rename to src/Avalonia.Base/ClassBindingManager.cs diff --git a/src/Avalonia.Visuals/Media/CombinedGeometry.cs b/src/Avalonia.Base/CombinedGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/CombinedGeometry.cs rename to src/Avalonia.Base/CombinedGeometry.cs diff --git a/src/Avalonia.Styling/Controls/ChildNameScope.cs b/src/Avalonia.Base/Controls/ChildNameScope.cs similarity index 99% rename from src/Avalonia.Styling/Controls/ChildNameScope.cs rename to src/Avalonia.Base/Controls/ChildNameScope.cs index 58114a57fd..1ceaff924c 100644 --- a/src/Avalonia.Styling/Controls/ChildNameScope.cs +++ b/src/Avalonia.Base/Controls/ChildNameScope.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls return new SynchronousCompletionAsyncResult(found); // Not found and both current and parent scope are in completed state if(IsCompleted) - return new SynchronousCompletionAsyncResult(null); + return new SynchronousCompletionAsyncResult((object?)null); return DoFindAsync(name); } diff --git a/src/Avalonia.Styling/Controls/Classes.cs b/src/Avalonia.Base/Controls/Classes.cs similarity index 100% rename from src/Avalonia.Styling/Controls/Classes.cs rename to src/Avalonia.Base/Controls/Classes.cs diff --git a/src/Avalonia.Styling/Controls/INameScope.cs b/src/Avalonia.Base/Controls/INameScope.cs similarity index 100% rename from src/Avalonia.Styling/Controls/INameScope.cs rename to src/Avalonia.Base/Controls/INameScope.cs diff --git a/src/Avalonia.Styling/Controls/IPseudoClasses.cs b/src/Avalonia.Base/Controls/IPseudoClasses.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IPseudoClasses.cs rename to src/Avalonia.Base/Controls/IPseudoClasses.cs diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Base/Controls/IResourceDictionary.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceDictionary.cs rename to src/Avalonia.Base/Controls/IResourceDictionary.cs diff --git a/src/Avalonia.Styling/Controls/IResourceHost.cs b/src/Avalonia.Base/Controls/IResourceHost.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceHost.cs rename to src/Avalonia.Base/Controls/IResourceHost.cs diff --git a/src/Avalonia.Styling/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceNode.cs rename to src/Avalonia.Base/Controls/IResourceNode.cs diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Base/Controls/IResourceProvider.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceProvider.cs rename to src/Avalonia.Base/Controls/IResourceProvider.cs diff --git a/src/Avalonia.Styling/Controls/ISetInheritanceParent.cs b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ISetInheritanceParent.cs rename to src/Avalonia.Base/Controls/ISetInheritanceParent.cs diff --git a/src/Avalonia.Styling/Controls/ISetLogicalParent.cs b/src/Avalonia.Base/Controls/ISetLogicalParent.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ISetLogicalParent.cs rename to src/Avalonia.Base/Controls/ISetLogicalParent.cs diff --git a/src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs b/src/Avalonia.Base/Controls/Metadata/PseudoClassesAttribute.cs similarity index 100% rename from src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs rename to src/Avalonia.Base/Controls/Metadata/PseudoClassesAttribute.cs diff --git a/src/Avalonia.Styling/Controls/Metadata/TemplatePartAttribute.cs b/src/Avalonia.Base/Controls/Metadata/TemplatePartAttribute.cs similarity index 100% rename from src/Avalonia.Styling/Controls/Metadata/TemplatePartAttribute.cs rename to src/Avalonia.Base/Controls/Metadata/TemplatePartAttribute.cs diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Base/Controls/NameScope.cs similarity index 99% rename from src/Avalonia.Styling/Controls/NameScope.cs rename to src/Avalonia.Base/Controls/NameScope.cs index 77f98f85c4..952401c00c 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Base/Controls/NameScope.cs @@ -82,7 +82,7 @@ namespace Avalonia.Controls if (found != null) return new SynchronousCompletionAsyncResult(found); if (IsCompleted) - return new SynchronousCompletionAsyncResult(null); + return new SynchronousCompletionAsyncResult((object?)null); if (!_pendingSearches.TryGetValue(name, out var tcs)) // We are intentionally running continuations synchronously here _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource(); diff --git a/src/Avalonia.Styling/Controls/NameScopeEventArgs.cs b/src/Avalonia.Base/Controls/NameScopeEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/Controls/NameScopeEventArgs.cs rename to src/Avalonia.Base/Controls/NameScopeEventArgs.cs diff --git a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs b/src/Avalonia.Base/Controls/NameScopeExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Controls/NameScopeExtensions.cs rename to src/Avalonia.Base/Controls/NameScopeExtensions.cs diff --git a/src/Avalonia.Styling/Controls/NameScopeLocator.cs b/src/Avalonia.Base/Controls/NameScopeLocator.cs similarity index 100% rename from src/Avalonia.Styling/Controls/NameScopeLocator.cs rename to src/Avalonia.Base/Controls/NameScopeLocator.cs diff --git a/src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs b/src/Avalonia.Base/Controls/PseudoClassesExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs rename to src/Avalonia.Base/Controls/PseudoClassesExtensions.cs diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Base/Controls/ResourceDictionary.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ResourceDictionary.cs rename to src/Avalonia.Base/Controls/ResourceDictionary.cs diff --git a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs rename to src/Avalonia.Base/Controls/ResourceNodeExtensions.cs diff --git a/src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs b/src/Avalonia.Base/Controls/ResourcesChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs rename to src/Avalonia.Base/Controls/ResourcesChangedEventArgs.cs diff --git a/src/Avalonia.Visuals/CornerRadius.cs b/src/Avalonia.Base/CornerRadius.cs similarity index 100% rename from src/Avalonia.Visuals/CornerRadius.cs rename to src/Avalonia.Base/CornerRadius.cs diff --git a/src/Avalonia.Styling/Diagnostics/StyleDiagnostics.cs b/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs similarity index 100% rename from src/Avalonia.Styling/Diagnostics/StyleDiagnostics.cs rename to src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs diff --git a/src/Avalonia.Styling/Diagnostics/StyledElementExtensions.cs b/src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Diagnostics/StyledElementExtensions.cs rename to src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs diff --git a/src/Avalonia.Visuals/Media/GeometryCollection.cs b/src/Avalonia.Base/GeometryCollection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GeometryCollection.cs rename to src/Avalonia.Base/GeometryCollection.cs diff --git a/src/Avalonia.Visuals/Media/GeometryGroup.cs b/src/Avalonia.Base/GeometryGroup.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GeometryGroup.cs rename to src/Avalonia.Base/GeometryGroup.cs diff --git a/src/Avalonia.Styling/IDataContextProvider.cs b/src/Avalonia.Base/IDataContextProvider.cs similarity index 100% rename from src/Avalonia.Styling/IDataContextProvider.cs rename to src/Avalonia.Base/IDataContextProvider.cs diff --git a/src/Avalonia.Styling/INamed.cs b/src/Avalonia.Base/INamed.cs similarity index 100% rename from src/Avalonia.Styling/INamed.cs rename to src/Avalonia.Base/INamed.cs diff --git a/src/Avalonia.Styling/IStyledElement.cs b/src/Avalonia.Base/IStyledElement.cs similarity index 100% rename from src/Avalonia.Styling/IStyledElement.cs rename to src/Avalonia.Base/IStyledElement.cs diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Base/Input/AccessKeyHandler.cs similarity index 100% rename from src/Avalonia.Input/AccessKeyHandler.cs rename to src/Avalonia.Base/Input/AccessKeyHandler.cs diff --git a/src/Avalonia.Input/Cursor.cs b/src/Avalonia.Base/Input/Cursor.cs similarity index 100% rename from src/Avalonia.Input/Cursor.cs rename to src/Avalonia.Base/Input/Cursor.cs diff --git a/src/Avalonia.Input/DataFormats.cs b/src/Avalonia.Base/Input/DataFormats.cs similarity index 100% rename from src/Avalonia.Input/DataFormats.cs rename to src/Avalonia.Base/Input/DataFormats.cs diff --git a/src/Avalonia.Input/DataObject.cs b/src/Avalonia.Base/Input/DataObject.cs similarity index 100% rename from src/Avalonia.Input/DataObject.cs rename to src/Avalonia.Base/Input/DataObject.cs diff --git a/src/Avalonia.Input/DragDrop.cs b/src/Avalonia.Base/Input/DragDrop.cs similarity index 100% rename from src/Avalonia.Input/DragDrop.cs rename to src/Avalonia.Base/Input/DragDrop.cs diff --git a/src/Avalonia.Input/DragDropDevice.cs b/src/Avalonia.Base/Input/DragDropDevice.cs similarity index 100% rename from src/Avalonia.Input/DragDropDevice.cs rename to src/Avalonia.Base/Input/DragDropDevice.cs diff --git a/src/Avalonia.Input/DragDropEffects.cs b/src/Avalonia.Base/Input/DragDropEffects.cs similarity index 100% rename from src/Avalonia.Input/DragDropEffects.cs rename to src/Avalonia.Base/Input/DragDropEffects.cs diff --git a/src/Avalonia.Input/DragEventArgs.cs b/src/Avalonia.Base/Input/DragEventArgs.cs similarity index 100% rename from src/Avalonia.Input/DragEventArgs.cs rename to src/Avalonia.Base/Input/DragEventArgs.cs diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Base/Input/FocusManager.cs similarity index 100% rename from src/Avalonia.Input/FocusManager.cs rename to src/Avalonia.Base/Input/FocusManager.cs diff --git a/src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs similarity index 100% rename from src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs rename to src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs diff --git a/src/Avalonia.Input/GestureRecognizers/IGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs similarity index 100% rename from src/Avalonia.Input/GestureRecognizers/IGestureRecognizer.cs rename to src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs diff --git a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs similarity index 100% rename from src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs rename to src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs similarity index 100% rename from src/Avalonia.Input/Gestures.cs rename to src/Avalonia.Base/Input/Gestures.cs diff --git a/src/Avalonia.Input/GotFocusEventArgs.cs b/src/Avalonia.Base/Input/GotFocusEventArgs.cs similarity index 100% rename from src/Avalonia.Input/GotFocusEventArgs.cs rename to src/Avalonia.Base/Input/GotFocusEventArgs.cs diff --git a/src/Avalonia.Input/IAccessKeyHandler.cs b/src/Avalonia.Base/Input/IAccessKeyHandler.cs similarity index 100% rename from src/Avalonia.Input/IAccessKeyHandler.cs rename to src/Avalonia.Base/Input/IAccessKeyHandler.cs diff --git a/src/Avalonia.Input/IClickableControl.cs b/src/Avalonia.Base/Input/IClickableControl.cs similarity index 100% rename from src/Avalonia.Input/IClickableControl.cs rename to src/Avalonia.Base/Input/IClickableControl.cs diff --git a/src/Avalonia.Input/ICloseable.cs b/src/Avalonia.Base/Input/ICloseable.cs similarity index 100% rename from src/Avalonia.Input/ICloseable.cs rename to src/Avalonia.Base/Input/ICloseable.cs diff --git a/src/Avalonia.Input/ICommandSource.cs b/src/Avalonia.Base/Input/ICommandSource.cs similarity index 100% rename from src/Avalonia.Input/ICommandSource.cs rename to src/Avalonia.Base/Input/ICommandSource.cs diff --git a/src/Avalonia.Input/ICustomKeyboardNavigation.cs b/src/Avalonia.Base/Input/ICustomKeyboardNavigation.cs similarity index 100% rename from src/Avalonia.Input/ICustomKeyboardNavigation.cs rename to src/Avalonia.Base/Input/ICustomKeyboardNavigation.cs diff --git a/src/Avalonia.Input/IDataObject.cs b/src/Avalonia.Base/Input/IDataObject.cs similarity index 100% rename from src/Avalonia.Input/IDataObject.cs rename to src/Avalonia.Base/Input/IDataObject.cs diff --git a/src/Avalonia.Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs similarity index 100% rename from src/Avalonia.Input/IFocusManager.cs rename to src/Avalonia.Base/Input/IFocusManager.cs diff --git a/src/Avalonia.Input/IFocusScope.cs b/src/Avalonia.Base/Input/IFocusScope.cs similarity index 100% rename from src/Avalonia.Input/IFocusScope.cs rename to src/Avalonia.Base/Input/IFocusScope.cs diff --git a/src/Avalonia.Input/IInputDevice.cs b/src/Avalonia.Base/Input/IInputDevice.cs similarity index 100% rename from src/Avalonia.Input/IInputDevice.cs rename to src/Avalonia.Base/Input/IInputDevice.cs diff --git a/src/Avalonia.Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs similarity index 100% rename from src/Avalonia.Input/IInputElement.cs rename to src/Avalonia.Base/Input/IInputElement.cs diff --git a/src/Avalonia.Input/IInputManager.cs b/src/Avalonia.Base/Input/IInputManager.cs similarity index 100% rename from src/Avalonia.Input/IInputManager.cs rename to src/Avalonia.Base/Input/IInputManager.cs diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs similarity index 100% rename from src/Avalonia.Input/IInputRoot.cs rename to src/Avalonia.Base/Input/IInputRoot.cs diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs similarity index 100% rename from src/Avalonia.Input/IKeyboardDevice.cs rename to src/Avalonia.Base/Input/IKeyboardDevice.cs diff --git a/src/Avalonia.Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs similarity index 100% rename from src/Avalonia.Input/IKeyboardNavigationHandler.cs rename to src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs diff --git a/src/Avalonia.Input/IMainMenu.cs b/src/Avalonia.Base/Input/IMainMenu.cs similarity index 100% rename from src/Avalonia.Input/IMainMenu.cs rename to src/Avalonia.Base/Input/IMainMenu.cs diff --git a/src/Avalonia.Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs similarity index 100% rename from src/Avalonia.Input/IMouseDevice.cs rename to src/Avalonia.Base/Input/IMouseDevice.cs diff --git a/src/Avalonia.Input/INavigableContainer.cs b/src/Avalonia.Base/Input/INavigableContainer.cs similarity index 100% rename from src/Avalonia.Input/INavigableContainer.cs rename to src/Avalonia.Base/Input/INavigableContainer.cs diff --git a/src/Avalonia.Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs similarity index 100% rename from src/Avalonia.Input/IPointer.cs rename to src/Avalonia.Base/Input/IPointer.cs diff --git a/src/Avalonia.Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs similarity index 100% rename from src/Avalonia.Input/IPointerDevice.cs rename to src/Avalonia.Base/Input/IPointerDevice.cs diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs similarity index 100% rename from src/Avalonia.Input/InputElement.cs rename to src/Avalonia.Base/Input/InputElement.cs diff --git a/src/Avalonia.Input/InputExtensions.cs b/src/Avalonia.Base/Input/InputExtensions.cs similarity index 100% rename from src/Avalonia.Input/InputExtensions.cs rename to src/Avalonia.Base/Input/InputExtensions.cs diff --git a/src/Avalonia.Input/InputManager.cs b/src/Avalonia.Base/Input/InputManager.cs similarity index 100% rename from src/Avalonia.Input/InputManager.cs rename to src/Avalonia.Base/Input/InputManager.cs diff --git a/src/Avalonia.Input/InputMethod.cs b/src/Avalonia.Base/Input/InputMethod.cs similarity index 100% rename from src/Avalonia.Input/InputMethod.cs rename to src/Avalonia.Base/Input/InputMethod.cs diff --git a/src/Avalonia.Input/Key.cs b/src/Avalonia.Base/Input/Key.cs similarity index 100% rename from src/Avalonia.Input/Key.cs rename to src/Avalonia.Base/Input/Key.cs diff --git a/src/Avalonia.Input/KeyBinding.cs b/src/Avalonia.Base/Input/KeyBinding.cs similarity index 100% rename from src/Avalonia.Input/KeyBinding.cs rename to src/Avalonia.Base/Input/KeyBinding.cs diff --git a/src/Avalonia.Input/KeyEventArgs.cs b/src/Avalonia.Base/Input/KeyEventArgs.cs similarity index 100% rename from src/Avalonia.Input/KeyEventArgs.cs rename to src/Avalonia.Base/Input/KeyEventArgs.cs diff --git a/src/Avalonia.Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs similarity index 100% rename from src/Avalonia.Input/KeyGesture.cs rename to src/Avalonia.Base/Input/KeyGesture.cs diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Base/Input/KeyboardDevice.cs similarity index 100% rename from src/Avalonia.Input/KeyboardDevice.cs rename to src/Avalonia.Base/Input/KeyboardDevice.cs diff --git a/src/Avalonia.Input/KeyboardNavigation.cs b/src/Avalonia.Base/Input/KeyboardNavigation.cs similarity index 100% rename from src/Avalonia.Input/KeyboardNavigation.cs rename to src/Avalonia.Base/Input/KeyboardNavigation.cs diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs similarity index 100% rename from src/Avalonia.Input/KeyboardNavigationHandler.cs rename to src/Avalonia.Base/Input/KeyboardNavigationHandler.cs diff --git a/src/Avalonia.Input/KeyboardNavigationMode.cs b/src/Avalonia.Base/Input/KeyboardNavigationMode.cs similarity index 100% rename from src/Avalonia.Input/KeyboardNavigationMode.cs rename to src/Avalonia.Base/Input/KeyboardNavigationMode.cs diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs similarity index 100% rename from src/Avalonia.Input/MouseDevice.cs rename to src/Avalonia.Base/Input/MouseDevice.cs diff --git a/src/Avalonia.Input/Navigation/FocusExtensions.cs b/src/Avalonia.Base/Input/Navigation/FocusExtensions.cs similarity index 100% rename from src/Avalonia.Input/Navigation/FocusExtensions.cs rename to src/Avalonia.Base/Input/Navigation/FocusExtensions.cs diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs similarity index 100% rename from src/Avalonia.Input/Navigation/TabNavigation.cs rename to src/Avalonia.Base/Input/Navigation/TabNavigation.cs diff --git a/src/Avalonia.Input/NavigationDirection.cs b/src/Avalonia.Base/Input/NavigationDirection.cs similarity index 100% rename from src/Avalonia.Input/NavigationDirection.cs rename to src/Avalonia.Base/Input/NavigationDirection.cs diff --git a/src/Avalonia.Input/NavigationMethod.cs b/src/Avalonia.Base/Input/NavigationMethod.cs similarity index 100% rename from src/Avalonia.Input/NavigationMethod.cs rename to src/Avalonia.Base/Input/NavigationMethod.cs diff --git a/src/Avalonia.Input/Platform/IClipboard.cs b/src/Avalonia.Base/Input/Platform/IClipboard.cs similarity index 100% rename from src/Avalonia.Input/Platform/IClipboard.cs rename to src/Avalonia.Base/Input/Platform/IClipboard.cs diff --git a/src/Avalonia.Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs similarity index 100% rename from src/Avalonia.Input/Platform/IPlatformDragSource.cs rename to src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs diff --git a/src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs similarity index 100% rename from src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs rename to src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs similarity index 100% rename from src/Avalonia.Input/Pointer.cs rename to src/Avalonia.Base/Input/Pointer.cs diff --git a/src/Avalonia.Input/PointerDeltaEventArgs.cs b/src/Avalonia.Base/Input/PointerDeltaEventArgs.cs similarity index 100% rename from src/Avalonia.Input/PointerDeltaEventArgs.cs rename to src/Avalonia.Base/Input/PointerDeltaEventArgs.cs diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs similarity index 100% rename from src/Avalonia.Input/PointerEventArgs.cs rename to src/Avalonia.Base/Input/PointerEventArgs.cs diff --git a/src/Avalonia.Input/PointerPoint.cs b/src/Avalonia.Base/Input/PointerPoint.cs similarity index 100% rename from src/Avalonia.Input/PointerPoint.cs rename to src/Avalonia.Base/Input/PointerPoint.cs diff --git a/src/Avalonia.Input/PointerWheelEventArgs.cs b/src/Avalonia.Base/Input/PointerWheelEventArgs.cs similarity index 100% rename from src/Avalonia.Input/PointerWheelEventArgs.cs rename to src/Avalonia.Base/Input/PointerWheelEventArgs.cs diff --git a/src/Avalonia.Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs similarity index 100% rename from src/Avalonia.Input/Raw/IDragDropDevice.cs rename to src/Avalonia.Base/Input/Raw/IDragDropDevice.cs diff --git a/src/Avalonia.Input/Raw/RawDragEvent.cs b/src/Avalonia.Base/Input/Raw/RawDragEvent.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawDragEvent.cs rename to src/Avalonia.Base/Input/Raw/RawDragEvent.cs diff --git a/src/Avalonia.Input/Raw/RawDragEventType.cs b/src/Avalonia.Base/Input/Raw/RawDragEventType.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawDragEventType.cs rename to src/Avalonia.Base/Input/Raw/RawDragEventType.cs diff --git a/src/Avalonia.Input/Raw/RawInputEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawInputEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawInputEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawInputEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawKeyEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawKeyEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawKeyEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawKeyEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawMouseWheelEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawMouseWheelEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawPointerEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawPointerGestureEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawPointerGestureEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawSizeEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawSizeEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawSizeEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawSizeEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawTextInputEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawTextInputEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawTextInputEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawTouchEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawTouchEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs diff --git a/src/Avalonia.Input/ScrollGestureEventArgs.cs b/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs similarity index 100% rename from src/Avalonia.Input/ScrollGestureEventArgs.cs rename to src/Avalonia.Base/Input/ScrollGestureEventArgs.cs diff --git a/src/Avalonia.Input/TappedEventArgs.cs b/src/Avalonia.Base/Input/TappedEventArgs.cs similarity index 100% rename from src/Avalonia.Input/TappedEventArgs.cs rename to src/Avalonia.Base/Input/TappedEventArgs.cs diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs similarity index 100% rename from src/Avalonia.Input/TextInput/ITextInputMethodClient.cs rename to src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs similarity index 100% rename from src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs rename to src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Base/Input/TextInput/InputMethodManager.cs similarity index 100% rename from src/Avalonia.Input/TextInput/InputMethodManager.cs rename to src/Avalonia.Base/Input/TextInput/InputMethodManager.cs diff --git a/src/Avalonia.Input/TextInput/TextInputContentType.cs b/src/Avalonia.Base/Input/TextInput/TextInputContentType.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TextInputContentType.cs rename to src/Avalonia.Base/Input/TextInput/TextInputContentType.cs diff --git a/src/Avalonia.Input/TextInput/TextInputMethodClientRequestedEventArgs.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TextInputMethodClientRequestedEventArgs.cs rename to src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs diff --git a/src/Avalonia.Input/TextInput/TextInputOptions.cs b/src/Avalonia.Base/Input/TextInput/TextInputOptions.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TextInputOptions.cs rename to src/Avalonia.Base/Input/TextInput/TextInputOptions.cs diff --git a/src/Avalonia.Input/TextInput/TransformTrackingHelper.cs b/src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TransformTrackingHelper.cs rename to src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs diff --git a/src/Avalonia.Input/TextInputEventArgs.cs b/src/Avalonia.Base/Input/TextInputEventArgs.cs similarity index 100% rename from src/Avalonia.Input/TextInputEventArgs.cs rename to src/Avalonia.Base/Input/TextInputEventArgs.cs diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs similarity index 100% rename from src/Avalonia.Input/TouchDevice.cs rename to src/Avalonia.Base/Input/TouchDevice.cs diff --git a/src/Avalonia.Input/VectorEventArgs.cs b/src/Avalonia.Base/Input/VectorEventArgs.cs similarity index 100% rename from src/Avalonia.Input/VectorEventArgs.cs rename to src/Avalonia.Base/Input/VectorEventArgs.cs diff --git a/src/Avalonia.Interactivity/EventRoute.cs b/src/Avalonia.Base/Interactivity/EventRoute.cs similarity index 100% rename from src/Avalonia.Interactivity/EventRoute.cs rename to src/Avalonia.Base/Interactivity/EventRoute.cs diff --git a/src/Avalonia.Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs similarity index 100% rename from src/Avalonia.Interactivity/IInteractive.cs rename to src/Avalonia.Base/Interactivity/IInteractive.cs diff --git a/src/Avalonia.Interactivity/Interactive.cs b/src/Avalonia.Base/Interactivity/Interactive.cs similarity index 100% rename from src/Avalonia.Interactivity/Interactive.cs rename to src/Avalonia.Base/Interactivity/Interactive.cs diff --git a/src/Avalonia.Interactivity/InteractiveExtensions.cs b/src/Avalonia.Base/Interactivity/InteractiveExtensions.cs similarity index 100% rename from src/Avalonia.Interactivity/InteractiveExtensions.cs rename to src/Avalonia.Base/Interactivity/InteractiveExtensions.cs diff --git a/src/Avalonia.Interactivity/RoutedEvent.cs b/src/Avalonia.Base/Interactivity/RoutedEvent.cs similarity index 100% rename from src/Avalonia.Interactivity/RoutedEvent.cs rename to src/Avalonia.Base/Interactivity/RoutedEvent.cs diff --git a/src/Avalonia.Interactivity/RoutedEventArgs.cs b/src/Avalonia.Base/Interactivity/RoutedEventArgs.cs similarity index 100% rename from src/Avalonia.Interactivity/RoutedEventArgs.cs rename to src/Avalonia.Base/Interactivity/RoutedEventArgs.cs diff --git a/src/Avalonia.Interactivity/RoutedEventRegistry.cs b/src/Avalonia.Base/Interactivity/RoutedEventRegistry.cs similarity index 100% rename from src/Avalonia.Interactivity/RoutedEventRegistry.cs rename to src/Avalonia.Base/Interactivity/RoutedEventRegistry.cs diff --git a/src/Avalonia.Layout/AttachedLayout.cs b/src/Avalonia.Base/Layout/AttachedLayout.cs similarity index 100% rename from src/Avalonia.Layout/AttachedLayout.cs rename to src/Avalonia.Base/Layout/AttachedLayout.cs diff --git a/src/Avalonia.Layout/EffectiveViewportChangedEventArgs.cs b/src/Avalonia.Base/Layout/EffectiveViewportChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Layout/EffectiveViewportChangedEventArgs.cs rename to src/Avalonia.Base/Layout/EffectiveViewportChangedEventArgs.cs diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Base/Layout/ElementManager.cs similarity index 100% rename from src/Avalonia.Layout/ElementManager.cs rename to src/Avalonia.Base/Layout/ElementManager.cs diff --git a/src/Avalonia.Layout/FlowLayoutAlgorithm.cs b/src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs similarity index 100% rename from src/Avalonia.Layout/FlowLayoutAlgorithm.cs rename to src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs diff --git a/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs b/src/Avalonia.Base/Layout/IEmbeddedLayoutRoot.cs similarity index 100% rename from src/Avalonia.Layout/IEmbeddedLayoutRoot.cs rename to src/Avalonia.Base/Layout/IEmbeddedLayoutRoot.cs diff --git a/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs b/src/Avalonia.Base/Layout/IFlowLayoutAlgorithmDelegates.cs similarity index 100% rename from src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs rename to src/Avalonia.Base/Layout/IFlowLayoutAlgorithmDelegates.cs diff --git a/src/Avalonia.Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs similarity index 100% rename from src/Avalonia.Layout/ILayoutManager.cs rename to src/Avalonia.Base/Layout/ILayoutManager.cs diff --git a/src/Avalonia.Layout/ILayoutRoot.cs b/src/Avalonia.Base/Layout/ILayoutRoot.cs similarity index 100% rename from src/Avalonia.Layout/ILayoutRoot.cs rename to src/Avalonia.Base/Layout/ILayoutRoot.cs diff --git a/src/Avalonia.Layout/ILayoutable.cs b/src/Avalonia.Base/Layout/ILayoutable.cs similarity index 100% rename from src/Avalonia.Layout/ILayoutable.cs rename to src/Avalonia.Base/Layout/ILayoutable.cs diff --git a/src/Avalonia.Layout/LayoutContext.cs b/src/Avalonia.Base/Layout/LayoutContext.cs similarity index 100% rename from src/Avalonia.Layout/LayoutContext.cs rename to src/Avalonia.Base/Layout/LayoutContext.cs diff --git a/src/Avalonia.Layout/LayoutContextAdapter.cs b/src/Avalonia.Base/Layout/LayoutContextAdapter.cs similarity index 100% rename from src/Avalonia.Layout/LayoutContextAdapter.cs rename to src/Avalonia.Base/Layout/LayoutContextAdapter.cs diff --git a/src/Avalonia.Layout/LayoutExtensions.cs b/src/Avalonia.Base/Layout/LayoutExtensions.cs similarity index 100% rename from src/Avalonia.Layout/LayoutExtensions.cs rename to src/Avalonia.Base/Layout/LayoutExtensions.cs diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Base/Layout/LayoutHelper.cs similarity index 100% rename from src/Avalonia.Layout/LayoutHelper.cs rename to src/Avalonia.Base/Layout/LayoutHelper.cs diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs similarity index 100% rename from src/Avalonia.Layout/LayoutManager.cs rename to src/Avalonia.Base/Layout/LayoutManager.cs diff --git a/src/Avalonia.Layout/LayoutQueue.cs b/src/Avalonia.Base/Layout/LayoutQueue.cs similarity index 100% rename from src/Avalonia.Layout/LayoutQueue.cs rename to src/Avalonia.Base/Layout/LayoutQueue.cs diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs similarity index 100% rename from src/Avalonia.Layout/Layoutable.cs rename to src/Avalonia.Base/Layout/Layoutable.cs diff --git a/src/Avalonia.Layout/NonVirtualizingLayout.cs b/src/Avalonia.Base/Layout/NonVirtualizingLayout.cs similarity index 100% rename from src/Avalonia.Layout/NonVirtualizingLayout.cs rename to src/Avalonia.Base/Layout/NonVirtualizingLayout.cs diff --git a/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs b/src/Avalonia.Base/Layout/NonVirtualizingLayoutContext.cs similarity index 100% rename from src/Avalonia.Layout/NonVirtualizingLayoutContext.cs rename to src/Avalonia.Base/Layout/NonVirtualizingLayoutContext.cs diff --git a/src/Avalonia.Layout/NonVirtualizingStackLayout.cs b/src/Avalonia.Base/Layout/NonVirtualizingStackLayout.cs similarity index 100% rename from src/Avalonia.Layout/NonVirtualizingStackLayout.cs rename to src/Avalonia.Base/Layout/NonVirtualizingStackLayout.cs diff --git a/src/Avalonia.Layout/Orientation.cs b/src/Avalonia.Base/Layout/Orientation.cs similarity index 100% rename from src/Avalonia.Layout/Orientation.cs rename to src/Avalonia.Base/Layout/Orientation.cs diff --git a/src/Avalonia.Layout/OrientationBasedMeasures.cs b/src/Avalonia.Base/Layout/OrientationBasedMeasures.cs similarity index 100% rename from src/Avalonia.Layout/OrientationBasedMeasures.cs rename to src/Avalonia.Base/Layout/OrientationBasedMeasures.cs diff --git a/src/Avalonia.Layout/StackLayout.cs b/src/Avalonia.Base/Layout/StackLayout.cs similarity index 100% rename from src/Avalonia.Layout/StackLayout.cs rename to src/Avalonia.Base/Layout/StackLayout.cs diff --git a/src/Avalonia.Layout/StackLayoutState.cs b/src/Avalonia.Base/Layout/StackLayoutState.cs similarity index 100% rename from src/Avalonia.Layout/StackLayoutState.cs rename to src/Avalonia.Base/Layout/StackLayoutState.cs diff --git a/src/Avalonia.Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs similarity index 100% rename from src/Avalonia.Layout/UniformGridLayout.cs rename to src/Avalonia.Base/Layout/UniformGridLayout.cs diff --git a/src/Avalonia.Layout/UniformGridLayoutState.cs b/src/Avalonia.Base/Layout/UniformGridLayoutState.cs similarity index 100% rename from src/Avalonia.Layout/UniformGridLayoutState.cs rename to src/Avalonia.Base/Layout/UniformGridLayoutState.cs diff --git a/src/Avalonia.Layout/Utils/ListUtils.cs b/src/Avalonia.Base/Layout/Utils/ListUtils.cs similarity index 100% rename from src/Avalonia.Layout/Utils/ListUtils.cs rename to src/Avalonia.Base/Layout/Utils/ListUtils.cs diff --git a/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs b/src/Avalonia.Base/Layout/VirtualLayoutContextAdapter.cs similarity index 100% rename from src/Avalonia.Layout/VirtualLayoutContextAdapter.cs rename to src/Avalonia.Base/Layout/VirtualLayoutContextAdapter.cs diff --git a/src/Avalonia.Layout/VirtualizingLayout.cs b/src/Avalonia.Base/Layout/VirtualizingLayout.cs similarity index 100% rename from src/Avalonia.Layout/VirtualizingLayout.cs rename to src/Avalonia.Base/Layout/VirtualizingLayout.cs diff --git a/src/Avalonia.Layout/VirtualizingLayoutContext.cs b/src/Avalonia.Base/Layout/VirtualizingLayoutContext.cs similarity index 100% rename from src/Avalonia.Layout/VirtualizingLayoutContext.cs rename to src/Avalonia.Base/Layout/VirtualizingLayoutContext.cs diff --git a/src/Avalonia.Layout/WrapLayout/UvBounds.cs b/src/Avalonia.Base/Layout/WrapLayout/UvBounds.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/UvBounds.cs rename to src/Avalonia.Base/Layout/WrapLayout/UvBounds.cs diff --git a/src/Avalonia.Layout/WrapLayout/UvMeasure.cs b/src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/UvMeasure.cs rename to src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs diff --git a/src/Avalonia.Layout/WrapLayout/WrapItem.cs b/src/Avalonia.Base/Layout/WrapLayout/WrapItem.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/WrapItem.cs rename to src/Avalonia.Base/Layout/WrapLayout/WrapItem.cs diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayout.cs b/src/Avalonia.Base/Layout/WrapLayout/WrapLayout.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/WrapLayout.cs rename to src/Avalonia.Base/Layout/WrapLayout/WrapLayout.cs diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs b/src/Avalonia.Base/Layout/WrapLayout/WrapLayoutState.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs rename to src/Avalonia.Base/Layout/WrapLayout/WrapLayoutState.cs diff --git a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs rename to src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Base/LogicalTree/ControlLocator.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ControlLocator.cs rename to src/Avalonia.Base/LogicalTree/ControlLocator.cs diff --git a/src/Avalonia.Styling/LogicalTree/IChildIndexProvider.cs b/src/Avalonia.Base/LogicalTree/IChildIndexProvider.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/IChildIndexProvider.cs rename to src/Avalonia.Base/LogicalTree/IChildIndexProvider.cs diff --git a/src/Avalonia.Styling/LogicalTree/ILogical.cs b/src/Avalonia.Base/LogicalTree/ILogical.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ILogical.cs rename to src/Avalonia.Base/LogicalTree/ILogical.cs diff --git a/src/Avalonia.Styling/LogicalTree/ILogicalRoot.cs b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ILogicalRoot.cs rename to src/Avalonia.Base/LogicalTree/ILogicalRoot.cs diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Base/LogicalTree/LogicalExtensions.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs rename to src/Avalonia.Base/LogicalTree/LogicalExtensions.cs diff --git a/src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs b/src/Avalonia.Base/LogicalTree/LogicalTreeAttachmentEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs rename to src/Avalonia.Base/LogicalTree/LogicalTreeAttachmentEventArgs.cs diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Base/Matrix.cs similarity index 100% rename from src/Avalonia.Visuals/Matrix.cs rename to src/Avalonia.Base/Matrix.cs diff --git a/src/Avalonia.Visuals/Media/AcrylicBackgroundSource.cs b/src/Avalonia.Base/Media/AcrylicBackgroundSource.cs similarity index 100% rename from src/Avalonia.Visuals/Media/AcrylicBackgroundSource.cs rename to src/Avalonia.Base/Media/AcrylicBackgroundSource.cs diff --git a/src/Avalonia.Visuals/Media/AlignmentX.cs b/src/Avalonia.Base/Media/AlignmentX.cs similarity index 100% rename from src/Avalonia.Visuals/Media/AlignmentX.cs rename to src/Avalonia.Base/Media/AlignmentX.cs diff --git a/src/Avalonia.Visuals/Media/AlignmentY.cs b/src/Avalonia.Base/Media/AlignmentY.cs similarity index 100% rename from src/Avalonia.Visuals/Media/AlignmentY.cs rename to src/Avalonia.Base/Media/AlignmentY.cs diff --git a/src/Avalonia.Visuals/Media/ArcSegment.cs b/src/Avalonia.Base/Media/ArcSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ArcSegment.cs rename to src/Avalonia.Base/Media/ArcSegment.cs diff --git a/src/Avalonia.Visuals/Media/BaselineAlignment.cs b/src/Avalonia.Base/Media/BaselineAlignment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BaselineAlignment.cs rename to src/Avalonia.Base/Media/BaselineAlignment.cs diff --git a/src/Avalonia.Visuals/Media/BezierSegment .cs b/src/Avalonia.Base/Media/BezierSegment .cs similarity index 100% rename from src/Avalonia.Visuals/Media/BezierSegment .cs rename to src/Avalonia.Base/Media/BezierSegment .cs diff --git a/src/Avalonia.Visuals/Media/BoxShadow.cs b/src/Avalonia.Base/Media/BoxShadow.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BoxShadow.cs rename to src/Avalonia.Base/Media/BoxShadow.cs diff --git a/src/Avalonia.Visuals/Media/BoxShadows.cs b/src/Avalonia.Base/Media/BoxShadows.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BoxShadows.cs rename to src/Avalonia.Base/Media/BoxShadows.cs diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Brush.cs rename to src/Avalonia.Base/Media/Brush.cs diff --git a/src/Avalonia.Visuals/Media/BrushConverter.cs b/src/Avalonia.Base/Media/BrushConverter.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BrushConverter.cs rename to src/Avalonia.Base/Media/BrushConverter.cs diff --git a/src/Avalonia.Visuals/Media/BrushExtensions.cs b/src/Avalonia.Base/Media/BrushExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BrushExtensions.cs rename to src/Avalonia.Base/Media/BrushExtensions.cs diff --git a/src/Avalonia.Visuals/Media/BrushMappingMode.cs b/src/Avalonia.Base/Media/BrushMappingMode.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BrushMappingMode.cs rename to src/Avalonia.Base/Media/BrushMappingMode.cs diff --git a/src/Avalonia.Visuals/Media/Brushes.cs b/src/Avalonia.Base/Media/Brushes.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Brushes.cs rename to src/Avalonia.Base/Media/Brushes.cs diff --git a/src/Avalonia.Visuals/Media/CharacterHit.cs b/src/Avalonia.Base/Media/CharacterHit.cs similarity index 100% rename from src/Avalonia.Visuals/Media/CharacterHit.cs rename to src/Avalonia.Base/Media/CharacterHit.cs diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Color.cs rename to src/Avalonia.Base/Media/Color.cs diff --git a/src/Avalonia.Visuals/Media/Colors.cs b/src/Avalonia.Base/Media/Colors.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Colors.cs rename to src/Avalonia.Base/Media/Colors.cs diff --git a/src/Avalonia.Visuals/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ConicGradientBrush.cs rename to src/Avalonia.Base/Media/ConicGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Base/Media/DashStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/DashStyle.cs rename to src/Avalonia.Base/Media/DashStyle.cs diff --git a/src/Avalonia.Visuals/Media/Drawing.cs b/src/Avalonia.Base/Media/Drawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Drawing.cs rename to src/Avalonia.Base/Media/Drawing.cs diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs similarity index 99% rename from src/Avalonia.Visuals/Media/DrawingContext.cs rename to src/Avalonia.Base/Media/DrawingContext.cs index d0a73ca9a8..d0f3b9e21a 100644 --- a/src/Avalonia.Visuals/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Threading; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs similarity index 100% rename from src/Avalonia.Visuals/Media/DrawingGroup.cs rename to src/Avalonia.Base/Media/DrawingGroup.cs diff --git a/src/Avalonia.Visuals/Media/DrawingImage.cs b/src/Avalonia.Base/Media/DrawingImage.cs similarity index 96% rename from src/Avalonia.Visuals/Media/DrawingImage.cs rename to src/Avalonia.Base/Media/DrawingImage.cs index 488822b693..1663e384b5 100644 --- a/src/Avalonia.Visuals/Media/DrawingImage.cs +++ b/src/Avalonia.Base/Media/DrawingImage.cs @@ -1,8 +1,6 @@ using System; -using Avalonia.Data; using Avalonia.Metadata; -using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Base/Media/EllipseGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/EllipseGeometry.cs rename to src/Avalonia.Base/Media/EllipseGeometry.cs diff --git a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/ExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/ExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/FillRule.cs b/src/Avalonia.Base/Media/FillRule.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FillRule.cs rename to src/Avalonia.Base/Media/FillRule.cs diff --git a/src/Avalonia.Visuals/Media/FlowDirection.cs b/src/Avalonia.Base/Media/FlowDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FlowDirection.cs rename to src/Avalonia.Base/Media/FlowDirection.cs diff --git a/src/Avalonia.Visuals/Media/FontFallback.cs b/src/Avalonia.Base/Media/FontFallback.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontFallback.cs rename to src/Avalonia.Base/Media/FontFallback.cs diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Base/Media/FontFamily.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontFamily.cs rename to src/Avalonia.Base/Media/FontFamily.cs diff --git a/src/Avalonia.Visuals/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontManager.cs rename to src/Avalonia.Base/Media/FontManager.cs diff --git a/src/Avalonia.Visuals/Media/FontManagerOptions.cs b/src/Avalonia.Base/Media/FontManagerOptions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontManagerOptions.cs rename to src/Avalonia.Base/Media/FontManagerOptions.cs diff --git a/src/Avalonia.Visuals/Media/FontStretch.cs b/src/Avalonia.Base/Media/FontStretch.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontStretch.cs rename to src/Avalonia.Base/Media/FontStretch.cs diff --git a/src/Avalonia.Visuals/Media/FontStyle.cs b/src/Avalonia.Base/Media/FontStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontStyle.cs rename to src/Avalonia.Base/Media/FontStyle.cs diff --git a/src/Avalonia.Visuals/Media/FontWeight.cs b/src/Avalonia.Base/Media/FontWeight.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontWeight.cs rename to src/Avalonia.Base/Media/FontWeight.cs diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs rename to src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs rename to src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs b/src/Avalonia.Base/Media/Fonts/FontFamilyLoader.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs rename to src/Avalonia.Base/Media/Fonts/FontFamilyLoader.cs diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FormattedText.cs rename to src/Avalonia.Base/Media/FormattedText.cs diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Base/Media/Geometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Geometry.cs rename to src/Avalonia.Base/Media/Geometry.cs diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GeometryDrawing.cs rename to src/Avalonia.Base/Media/GeometryDrawing.cs diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphRun.cs rename to src/Avalonia.Base/Media/GlyphRun.cs diff --git a/src/Avalonia.Visuals/Media/GlyphRunDrawing.cs b/src/Avalonia.Base/Media/GlyphRunDrawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphRunDrawing.cs rename to src/Avalonia.Base/Media/GlyphRunDrawing.cs diff --git a/src/Avalonia.Visuals/Media/GlyphRunMetrics.cs b/src/Avalonia.Base/Media/GlyphRunMetrics.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphRunMetrics.cs rename to src/Avalonia.Base/Media/GlyphRunMetrics.cs diff --git a/src/Avalonia.Visuals/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphTypeface.cs rename to src/Avalonia.Base/Media/GlyphTypeface.cs diff --git a/src/Avalonia.Visuals/Media/GradientBrush.cs b/src/Avalonia.Base/Media/GradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientBrush.cs rename to src/Avalonia.Base/Media/GradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/GradientSpreadMethod.cs b/src/Avalonia.Base/Media/GradientSpreadMethod.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientSpreadMethod.cs rename to src/Avalonia.Base/Media/GradientSpreadMethod.cs diff --git a/src/Avalonia.Visuals/Media/GradientStop.cs b/src/Avalonia.Base/Media/GradientStop.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientStop.cs rename to src/Avalonia.Base/Media/GradientStop.cs diff --git a/src/Avalonia.Visuals/Media/GradientStops.cs b/src/Avalonia.Base/Media/GradientStops.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientStops.cs rename to src/Avalonia.Base/Media/GradientStops.cs diff --git a/src/Avalonia.Visuals/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs similarity index 100% rename from src/Avalonia.Visuals/Media/HslColor.cs rename to src/Avalonia.Base/Media/HslColor.cs diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs similarity index 100% rename from src/Avalonia.Visuals/Media/HsvColor.cs rename to src/Avalonia.Base/Media/HsvColor.cs diff --git a/src/Avalonia.Visuals/Media/IAffectsRender.cs b/src/Avalonia.Base/Media/IAffectsRender.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IAffectsRender.cs rename to src/Avalonia.Base/Media/IAffectsRender.cs diff --git a/src/Avalonia.Visuals/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IBrush.cs rename to src/Avalonia.Base/Media/IBrush.cs diff --git a/src/Avalonia.Visuals/Media/IConicGradientBrush.cs b/src/Avalonia.Base/Media/IConicGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IConicGradientBrush.cs rename to src/Avalonia.Base/Media/IConicGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IDashStyle.cs rename to src/Avalonia.Base/Media/IDashStyle.cs diff --git a/src/Avalonia.Visuals/Media/IExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/IGradientBrush.cs b/src/Avalonia.Base/Media/IGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IGradientBrush.cs rename to src/Avalonia.Base/Media/IGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/IGradientStop.cs b/src/Avalonia.Base/Media/IGradientStop.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IGradientStop.cs rename to src/Avalonia.Base/Media/IGradientStop.cs diff --git a/src/Avalonia.Visuals/Media/IImage.cs b/src/Avalonia.Base/Media/IImage.cs similarity index 93% rename from src/Avalonia.Visuals/Media/IImage.cs rename to src/Avalonia.Base/Media/IImage.cs index aff2a9ddf9..cbe25b7b58 100644 --- a/src/Avalonia.Visuals/Media/IImage.cs +++ b/src/Avalonia.Base/Media/IImage.cs @@ -1,5 +1,4 @@ -using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IImageBrush.cs rename to src/Avalonia.Base/Media/IImageBrush.cs diff --git a/src/Avalonia.Visuals/Media/ILinearGradientBrush.cs b/src/Avalonia.Base/Media/ILinearGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ILinearGradientBrush.cs rename to src/Avalonia.Base/Media/ILinearGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IMutableBrush.cs rename to src/Avalonia.Base/Media/IMutableBrush.cs diff --git a/src/Avalonia.Visuals/Media/IMutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IMutableExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/IMutableTransform.cs b/src/Avalonia.Base/Media/IMutableTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IMutableTransform.cs rename to src/Avalonia.Base/Media/IMutableTransform.cs diff --git a/src/Avalonia.Visuals/Media/IPen.cs b/src/Avalonia.Base/Media/IPen.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IPen.cs rename to src/Avalonia.Base/Media/IPen.cs diff --git a/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs b/src/Avalonia.Base/Media/IRadialGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IRadialGradientBrush.cs rename to src/Avalonia.Base/Media/IRadialGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ISolidColorBrush.cs rename to src/Avalonia.Base/Media/ISolidColorBrush.cs diff --git a/src/Avalonia.Visuals/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs similarity index 97% rename from src/Avalonia.Visuals/Media/ITileBrush.cs rename to src/Avalonia.Base/Media/ITileBrush.cs index 12fb221a89..991857eec9 100644 --- a/src/Avalonia.Visuals/Media/ITileBrush.cs +++ b/src/Avalonia.Base/Media/ITileBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/ITransform.cs b/src/Avalonia.Base/Media/ITransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ITransform.cs rename to src/Avalonia.Base/Media/ITransform.cs diff --git a/src/Avalonia.Visuals/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IVisualBrush.cs rename to src/Avalonia.Base/Media/IVisualBrush.cs diff --git a/src/Avalonia.Visuals/Media/ImageBrush.cs b/src/Avalonia.Base/Media/ImageBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ImageBrush.cs rename to src/Avalonia.Base/Media/ImageBrush.cs diff --git a/src/Avalonia.Visuals/Media/ImageDrawing.cs b/src/Avalonia.Base/Media/ImageDrawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ImageDrawing.cs rename to src/Avalonia.Base/Media/ImageDrawing.cs diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs similarity index 99% rename from src/Avalonia.Visuals/Media/Imaging/Bitmap.cs rename to src/Avalonia.Base/Media/Imaging/Bitmap.cs index 647ce13528..5f1617d778 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs @@ -2,7 +2,6 @@ using System; using System.IO; using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Imaging { diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs similarity index 97% rename from src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs rename to src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs index 473b43dab3..eb39020939 100644 --- a/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs +++ b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Visuals.Media.Imaging +namespace Avalonia.Media.Imaging { /// /// Controls the way the bitmaps are drawn together. diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs similarity index 93% rename from src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs rename to src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs index 5fa48a5ec8..7cdb5d8b9f 100644 --- a/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs +++ b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Visuals.Media.Imaging +namespace Avalonia.Media.Imaging { /// /// Controls the performance and quality of bitmap scaling. diff --git a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs rename to src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs index be1f6db561..70f9fbf567 100644 --- a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs @@ -1,5 +1,5 @@ using System; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media.Imaging { diff --git a/src/Avalonia.Visuals/Media/Imaging/IBitmap.cs b/src/Avalonia.Base/Media/Imaging/IBitmap.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Imaging/IBitmap.cs rename to src/Avalonia.Base/Media/Imaging/IBitmap.cs diff --git a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs rename to src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs rename to src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs index 40a24db338..1f39b1344d 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs @@ -1,8 +1,6 @@ using System; using System.IO; -using System.Threading.Tasks; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Imaging { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientStop.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientStop.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableGradientStop.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableGradientStop.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index 2d4fe45c8e..c36e82eacb 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -1,5 +1,4 @@ using Avalonia.Media.Imaging; -using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Immutable { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs b/src/Avalonia.Base/Media/Immutable/ImmutablePen.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs rename to src/Avalonia.Base/Media/Immutable/ImmutablePen.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTextDecoration.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTextDecoration.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableTextDecoration.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableTextDecoration.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 1019751733..2c1844a2c2 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media.Immutable { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTransform.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableTransform.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs similarity index 94% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 0fd2905660..8ecef63237 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Avalonia.VisualTree; namespace Avalonia.Media.Immutable @@ -33,7 +33,7 @@ namespace Avalonia.Media.Immutable RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + Imaging.BitmapInterpolationMode bitmapInterpolationMode = Imaging.BitmapInterpolationMode.Default) : base( alignmentX, alignmentY, diff --git a/src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/ImmutableExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/ImmutableExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Base/Media/KnownColors.cs similarity index 100% rename from src/Avalonia.Visuals/Media/KnownColors.cs rename to src/Avalonia.Base/Media/KnownColors.cs diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Base/Media/LineGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/LineGeometry.cs rename to src/Avalonia.Base/Media/LineGeometry.cs diff --git a/src/Avalonia.Visuals/Media/LineSegment.cs b/src/Avalonia.Base/Media/LineSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/LineSegment.cs rename to src/Avalonia.Base/Media/LineSegment.cs diff --git a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs b/src/Avalonia.Base/Media/LinearGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/LinearGradientBrush.cs rename to src/Avalonia.Base/Media/LinearGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/MaterialExtensions.cs b/src/Avalonia.Base/Media/MaterialExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/MaterialExtensions.cs rename to src/Avalonia.Base/Media/MaterialExtensions.cs diff --git a/src/Avalonia.Visuals/Media/MatrixTransform.cs b/src/Avalonia.Base/Media/MatrixTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/MatrixTransform.cs rename to src/Avalonia.Base/Media/MatrixTransform.cs diff --git a/src/Avalonia.Visuals/Media/MediaExtensions.cs b/src/Avalonia.Base/Media/MediaExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/MediaExtensions.cs rename to src/Avalonia.Base/Media/MediaExtensions.cs diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Base/Media/PathFigure.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathFigure.cs rename to src/Avalonia.Base/Media/PathFigure.cs diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Base/Media/PathGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathGeometry.cs rename to src/Avalonia.Base/Media/PathGeometry.cs diff --git a/src/Avalonia.Visuals/Media/PathGeometryCollections.cs b/src/Avalonia.Base/Media/PathGeometryCollections.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathGeometryCollections.cs rename to src/Avalonia.Base/Media/PathGeometryCollections.cs diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Base/Media/PathMarkupParser.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathMarkupParser.cs rename to src/Avalonia.Base/Media/PathMarkupParser.cs diff --git a/src/Avalonia.Visuals/Media/PathSegment.cs b/src/Avalonia.Base/Media/PathSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathSegment.cs rename to src/Avalonia.Base/Media/PathSegment.cs diff --git a/src/Avalonia.Visuals/Media/Pen.cs b/src/Avalonia.Base/Media/Pen.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Pen.cs rename to src/Avalonia.Base/Media/Pen.cs diff --git a/src/Avalonia.Visuals/Media/PenLineCap.cs b/src/Avalonia.Base/Media/PenLineCap.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PenLineCap.cs rename to src/Avalonia.Base/Media/PenLineCap.cs diff --git a/src/Avalonia.Visuals/Media/PenLineJoin.cs b/src/Avalonia.Base/Media/PenLineJoin.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PenLineJoin.cs rename to src/Avalonia.Base/Media/PenLineJoin.cs diff --git a/src/Avalonia.Visuals/Media/PolyLineSegment.cs b/src/Avalonia.Base/Media/PolyLineSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PolyLineSegment.cs rename to src/Avalonia.Base/Media/PolyLineSegment.cs diff --git a/src/Avalonia.Visuals/Media/PolylineGeometry.cs b/src/Avalonia.Base/Media/PolylineGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PolylineGeometry.cs rename to src/Avalonia.Base/Media/PolylineGeometry.cs diff --git a/src/Avalonia.Visuals/Media/PreciseEllipticArcHelper.cs b/src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PreciseEllipticArcHelper.cs rename to src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs diff --git a/src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs b/src/Avalonia.Base/Media/QuadraticBezierSegment .cs similarity index 100% rename from src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs rename to src/Avalonia.Base/Media/QuadraticBezierSegment .cs diff --git a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs b/src/Avalonia.Base/Media/RadialGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/RadialGradientBrush.cs rename to src/Avalonia.Base/Media/RadialGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Base/Media/RectangleGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/RectangleGeometry.cs rename to src/Avalonia.Base/Media/RectangleGeometry.cs diff --git a/src/Avalonia.Visuals/Media/RenderOptions.cs b/src/Avalonia.Base/Media/RenderOptions.cs similarity index 97% rename from src/Avalonia.Visuals/Media/RenderOptions.cs rename to src/Avalonia.Base/Media/RenderOptions.cs index 7abbc8a656..5863d0ac58 100644 --- a/src/Avalonia.Visuals/Media/RenderOptions.cs +++ b/src/Avalonia.Base/Media/RenderOptions.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/RotateTransform.cs b/src/Avalonia.Base/Media/RotateTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/RotateTransform.cs rename to src/Avalonia.Base/Media/RotateTransform.cs diff --git a/src/Avalonia.Visuals/Media/ScaleTransform.cs b/src/Avalonia.Base/Media/ScaleTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ScaleTransform.cs rename to src/Avalonia.Base/Media/ScaleTransform.cs diff --git a/src/Avalonia.Visuals/Media/SkewTransform.cs b/src/Avalonia.Base/Media/SkewTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/SkewTransform.cs rename to src/Avalonia.Base/Media/SkewTransform.cs diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Base/Media/SolidColorBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/SolidColorBrush.cs rename to src/Avalonia.Base/Media/SolidColorBrush.cs diff --git a/src/Avalonia.Visuals/Media/StreamGeometry.cs b/src/Avalonia.Base/Media/StreamGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/StreamGeometry.cs rename to src/Avalonia.Base/Media/StreamGeometry.cs diff --git a/src/Avalonia.Visuals/Media/StreamGeometryContext.cs b/src/Avalonia.Base/Media/StreamGeometryContext.cs similarity index 100% rename from src/Avalonia.Visuals/Media/StreamGeometryContext.cs rename to src/Avalonia.Base/Media/StreamGeometryContext.cs diff --git a/src/Avalonia.Visuals/Media/Stretch.cs b/src/Avalonia.Base/Media/Stretch.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Stretch.cs rename to src/Avalonia.Base/Media/Stretch.cs diff --git a/src/Avalonia.Visuals/Media/StretchDirection.cs b/src/Avalonia.Base/Media/StretchDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/StretchDirection.cs rename to src/Avalonia.Base/Media/StretchDirection.cs diff --git a/src/Avalonia.Visuals/Media/SweepDirection.cs b/src/Avalonia.Base/Media/SweepDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/SweepDirection.cs rename to src/Avalonia.Base/Media/SweepDirection.cs diff --git a/src/Avalonia.Visuals/Media/TextAlignment.cs b/src/Avalonia.Base/Media/TextAlignment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextAlignment.cs rename to src/Avalonia.Base/Media/TextAlignment.cs diff --git a/src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs b/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs rename to src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs diff --git a/src/Avalonia.Visuals/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecoration.cs rename to src/Avalonia.Base/Media/TextDecoration.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorationCollection.cs b/src/Avalonia.Base/Media/TextDecorationCollection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorationCollection.cs rename to src/Avalonia.Base/Media/TextDecorationCollection.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorationLocation.cs b/src/Avalonia.Base/Media/TextDecorationLocation.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorationLocation.cs rename to src/Avalonia.Base/Media/TextDecorationLocation.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorationUnit.cs b/src/Avalonia.Base/Media/TextDecorationUnit.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorationUnit.cs rename to src/Avalonia.Base/Media/TextDecorationUnit.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorations.cs b/src/Avalonia.Base/Media/TextDecorations.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorations.cs rename to src/Avalonia.Base/Media/TextDecorations.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/DrawableTextRun.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs rename to src/Avalonia.Base/Media/TextFormatting/DrawableTextRun.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/FontMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/FontMetrics.cs rename to src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs rename to src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs b/src/Avalonia.Base/Media/TextFormatting/GenericTextParagraphProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/GenericTextParagraphProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs rename to src/Avalonia.Base/Media/TextFormatting/ITextSource.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/LogicalDirection.cs b/src/Avalonia.Base/Media/TextFormatting/LogicalDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/LogicalDirection.cs rename to src/Avalonia.Base/Media/TextFormatting/LogicalDirection.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapeableTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ShapeableTextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ShapedBuffer.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/SplitResult.cs b/src/Avalonia.Base/Media/TextFormatting/SplitResult.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/SplitResult.cs rename to src/Avalonia.Base/Media/TextFormatting/SplitResult.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs b/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs rename to src/Avalonia.Base/Media/TextFormatting/TextBounds.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs rename to src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEndOfLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextEndOfLine.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextEndOfLine.cs rename to src/Avalonia.Base/Media/TextFormatting/TextEndOfLine.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEndOfParagraph.cs b/src/Avalonia.Base/Media/TextFormatting/TextEndOfParagraph.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextEndOfParagraph.cs rename to src/Avalonia.Base/Media/TextFormatting/TextEndOfParagraph.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs rename to src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs rename to src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLayout.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLine.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineBreak.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLineBreak.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineMetrics.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLineMetrics.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextParagraphProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/TextParagraphProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRange.cs b/src/Avalonia.Base/Media/TextFormatting/TextRange.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextRange.cs rename to src/Avalonia.Base/Media/TextFormatting/TextRange.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs b/src/Avalonia.Base/Media/TextFormatting/TextRun.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs rename to src/Avalonia.Base/Media/TextFormatting/TextRun.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs rename to src/Avalonia.Base/Media/TextFormatting/TextShaper.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs rename to src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs rename to src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs rename to src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiClass.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiClass.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiData.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiData.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/CodepointEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/CodepointEnumerator.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GeneralCategory.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GeneralCategory.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/GeneralCategory.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/GeneralCategory.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Grapheme.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/Grapheme.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreakClass.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreakClass.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeEnumerator.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreak.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreak.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakPairTable.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakPairTable.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakPairTable.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakPairTable.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrie.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs diff --git a/src/Avalonia.Visuals/Media/TextHitTestResult.cs b/src/Avalonia.Base/Media/TextHitTestResult.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextHitTestResult.cs rename to src/Avalonia.Base/Media/TextHitTestResult.cs diff --git a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs rename to src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextNoneTrimming.cs b/src/Avalonia.Base/Media/TextNoneTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextNoneTrimming.cs rename to src/Avalonia.Base/Media/TextNoneTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs b/src/Avalonia.Base/Media/TextTrailingTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextTrailingTrimming.cs rename to src/Avalonia.Base/Media/TextTrailingTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextTrimming.cs b/src/Avalonia.Base/Media/TextTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextTrimming.cs rename to src/Avalonia.Base/Media/TextTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextWrapping.cs b/src/Avalonia.Base/Media/TextWrapping.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextWrapping.cs rename to src/Avalonia.Base/Media/TextWrapping.cs diff --git a/src/Avalonia.Visuals/Media/TileBrush.cs b/src/Avalonia.Base/Media/TileBrush.cs similarity index 99% rename from src/Avalonia.Visuals/Media/TileBrush.cs rename to src/Avalonia.Base/Media/TileBrush.cs index 0a4bc0e56d..ab1ee2d604 100644 --- a/src/Avalonia.Visuals/Media/TileBrush.cs +++ b/src/Avalonia.Base/Media/TileBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/Transform.cs b/src/Avalonia.Base/Media/Transform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transform.cs rename to src/Avalonia.Base/Media/Transform.cs diff --git a/src/Avalonia.Visuals/Media/TransformConverter.cs b/src/Avalonia.Base/Media/TransformConverter.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TransformConverter.cs rename to src/Avalonia.Base/Media/TransformConverter.cs diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Base/Media/TransformExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TransformExtensions.cs rename to src/Avalonia.Base/Media/TransformExtensions.cs diff --git a/src/Avalonia.Visuals/Media/TransformGroup.cs b/src/Avalonia.Base/Media/TransformGroup.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TransformGroup.cs rename to src/Avalonia.Base/Media/TransformGroup.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs b/src/Avalonia.Base/Media/Transformation/InterpolationUtilities.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs rename to src/Avalonia.Base/Media/Transformation/InterpolationUtilities.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs b/src/Avalonia.Base/Media/Transformation/TransformOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs rename to src/Avalonia.Base/Media/Transformation/TransformOperation.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformOperations.cs b/src/Avalonia.Base/Media/Transformation/TransformOperations.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/TransformOperations.cs rename to src/Avalonia.Base/Media/Transformation/TransformOperations.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformParser.cs b/src/Avalonia.Base/Media/Transformation/TransformParser.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/TransformParser.cs rename to src/Avalonia.Base/Media/Transformation/TransformParser.cs diff --git a/src/Avalonia.Visuals/Media/TranslateTransform.cs b/src/Avalonia.Base/Media/TranslateTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TranslateTransform.cs rename to src/Avalonia.Base/Media/TranslateTransform.cs diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Base/Media/Typeface.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Typeface.cs rename to src/Avalonia.Base/Media/Typeface.cs diff --git a/src/Avalonia.Visuals/Media/UnicodeRange.cs b/src/Avalonia.Base/Media/UnicodeRange.cs similarity index 100% rename from src/Avalonia.Visuals/Media/UnicodeRange.cs rename to src/Avalonia.Base/Media/UnicodeRange.cs diff --git a/src/Avalonia.Visuals/Media/VisualBrush.cs b/src/Avalonia.Base/Media/VisualBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/VisualBrush.cs rename to src/Avalonia.Base/Media/VisualBrush.cs diff --git a/src/Avalonia.Visuals/Media/PixelPoint.cs b/src/Avalonia.Base/PixelPoint.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelPoint.cs rename to src/Avalonia.Base/PixelPoint.cs diff --git a/src/Avalonia.Visuals/Media/PixelRect.cs b/src/Avalonia.Base/PixelRect.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelRect.cs rename to src/Avalonia.Base/PixelRect.cs diff --git a/src/Avalonia.Visuals/Media/PixelSize.cs b/src/Avalonia.Base/PixelSize.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelSize.cs rename to src/Avalonia.Base/PixelSize.cs diff --git a/src/Avalonia.Visuals/Media/PixelVector.cs b/src/Avalonia.Base/PixelVector.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelVector.cs rename to src/Avalonia.Base/PixelVector.cs diff --git a/src/Avalonia.Visuals/Platform/AlphaFormat.cs b/src/Avalonia.Base/Platform/AlphaFormat.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/AlphaFormat.cs rename to src/Avalonia.Base/Platform/AlphaFormat.cs diff --git a/src/Avalonia.Visuals/Platform/IBitmapImpl.cs b/src/Avalonia.Base/Platform/IBitmapImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IBitmapImpl.cs rename to src/Avalonia.Base/Platform/IBitmapImpl.cs diff --git a/src/Avalonia.Input/Platform/ICursorFactory.cs b/src/Avalonia.Base/Platform/ICursorFactory.cs similarity index 100% rename from src/Avalonia.Input/Platform/ICursorFactory.cs rename to src/Avalonia.Base/Platform/ICursorFactory.cs diff --git a/src/Avalonia.Input/Platform/ICursorImpl.cs b/src/Avalonia.Base/Platform/ICursorImpl.cs similarity index 100% rename from src/Avalonia.Input/Platform/ICursorImpl.cs rename to src/Avalonia.Base/Platform/ICursorImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs similarity index 99% rename from src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs rename to src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 82af6ff0b6..4e6612e908 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -2,7 +2,7 @@ using System; using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Platform { diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextWithAcrylicLikeSupport.cs b/src/Avalonia.Base/Platform/IDrawingContextWithAcrylicLikeSupport.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IDrawingContextWithAcrylicLikeSupport.cs rename to src/Avalonia.Base/Platform/IDrawingContextWithAcrylicLikeSupport.cs diff --git a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IFontManagerImpl.cs rename to src/Avalonia.Base/Platform/IFontManagerImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IGeometryContext.cs b/src/Avalonia.Base/Platform/IGeometryContext.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGeometryContext.cs rename to src/Avalonia.Base/Platform/IGeometryContext.cs diff --git a/src/Avalonia.Visuals/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGeometryImpl.cs rename to src/Avalonia.Base/Platform/IGeometryImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGlyphRunImpl.cs rename to src/Avalonia.Base/Platform/IGlyphRunImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGlyphTypefaceImpl.cs rename to src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs diff --git a/src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs b/src/Avalonia.Base/Platform/ILockedFramebuffer.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs rename to src/Avalonia.Base/Platform/ILockedFramebuffer.cs diff --git a/src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs b/src/Avalonia.Base/Platform/IModuleEnvironmentChecker.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs rename to src/Avalonia.Base/Platform/IModuleEnvironmentChecker.cs diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs similarity index 99% rename from src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs rename to src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index 444cc2eb8c..c46efd46c3 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using Avalonia.Media; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Platform { diff --git a/src/Avalonia.Visuals/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IPlatformSettings.cs rename to src/Avalonia.Base/Platform/IPlatformSettings.cs diff --git a/src/Avalonia.Visuals/Platform/IRenderTarget.cs b/src/Avalonia.Base/Platform/IRenderTarget.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IRenderTarget.cs rename to src/Avalonia.Base/Platform/IRenderTarget.cs diff --git a/src/Avalonia.Visuals/Platform/IRenderTargetBitmapImpl.cs b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IRenderTargetBitmapImpl.cs rename to src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IStreamGeometryContextImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IStreamGeometryContextImpl.cs rename to src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IStreamGeometryImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IStreamGeometryImpl.cs rename to src/Avalonia.Base/Platform/IStreamGeometryImpl.cs diff --git a/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs b/src/Avalonia.Base/Platform/ITextShaperImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/ITextShaperImpl.cs rename to src/Avalonia.Base/Platform/ITextShaperImpl.cs diff --git a/src/Avalonia.Visuals/Platform/ITransformedGeometryImpl.cs b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/ITransformedGeometryImpl.cs rename to src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IWriteableBitmapImpl.cs rename to src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs diff --git a/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs b/src/Avalonia.Base/Platform/LockedFramebuffer.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/LockedFramebuffer.cs rename to src/Avalonia.Base/Platform/LockedFramebuffer.cs diff --git a/src/Avalonia.Visuals/Platform/PathGeometryContext.cs b/src/Avalonia.Base/Platform/PathGeometryContext.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/PathGeometryContext.cs rename to src/Avalonia.Base/Platform/PathGeometryContext.cs diff --git a/src/Avalonia.Visuals/Platform/PixelFormat.cs b/src/Avalonia.Base/Platform/PixelFormat.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/PixelFormat.cs rename to src/Avalonia.Base/Platform/PixelFormat.cs diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Base/Point.cs similarity index 100% rename from src/Avalonia.Visuals/Point.cs rename to src/Avalonia.Base/Point.cs diff --git a/src/Avalonia.Visuals/Points.cs b/src/Avalonia.Base/Points.cs similarity index 100% rename from src/Avalonia.Visuals/Points.cs rename to src/Avalonia.Base/Points.cs diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 9ffb5872f0..4d7f487e00 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -1,15 +1,32 @@ -// Licensed under the MIT license. See licence.md file in the project root for full license information. - using System.Reflection; using System.Runtime.CompilerServices; using Avalonia.Metadata; +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data.Converters")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.GestureRecognizers")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.TextInput")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] + [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Visuals, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.PlatformSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - +[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Base/Rect.cs similarity index 100% rename from src/Avalonia.Visuals/Rect.cs rename to src/Avalonia.Base/Rect.cs diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Base/RelativePoint.cs similarity index 100% rename from src/Avalonia.Visuals/RelativePoint.cs rename to src/Avalonia.Base/RelativePoint.cs diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Base/RelativeRect.cs similarity index 100% rename from src/Avalonia.Visuals/RelativeRect.cs rename to src/Avalonia.Base/RelativeRect.cs diff --git a/src/Avalonia.Visuals/RenderTargetCorruptedException.cs b/src/Avalonia.Base/RenderTargetCorruptedException.cs similarity index 100% rename from src/Avalonia.Visuals/RenderTargetCorruptedException.cs rename to src/Avalonia.Base/RenderTargetCorruptedException.cs diff --git a/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs rename to src/Avalonia.Base/Rendering/DefaultRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Base/Rendering/DeferredRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DeferredRenderer.cs rename to src/Avalonia.Base/Rendering/DeferredRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/DirtyRects.cs b/src/Avalonia.Base/Rendering/DirtyRects.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DirtyRects.cs rename to src/Avalonia.Base/Rendering/DirtyRects.cs diff --git a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs b/src/Avalonia.Base/Rendering/DirtyVisuals.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DirtyVisuals.cs rename to src/Avalonia.Base/Rendering/DirtyVisuals.cs diff --git a/src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs b/src/Avalonia.Base/Rendering/DisplayDirtyRect.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs rename to src/Avalonia.Base/Rendering/DisplayDirtyRect.cs diff --git a/src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs b/src/Avalonia.Base/Rendering/DisplayDirtyRects.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs rename to src/Avalonia.Base/Rendering/DisplayDirtyRects.cs diff --git a/src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs b/src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs rename to src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs diff --git a/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs rename to src/Avalonia.Base/Rendering/IDeferredRendererLock.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderLoop.cs rename to src/Avalonia.Base/Rendering/IRenderLoop.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderLoopTask.cs b/src/Avalonia.Base/Rendering/IRenderLoopTask.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderLoopTask.cs rename to src/Avalonia.Base/Rendering/IRenderLoopTask.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderRoot.cs rename to src/Avalonia.Base/Rendering/IRenderRoot.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderTimer.cs b/src/Avalonia.Base/Rendering/IRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderTimer.cs rename to src/Avalonia.Base/Rendering/IRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderer.cs b/src/Avalonia.Base/Rendering/IRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderer.cs rename to src/Avalonia.Base/Rendering/IRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/IRendererFactory.cs b/src/Avalonia.Base/Rendering/IRendererFactory.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRendererFactory.cs rename to src/Avalonia.Base/Rendering/IRendererFactory.cs diff --git a/src/Avalonia.Visuals/Rendering/IVisualBrushInitialize.cs b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IVisualBrushInitialize.cs rename to src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs diff --git a/src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs rename to src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs rename to src/Avalonia.Base/Rendering/ImmediateRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs rename to src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Base/Rendering/RenderLayer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RenderLayer.cs rename to src/Avalonia.Base/Rendering/RenderLayer.cs diff --git a/src/Avalonia.Visuals/Rendering/RenderLayers.cs b/src/Avalonia.Base/Rendering/RenderLayers.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RenderLayers.cs rename to src/Avalonia.Base/Rendering/RenderLayers.cs diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Base/Rendering/RenderLoop.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RenderLoop.cs rename to src/Avalonia.Base/Rendering/RenderLoop.cs diff --git a/src/Avalonia.Visuals/Rendering/RendererBase.cs b/src/Avalonia.Base/Rendering/RendererBase.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RendererBase.cs rename to src/Avalonia.Base/Rendering/RendererBase.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BitmapBlendModeNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs similarity index 98% rename from src/Avalonia.Visuals/Rendering/SceneGraph/BitmapBlendModeNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs index 45b62b843b..98e89f6549 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/BitmapBlendModeNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs @@ -1,5 +1,5 @@ using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Rendering.SceneGraph { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/BrushDrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/BrushDrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ClipNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ClipNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs similarity index 99% rename from src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs rename to src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 9710ca6c3c..5225b85020 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/DrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/DrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/EllipseNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/EllipseNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryBoundsHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GeometryBoundsHelper.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryClipNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GeometryClipNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/IDrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/IDrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ISceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ISceneBuilder.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs similarity index 99% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs index d3da19d8c9..23267166a5 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs @@ -1,6 +1,6 @@ using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Rendering.SceneGraph { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineBoundsHelper.cs b/src/Avalonia.Base/Rendering/SceneGraph/LineBoundsHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/LineBoundsHelper.cs rename to src/Avalonia.Base/Rendering/SceneGraph/LineBoundsHelper.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Base/Rendering/SceneGraph/Scene.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs rename to src/Avalonia.Base/Rendering/SceneGraph/Scene.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs rename to src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneLayer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs rename to src/Avalonia.Base/Rendering/SceneGraph/SceneLayer.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneLayers.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs rename to src/Avalonia.Base/Rendering/SceneGraph/SceneLayers.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneInvalidatedEventArgs.cs b/src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneInvalidatedEventArgs.cs rename to src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs diff --git a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs b/src/Avalonia.Base/Rendering/SleepLoopRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs rename to src/Avalonia.Base/Rendering/SleepLoopRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs b/src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs rename to src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs rename to src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs diff --git a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs b/src/Avalonia.Base/Rendering/ZIndexComparer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ZIndexComparer.cs rename to src/Avalonia.Base/Rendering/ZIndexComparer.cs diff --git a/src/Avalonia.Visuals/RoundedRect.cs b/src/Avalonia.Base/RoundedRect.cs similarity index 100% rename from src/Avalonia.Visuals/RoundedRect.cs rename to src/Avalonia.Base/RoundedRect.cs diff --git a/src/Avalonia.Base/Settings.StyleCop b/src/Avalonia.Base/Settings.StyleCop deleted file mode 100644 index bb05f99bc1..0000000000 --- a/src/Avalonia.Base/Settings.StyleCop +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Base/Size.cs similarity index 100% rename from src/Avalonia.Visuals/Size.cs rename to src/Avalonia.Base/Size.cs diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs similarity index 100% rename from src/Avalonia.Styling/StyledElement.cs rename to src/Avalonia.Base/StyledElement.cs diff --git a/src/Avalonia.Styling/StyledElementExtensions.cs b/src/Avalonia.Base/StyledElementExtensions.cs similarity index 100% rename from src/Avalonia.Styling/StyledElementExtensions.cs rename to src/Avalonia.Base/StyledElementExtensions.cs diff --git a/src/Avalonia.Styling/Styling/Activators/AndActivator.cs b/src/Avalonia.Base/Styling/Activators/AndActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/AndActivator.cs rename to src/Avalonia.Base/Styling/Activators/AndActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/AndActivatorBuilder.cs b/src/Avalonia.Base/Styling/Activators/AndActivatorBuilder.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/AndActivatorBuilder.cs rename to src/Avalonia.Base/Styling/Activators/AndActivatorBuilder.cs diff --git a/src/Avalonia.Styling/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/IStyleActivator.cs rename to src/Avalonia.Base/Styling/Activators/IStyleActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/IStyleActivatorSink.cs rename to src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs diff --git a/src/Avalonia.Styling/Styling/Activators/NotActivator.cs b/src/Avalonia.Base/Styling/Activators/NotActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/NotActivator.cs rename to src/Avalonia.Base/Styling/Activators/NotActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs b/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs rename to src/Avalonia.Base/Styling/Activators/NthChildActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/OrActivator.cs b/src/Avalonia.Base/Styling/Activators/OrActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/OrActivator.cs rename to src/Avalonia.Base/Styling/Activators/OrActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/OrActivatorBuilder.cs b/src/Avalonia.Base/Styling/Activators/OrActivatorBuilder.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/OrActivatorBuilder.cs rename to src/Avalonia.Base/Styling/Activators/OrActivatorBuilder.cs diff --git a/src/Avalonia.Styling/Styling/Activators/PropertyEqualsActivator.cs b/src/Avalonia.Base/Styling/Activators/PropertyEqualsActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/PropertyEqualsActivator.cs rename to src/Avalonia.Base/Styling/Activators/PropertyEqualsActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/StyleActivatorBase.cs b/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/StyleActivatorBase.cs rename to src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs diff --git a/src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs b/src/Avalonia.Base/Styling/Activators/StyleClassActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs rename to src/Avalonia.Base/Styling/Activators/StyleClassActivator.cs diff --git a/src/Avalonia.Styling/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ChildSelector.cs rename to src/Avalonia.Base/Styling/ChildSelector.cs diff --git a/src/Avalonia.Styling/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/DescendentSelector.cs rename to src/Avalonia.Base/Styling/DescendentSelector.cs diff --git a/src/Avalonia.Styling/Styling/IGlobalStyles.cs b/src/Avalonia.Base/Styling/IGlobalStyles.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IGlobalStyles.cs rename to src/Avalonia.Base/Styling/IGlobalStyles.cs diff --git a/src/Avalonia.Styling/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ISetter.cs rename to src/Avalonia.Base/Styling/ISetter.cs diff --git a/src/Avalonia.Styling/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ISetterInstance.cs rename to src/Avalonia.Base/Styling/ISetterInstance.cs diff --git a/src/Avalonia.Styling/Styling/ISetterValue.cs b/src/Avalonia.Base/Styling/ISetterValue.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ISetterValue.cs rename to src/Avalonia.Base/Styling/ISetterValue.cs diff --git a/src/Avalonia.Styling/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyle.cs rename to src/Avalonia.Base/Styling/IStyle.cs diff --git a/src/Avalonia.Styling/Styling/IStyleHost.cs b/src/Avalonia.Base/Styling/IStyleHost.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyleHost.cs rename to src/Avalonia.Base/Styling/IStyleHost.cs diff --git a/src/Avalonia.Styling/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyleInstance.cs rename to src/Avalonia.Base/Styling/IStyleInstance.cs diff --git a/src/Avalonia.Styling/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyleable.cs rename to src/Avalonia.Base/Styling/IStyleable.cs diff --git a/src/Avalonia.Styling/Styling/IStyler.cs b/src/Avalonia.Base/Styling/IStyler.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyler.cs rename to src/Avalonia.Base/Styling/IStyler.cs diff --git a/src/Avalonia.Styling/Styling/ITemplate.cs b/src/Avalonia.Base/Styling/ITemplate.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ITemplate.cs rename to src/Avalonia.Base/Styling/ITemplate.cs diff --git a/src/Avalonia.Styling/Styling/ITemplatedControl.cs b/src/Avalonia.Base/Styling/ITemplatedControl.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ITemplatedControl.cs rename to src/Avalonia.Base/Styling/ITemplatedControl.cs diff --git a/src/Avalonia.Styling/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/NotSelector.cs rename to src/Avalonia.Base/Styling/NotSelector.cs diff --git a/src/Avalonia.Styling/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/NthChildSelector.cs rename to src/Avalonia.Base/Styling/NthChildSelector.cs diff --git a/src/Avalonia.Styling/Styling/NthLastChildSelector.cs b/src/Avalonia.Base/Styling/NthLastChildSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/NthLastChildSelector.cs rename to src/Avalonia.Base/Styling/NthLastChildSelector.cs diff --git a/src/Avalonia.Styling/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/OrSelector.cs rename to src/Avalonia.Base/Styling/OrSelector.cs diff --git a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs rename to src/Avalonia.Base/Styling/PropertyEqualsSelector.cs diff --git a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs b/src/Avalonia.Base/Styling/PropertySetterBindingInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterBindingInstance.cs diff --git a/src/Avalonia.Styling/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertySetterInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterInstance.cs diff --git a/src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs diff --git a/src/Avalonia.Styling/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Selector.cs rename to src/Avalonia.Base/Styling/Selector.cs diff --git a/src/Avalonia.Styling/Styling/SelectorMatch.cs b/src/Avalonia.Base/Styling/SelectorMatch.cs similarity index 100% rename from src/Avalonia.Styling/Styling/SelectorMatch.cs rename to src/Avalonia.Base/Styling/SelectorMatch.cs diff --git a/src/Avalonia.Styling/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Selectors.cs rename to src/Avalonia.Base/Styling/Selectors.cs diff --git a/src/Avalonia.Styling/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Setter.cs rename to src/Avalonia.Base/Styling/Setter.cs diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Style.cs rename to src/Avalonia.Base/Styling/Style.cs diff --git a/src/Avalonia.Styling/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/StyleInstance.cs rename to src/Avalonia.Base/Styling/StyleInstance.cs diff --git a/src/Avalonia.Styling/Styling/Styler.cs b/src/Avalonia.Base/Styling/Styler.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Styler.cs rename to src/Avalonia.Base/Styling/Styler.cs diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Styles.cs rename to src/Avalonia.Base/Styling/Styles.cs diff --git a/src/Avalonia.Styling/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/TemplateSelector.cs rename to src/Avalonia.Base/Styling/TemplateSelector.cs diff --git a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs rename to src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Base/Thickness.cs similarity index 100% rename from src/Avalonia.Visuals/Thickness.cs rename to src/Avalonia.Base/Thickness.cs diff --git a/src/Avalonia.Visuals/Utilities/ArrayBuilder.cs b/src/Avalonia.Base/Utilities/ArrayBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ArrayBuilder.cs rename to src/Avalonia.Base/Utilities/ArrayBuilder.cs diff --git a/src/Avalonia.Visuals/Utilities/ArraySlice.cs b/src/Avalonia.Base/Utilities/ArraySlice.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ArraySlice.cs rename to src/Avalonia.Base/Utilities/ArraySlice.cs diff --git a/src/Avalonia.Visuals/Utilities/BinarySearchExtension.cs b/src/Avalonia.Base/Utilities/BinarySearchExtension.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/BinarySearchExtension.cs rename to src/Avalonia.Base/Utilities/BinarySearchExtension.cs diff --git a/src/Avalonia.Visuals/Utilities/FrugalList.cs b/src/Avalonia.Base/Utilities/FrugalList.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/FrugalList.cs rename to src/Avalonia.Base/Utilities/FrugalList.cs diff --git a/src/Avalonia.Visuals/Utilities/MappedArraySlice.cs b/src/Avalonia.Base/Utilities/MappedArraySlice.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/MappedArraySlice.cs rename to src/Avalonia.Base/Utilities/MappedArraySlice.cs diff --git a/src/Avalonia.Visuals/Utilities/ReadOnlySlice.cs b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ReadOnlySlice.cs rename to src/Avalonia.Base/Utilities/ReadOnlySlice.cs diff --git a/src/Avalonia.Visuals/Utilities/Span.cs b/src/Avalonia.Base/Utilities/Span.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/Span.cs rename to src/Avalonia.Base/Utilities/Span.cs diff --git a/src/Avalonia.Visuals/Utilities/ValueSpan.cs b/src/Avalonia.Base/Utilities/ValueSpan.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ValueSpan.cs rename to src/Avalonia.Base/Utilities/ValueSpan.cs diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Base/Vector.cs similarity index 100% rename from src/Avalonia.Visuals/Vector.cs rename to src/Avalonia.Base/Vector.cs diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Base/Visual.cs similarity index 100% rename from src/Avalonia.Visuals/Visual.cs rename to src/Avalonia.Base/Visual.cs diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Base/VisualExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/VisualExtensions.cs rename to src/Avalonia.Base/VisualExtensions.cs diff --git a/src/Avalonia.Visuals/VisualTree/IHostedVisualTreeRoot.cs b/src/Avalonia.Base/VisualTree/IHostedVisualTreeRoot.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IHostedVisualTreeRoot.cs rename to src/Avalonia.Base/VisualTree/IHostedVisualTreeRoot.cs diff --git a/src/Avalonia.Visuals/VisualTree/IVisual.cs b/src/Avalonia.Base/VisualTree/IVisual.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IVisual.cs rename to src/Avalonia.Base/VisualTree/IVisual.cs diff --git a/src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs b/src/Avalonia.Base/VisualTree/IVisualTreeHost.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs rename to src/Avalonia.Base/VisualTree/IVisualTreeHost.cs diff --git a/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs b/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs rename to src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs diff --git a/src/Avalonia.Visuals/VisualTree/TransformedBounds.cs b/src/Avalonia.Base/VisualTree/TransformedBounds.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/TransformedBounds.cs rename to src/Avalonia.Base/VisualTree/TransformedBounds.cs diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Base/VisualTree/VisualExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/VisualExtensions.cs rename to src/Avalonia.Base/VisualTree/VisualExtensions.cs diff --git a/src/Avalonia.Visuals/VisualTree/VisualLocator.cs b/src/Avalonia.Base/VisualTree/VisualLocator.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/VisualLocator.cs rename to src/Avalonia.Base/VisualTree/VisualLocator.cs diff --git a/src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs b/src/Avalonia.Base/VisualTreeAttachmentEventArgs.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs rename to src/Avalonia.Base/VisualTreeAttachmentEventArgs.cs diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 9629324c8d..6267c74df9 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -62,34 +62,34 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 0438e4e0e8..410cac72fc 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -4,14 +4,8 @@ Avalonia.Controls.DataGrid - - - - - - diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 5d34261bdf..4d239e69f4 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -6,14 +6,8 @@ - - - - - - diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index f8a7cdc690..0270000d8c 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index bb908e07dc..2fb7c07b6f 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 90221bb922..addc248d58 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -7,7 +7,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Headless { diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt deleted file mode 100644 index 93c4cba5a6..0000000000 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ /dev/null @@ -1,30 +0,0 @@ -Compat issues with assembly Avalonia.Input: -MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Input.Cursor.PlatformCursor.get()' 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.ScrollGestureEndedEvent' 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.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.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(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. -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: 28 diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj deleted file mode 100644 index 6e0321b3aa..0000000000 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - - - - - diff --git a/src/Avalonia.Input/Properties/AssemblyInfo.cs b/src/Avalonia.Input/Properties/AssemblyInfo.cs deleted file mode 100644 index a7126d6ba9..0000000000 --- a/src/Avalonia.Input/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -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")] - -[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Interactivity/ApiCompatBaseline.txt b/src/Avalonia.Interactivity/ApiCompatBaseline.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/src/Avalonia.Interactivity/ApiCompatBaseline.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj deleted file mode 100644 index fa115ed980..0000000000 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Layout/ApiCompatBaseline.txt b/src/Avalonia.Layout/ApiCompatBaseline.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/src/Avalonia.Layout/ApiCompatBaseline.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj deleted file mode 100644 index d94c3eb101..0000000000 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - diff --git a/src/Avalonia.Layout/Properties/AssemblyInfo.cs b/src/Avalonia.Layout/Properties/AssemblyInfo.cs deleted file mode 100644 index efcbf184b5..0000000000 --- a/src/Avalonia.Layout/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: InternalsVisibleTo("Avalonia.Layout.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")] - diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index 07cd295711..76924d060f 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj index 5a8301a2e9..d746c6db7e 100644 --- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -7,7 +7,7 @@ Avalonia.Remote.Protocol - + \ No newline at end of file diff --git a/src/Avalonia.Styling/ApiCompatBaseline.txt b/src/Avalonia.Styling/ApiCompatBaseline.txt deleted file mode 100644 index 0eedc3e360..0000000000 --- a/src/Avalonia.Styling/ApiCompatBaseline.txt +++ /dev/null @@ -1,4 +0,0 @@ -Compat issues with assembly Avalonia.Styling: -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive.get()' is present in the implementation but not in the contract. -Total Issues: 2 diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj deleted file mode 100644 index 260cfb0516..0000000000 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net6.0;netstandard2.0 - Avalonia.Styling - Avalonia - - - - - - - - - diff --git a/src/Avalonia.Styling/Properties/AssemblyInfo.cs b/src/Avalonia.Styling/Properties/AssemblyInfo.cs deleted file mode 100644 index ab034740ed..0000000000 --- a/src/Avalonia.Styling/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] - -[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index ef200b5532..40ed4a0f87 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -3,16 +3,10 @@ net6.0;netstandard2.0 - - - - - - - + diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 885661536c..35603fe216 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -3,16 +3,10 @@ net6.0;netstandard2.0 - - - - - - - + diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt deleted file mode 100644 index 0e82ef7367..0000000000 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ /dev/null @@ -1,188 +0,0 @@ -Compat issues with assembly Avalonia.Visuals: -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.CompositePageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.CrossFade.Start(Avalonia.Visual, Avalonia.Visual)' 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)' is present in the contract but not in the implementation. -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. -MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Media.FormattedText)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Boolean Avalonia.Media.FontManager.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(Avalonia.Platform.IPlatformRenderInterface)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.FormattedText.Bounds.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.FormattedText.Constraint.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Constraint.set(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Double Avalonia.Media.FormattedText.FontSize.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.FontSize.set(System.Double)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IEnumerable Avalonia.Media.FormattedText.GetLines()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextHitTestResult Avalonia.Media.FormattedText.HitTestPoint(Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.FormattedText.HitTestTextPosition(System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IEnumerable Avalonia.Media.FormattedText.HitTestTextRange(System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Media.FormattedText.PlatformImpl.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.FormattedText.Spans.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Spans.set(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.String Avalonia.Media.FormattedText.Text.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Text.set(System.String)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.FormattedText.TextWrapping.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.TextWrapping.set(Avalonia.Media.TextWrapping)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FormattedText.Typeface.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Typeface.set(Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.FormattedTextLine' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.FormattedTextStyleSpan' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor(Avalonia.Media.GlyphTypeface, System.Double, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphAdvances.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphAdvances.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphClusters.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphClusters.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphIndices.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphIndices.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -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. -CannotSealType : Type 'Avalonia.Media.TextHitTestResult' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.TextHitTestResult' is a 'struct' in the implementation but is a 'class' in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsInside.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsTrailing.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.TextPosition.set(System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotMakeTypeAbstract : Type 'Avalonia.Media.TextTrimming' is abstract in the implementation but is not abstract in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.TextTrimming' is a 'class' in the implementation but is a 'struct' in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.CharacterEllipsis' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.None' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.WordEllipsis' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Int32 System.Int32 Avalonia.Media.TextTrimming.value__' 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. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline.get()' is abstract in the implementation but is missing 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. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextFormatting.TextRunProperties Avalonia.Media.TextFormatting.GenericTextParagraphProperties.DefaultTextRunProperties' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Double Avalonia.Media.TextFormatting.GenericTextParagraphProperties.LineHeight' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextAlignment Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextAlignment' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextWrapping' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextFormatting.TextRunProperties Avalonia.Media.TextFormatting.GenericTextParagraphProperties.DefaultTextRunProperties.get()' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Double Avalonia.Media.TextFormatting.GenericTextParagraphProperties.LineHeight.get()' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextAlignment Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextAlignment.get()' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextWrapping.get()' is non-virtual in the implementation but is virtual in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.GenericTextRunProperties..ctor(Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextDecorationCollection, Avalonia.Media.IBrush, Avalonia.Media.IBrush, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapeableTextCharacters..ctor(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters..ctor(Avalonia.Media.GlyphRun, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Collections.Generic.List Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.IBrush, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Media.TextTrimming, Avalonia.Media.TextDecorationCollection, System.Double, System.Double, System.Double, System.Int32, System.Collections.Generic.IReadOnlyList>)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLayout.Size.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Baseline' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.FirstTextSourceIndex' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.Length' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.NewLineLength' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangAfter' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangLeading' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangTrailing' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Start' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.TrailingWhitespaceLength' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Baseline.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.FirstTextSourceIndex.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextLine.GetTextBounds(System.Int32, System.Int32)' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.Length.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLine.LineMetrics.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.NewLineLength.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangAfter.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangLeading.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangTrailing.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Start.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextRange Avalonia.Media.TextFormatting.TextLine.TextRange.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.TrailingWhitespaceLength.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineBreak..ctor(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextLineBreak.RemainingCharacters.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineMetrics..ctor(Avalonia.Size, System.Double, Avalonia.Media.TextFormatting.TextRange, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLineMetrics.Create(System.Collections.Generic.IEnumerable, Avalonia.Media.TextFormatting.TextRange, System.Double, Avalonia.Media.TextFormatting.TextParagraphProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLineMetrics.Size.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextRange Avalonia.Media.TextFormatting.TextLineMetrics.TextRange.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Media.TextFormatting.TextShaper.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. -CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingWordEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingWordEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingWordEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.TextFormatting.Unicode.BiDiClass' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.Unicode.BiDiClass Avalonia.Media.TextFormatting.Unicode.Codepoint.BiDiClass.get()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Platform.ExportRenderingSubsystemAttribute' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawEllipse(Avalonia.Media.IBrush, Avalonia.Media.IPen, Avalonia.Rect)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PopBitmapBlendMode()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PushBitmapBlendMode(Avalonia.Visuals.Media.Imaging.BitmapBlendingMode)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontStretch, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. -TypesMustExist : Type 'Avalonia.Platform.IFormattedTextImpl' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetPointAndTangentAtDistance(System.Double, Avalonia.Point, Avalonia.Point)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetPointAtDistance(System.Double, Avalonia.Point)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetSegment(System.Double, System.Double, System.Boolean, Avalonia.Platform.IGeometryImpl)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGeometryImpl Avalonia.Platform.IPlatformRenderInterface.CreateCombinedGeometry(Avalonia.Media.GeometryCombineMode, Avalonia.Media.Geometry, Avalonia.Media.Geometry)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Platform.IPlatformRenderInterface.CreateFormattedText(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size, System.Collections.Generic.IReadOnlyList)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Platform.IPlatformRenderInterface.CreateFormattedText(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size, System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGeometryImpl Avalonia.Platform.IPlatformRenderInterface.CreateGeometryGroup(Avalonia.Media.FillRule, System.Collections.Generic.IReadOnlyList)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.IO.Stream)' is present in the implementation but not in the contract. -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. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.TextFormatting.TextShaperOptions)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' is present in the contract but not in the implementation. -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. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.VisualTree.IVisual.HasMirrorTransform' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.VisualTree.IVisual.HasMirrorTransform.get()' is present in the implementation but not 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: 186 diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj deleted file mode 100644 index 7e9947812b..0000000000 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - net6.0;netstandard2.0 - Avalonia - true - - - - - - - - - - - - - - - - - - diff --git a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs deleted file mode 100644 index ebff097199..0000000000 --- a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] - -[assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index 6632573ba2..36939f4103 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -2,12 +2,9 @@ using System; using System.IO; using System.Runtime.InteropServices; using Avalonia.Controls.Platform.Surfaces; -using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; -using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; -using static Avalonia.X11.XLib; + namespace Avalonia.X11 { class X11IconLoader : IPlatformIconLoader diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 6624392258..622ec416e8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -56,14 +56,8 @@ - - - - - - diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index 119f57973c..f7f1a111ba 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -11,8 +11,6 @@ - - diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 642808eacb..2548b9f5aa 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -9,7 +9,7 @@ using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.Utilities; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs index 3010f49420..5628ae177c 100644 --- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs +++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs @@ -5,7 +5,7 @@ using System.Security.Cryptography; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Skia.Helpers; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 1c92c7d193..d3c3585cd0 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -10,7 +10,7 @@ using Avalonia.Media; using Avalonia.OpenGL; using Avalonia.OpenGL.Imaging; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 75b4231640..166f2e8f98 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -1,7 +1,7 @@ using System; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 63a1e8f869..506edf0627 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -4,7 +4,7 @@ using System.Threading; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Skia.Helpers; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index c32c58605f..d9e992bb80 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -9,7 +9,7 @@ using Avalonia.Direct2D1.Media.Imaging; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SharpDX.DirectWrite; using GlyphRun = Avalonia.Media.GlyphRun; using TextAlignment = Avalonia.Media.TextAlignment; diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index b62a6fa5a6..a259d8fab9 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -5,11 +5,11 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SharpDX; using SharpDX.Direct2D1; using SharpDX.Mathematics.Interop; -using BitmapInterpolationMode = Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode; +using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode; namespace Avalonia.Direct2D1.Media { diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 09b900b0c2..296cefad4e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -9,7 +9,7 @@ namespace Avalonia.Direct2D1.Media { private readonly OptionalDispose _bitmap; - private readonly Visuals.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode; + private readonly Avalonia.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode; public ImageBrushImpl( ITileBrush brush, diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index cac35aa4ae..df07f7f39c 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -15,21 +15,21 @@ namespace Avalonia.Direct2D1.Media { private readonly BitmapDecoder _decoder; - private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) { switch (interpolationMode) { - case Visuals.Media.Imaging.BitmapInterpolationMode.Default: + case Avalonia.Media.Imaging.BitmapInterpolationMode.Default: return BitmapInterpolationMode.Fant; - case Visuals.Media.Imaging.BitmapInterpolationMode.LowQuality: + case Avalonia.Media.Imaging.BitmapInterpolationMode.LowQuality: return BitmapInterpolationMode.NearestNeighbor; - case Visuals.Media.Imaging.BitmapInterpolationMode.MediumQuality: + case Avalonia.Media.Imaging.BitmapInterpolationMode.MediumQuality: return BitmapInterpolationMode.Fant; default: - case Visuals.Media.Imaging.BitmapInterpolationMode.HighQuality: + case Avalonia.Media.Imaging.BitmapInterpolationMode.HighQuality: return BitmapInterpolationMode.HighQualityCubic; } @@ -118,7 +118,7 @@ namespace Avalonia.Direct2D1.Media } } - public WicBitmapImpl(Stream stream, int decodeSize, bool horizontal, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + public WicBitmapImpl(Stream stream, int decodeSize, bool horizontal, Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) { _decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad); diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs index fef6a9aa82..885be132a4 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs @@ -9,7 +9,7 @@ namespace Avalonia.Direct2D1.Media.Imaging class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl { public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal, - Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) : base(stream, decodeSize, horizontal, interpolationMode) { } diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index a2d27fd579..1cf68c1605 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -7,15 +7,9 @@ - - - - - - diff --git a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj deleted file mode 100644 index e07ece5460..0000000000 --- a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - net6.0 - Library - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index b57cd55868..0000000000 --- a/tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Animation.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs similarity index 99% rename from tests/Avalonia.Animation.UnitTests/AnimatableTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs index 1d5296bebd..f9ff8caab1 100644 --- a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs @@ -1,5 +1,5 @@ using System; -using Avalonia.Animation.Animators; +using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Data; @@ -10,7 +10,7 @@ using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class AnimatableTests { @@ -136,7 +136,7 @@ namespace Avalonia.Animation.UnitTests KeyTime = TimeSpan.FromSeconds(3), }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(3), Children = diff --git a/tests/Avalonia.Animation.UnitTests/AnimationIterationTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs similarity index 97% rename from tests/Avalonia.Animation.UnitTests/AnimationIterationTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs index 58bd7a42c3..ca24b95e65 100644 --- a/tests/Avalonia.Animation.UnitTests/AnimationIterationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs @@ -12,8 +12,10 @@ using Avalonia.Animation.Easings; using System.Threading; using System.Reactive.Linq; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { + using Animation = Avalonia.Animation.Animation; + public class AnimationIterationTests { [Fact] @@ -37,7 +39,7 @@ namespace Avalonia.Animation.UnitTests KeyTime = TimeSpan.FromSeconds(0) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(1), Children = @@ -86,7 +88,7 @@ namespace Avalonia.Animation.UnitTests Cue = new Cue(0d) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(3), Delay = TimeSpan.FromSeconds(3), @@ -147,7 +149,7 @@ namespace Avalonia.Animation.UnitTests Cue = new Cue(1.0d) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(0.05d), Delay = TimeSpan.FromSeconds(0.05d), diff --git a/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs b/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs similarity index 93% rename from tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs index 1986bd2ee3..acc32fd6a2 100644 --- a/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Media; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class BrushTransitionTests { diff --git a/tests/Avalonia.Animation.UnitTests/KeySplineTests.cs b/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs similarity index 97% rename from tests/Avalonia.Animation.UnitTests/KeySplineTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs index fa2ed61e65..3cd12d92ec 100644 --- a/tests/Avalonia.Animation.UnitTests/KeySplineTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs @@ -1,11 +1,12 @@ using System; +using Avalonia.Animation; using Avalonia.Animation.Easings; using Avalonia.Controls.Shapes; using Avalonia.Media; using Avalonia.Styling; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class KeySplineTests { @@ -123,7 +124,7 @@ namespace Avalonia.Animation.UnitTests 0.499999999999999999) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), Children = @@ -190,7 +191,7 @@ namespace Avalonia.Animation.UnitTests KeyTime = TimeSpan.FromSeconds(5), }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), Children = diff --git a/tests/Avalonia.Animation.UnitTests/TestClock.cs b/tests/Avalonia.Base.UnitTests/Animation/TestClock.cs similarity index 90% rename from tests/Avalonia.Animation.UnitTests/TestClock.cs rename to tests/Avalonia.Base.UnitTests/Animation/TestClock.cs index 4812880c03..6604bd29a4 100644 --- a/tests/Avalonia.Animation.UnitTests/TestClock.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/TestClock.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; +using Avalonia.Animation; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { internal class TestClock : IClock, IDisposable { @@ -33,4 +33,4 @@ namespace Avalonia.Animation.UnitTests return this; } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Animation.UnitTests/TransitionsTests.cs b/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs similarity index 98% rename from tests/Avalonia.Animation.UnitTests/TransitionsTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs index 640013dedd..65cf90b642 100644 --- a/tests/Avalonia.Animation.UnitTests/TransitionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs @@ -1,9 +1,9 @@ using System; +using Avalonia.Animation; using Avalonia.Controls; -using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class TransitionsTests { diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index 9628d16a6c..602e5e4496 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -12,6 +12,11 @@ + + + + + diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs index 7f4db23268..0d62f20f7e 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Reactive.Linq; using Xunit; @@ -56,7 +57,7 @@ namespace Avalonia.Base.UnitTests .Select(x => x.Name) .ToArray(); - Assert.Equal(new[] { "Attached" }, names); + Assert.Contains("Attached", names); } [Fact] @@ -66,7 +67,7 @@ namespace Avalonia.Base.UnitTests .Select(x => x.Name) .ToArray(); - Assert.Equal(new[] { "Attached" }, names); + Assert.Contains("Attached", names); } [Fact] diff --git a/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs b/tests/Avalonia.Base.UnitTests/CornerRadiusTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs rename to tests/Avalonia.Base.UnitTests/CornerRadiusTests.cs index 21633b1cec..edfbd5acd9 100644 --- a/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs +++ b/tests/Avalonia.Base.UnitTests/CornerRadiusTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class CornerRadiusTests { @@ -37,4 +36,4 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new CornerRadius(1.1, 2.2, 3.3, 4.4), result); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs index 1d9524a191..574ecd2ca4 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs @@ -7,7 +7,7 @@ using Avalonia.Data.Core.Plugins; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Markup.UnitTests.Data.Plugins +namespace Avalonia.Base.UnitTests.Data.Core.Plugins { public class DataAnnotationsValidationPluginTests : IClassFixture { diff --git a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs index f522acf9ce..7cf84d767a 100644 --- a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs @@ -7,7 +7,7 @@ using Avalonia.Data.Converters; using Avalonia.Layout; using Xunit; -namespace Avalonia.Base.UnitTests.Data.Converters +namespace Avalonia.Base.UnitTests.Data { public class DefaultValueConverterTests { diff --git a/tests/Avalonia.Input.UnitTests/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/GesturesTests.cs rename to tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index b2687c2555..57ec00cb58 100644 --- a/tests/Avalonia.Input.UnitTests/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class GesturesTests { diff --git a/tests/Avalonia.Input.UnitTests/InputElement_Enabled.cs b/tests/Avalonia.Base.UnitTests/Input/InputElement_Enabled.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/InputElement_Enabled.cs rename to tests/Avalonia.Base.UnitTests/Input/InputElement_Enabled.cs index 11d68d307e..1c001d4b3a 100644 --- a/tests/Avalonia.Input.UnitTests/InputElement_Enabled.cs +++ b/tests/Avalonia.Base.UnitTests/Input/InputElement_Enabled.cs @@ -1,7 +1,7 @@ using Avalonia.Controls; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class InputElement_Enabled { diff --git a/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs similarity index 99% rename from tests/Avalonia.Input.UnitTests/InputElement_Focus.cs rename to tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs index 602455ba36..2d8ee62ef2 100644 --- a/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs +++ b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; +using Avalonia.Input; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class InputElement_Focus { diff --git a/tests/Avalonia.Input.UnitTests/KeyGestureTests.cs b/tests/Avalonia.Base.UnitTests/Input/KeyGestureTests.cs similarity index 97% rename from tests/Avalonia.Input.UnitTests/KeyGestureTests.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyGestureTests.cs index 90f9a0a985..33cd94ec98 100644 --- a/tests/Avalonia.Input.UnitTests/KeyGestureTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyGestureTests.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Avalonia.Input; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyGestureTests { diff --git a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs index 32ba14d337..07bfa0d2e6 100644 --- a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs @@ -1,12 +1,13 @@ using System; using System.Windows.Input; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyboardDeviceTests { diff --git a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Custom.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Custom.cs similarity index 99% rename from tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Custom.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Custom.cs index f9c85ee4ca..830f7223ec 100644 --- a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Custom.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Custom.cs @@ -1,7 +1,8 @@ using Avalonia.Controls; +using Avalonia.Input; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyboardNavigationTests_Custom { diff --git a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs similarity index 99% rename from tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs index 9a117bb71d..7fd8cb345d 100644 --- a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Input; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyboardNavigationTests_Tab { diff --git a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs rename to tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs index 223f458f25..758d501cc1 100644 --- a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs @@ -1,16 +1,16 @@ -using Avalonia.Controls; +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.UnitTests; using Avalonia.VisualTree; using Moq; -using System; -using System.Collections.Generic; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class MouseDeviceTests { diff --git a/tests/Avalonia.Input.UnitTests/PointerTests.cs b/tests/Avalonia.Base.UnitTests/Input/PointerTests.cs similarity index 95% rename from tests/Avalonia.Input.UnitTests/PointerTests.cs rename to tests/Avalonia.Base.UnitTests/Input/PointerTests.cs index e639726dbd..e25ef0a9b8 100644 --- a/tests/Avalonia.Input.UnitTests/PointerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/PointerTests.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class PointerTests { diff --git a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs similarity index 100% rename from tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs rename to tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs diff --git a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs b/tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs similarity index 99% rename from tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs rename to tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs index b997ffbbd9..1ca7e9bd49 100644 --- a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs +++ b/tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using Avalonia.Interactivity; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Interactivity.UnitTests +namespace Avalonia.Base.UnitTests.Interactivity { public class InteractiveTests { diff --git a/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs b/tests/Avalonia.Base.UnitTests/Interactivity/RoutedEventRegistryTests.cs similarity index 95% rename from tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs rename to tests/Avalonia.Base.UnitTests/Interactivity/RoutedEventRegistryTests.cs index 8276039d09..05b2187df4 100644 --- a/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Interactivity/RoutedEventRegistryTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; using Xunit; -namespace Avalonia.Interactivity.UnitTests +namespace Avalonia.Base.UnitTests.Interactivity { public class RoutedEventRegistryTests { diff --git a/tests/Avalonia.Layout.UnitTests/ArrangeTests.cs b/tests/Avalonia.Base.UnitTests/Layout/ArrangeTests.cs similarity index 98% rename from tests/Avalonia.Layout.UnitTests/ArrangeTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/ArrangeTests.cs index db0ead1b00..b771cf2d24 100644 --- a/tests/Avalonia.Layout.UnitTests/ArrangeTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/ArrangeTests.cs @@ -1,7 +1,8 @@ using Avalonia.Controls; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class ArrangeTests { diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs similarity index 98% rename from tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs index dcc29a9716..1d6eead6dd 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs @@ -3,12 +3,12 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Styling; +using Avalonia.Layout; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class FullLayoutTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutHelperTests.cs similarity index 92% rename from tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutHelperTests.cs index f9187edc42..3df31939d1 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutHelperTests.cs @@ -1,7 +1,8 @@ using System; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutHelperTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs index e429adce85..f0e8e1cd11 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutManagerTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutQueueTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutQueueTests.cs similarity index 98% rename from tests/Avalonia.Layout.UnitTests/LayoutQueueTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutQueueTests.cs index f047677a8a..fc3b5c3d43 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutQueueTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutQueueTests.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Linq; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutQueueTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutTestControl.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs similarity index 93% rename from tests/Avalonia.Layout.UnitTests/LayoutTestControl.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs index f7f072eb1e..ca593cc665 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutTestControl.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs @@ -1,7 +1,8 @@ using System; using Avalonia.Controls; +using Avalonia.Layout; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { internal class LayoutTestControl : Decorator { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutTestRoot.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestRoot.cs similarity index 94% rename from tests/Avalonia.Layout.UnitTests/LayoutTestRoot.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutTestRoot.cs index a94459df96..19ab54a339 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutTestRoot.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestRoot.cs @@ -1,7 +1,8 @@ using System; +using Avalonia.Layout; using Avalonia.UnitTests; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { internal class LayoutTestRoot : TestRoot, ILayoutable { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/LayoutableTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs index a8aa0bbf0e..87fa8cf1f3 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs @@ -1,10 +1,11 @@ using System; using Avalonia.Controls; +using Avalonia.Layout; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutableTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs index 178855d71c..7c1a525002 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs @@ -4,11 +4,12 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutableTests_EffectiveViewportChanged { diff --git a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs b/tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/MeasureTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs index 140ebede15..32c3e89b74 100644 --- a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs @@ -1,7 +1,7 @@ using Avalonia.Controls; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class MeasureTests { diff --git a/tests/Avalonia.Layout.UnitTests/NonVirtualizingStackLayoutTests.cs b/tests/Avalonia.Base.UnitTests/Layout/NonVirtualizingStackLayoutTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/NonVirtualizingStackLayoutTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/NonVirtualizingStackLayoutTests.cs index a7b378c322..78cf63ed3f 100644 --- a/tests/Avalonia.Layout.UnitTests/NonVirtualizingStackLayoutTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/NonVirtualizingStackLayoutTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class NonVirtualizingStackLayoutTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/BoxShadowTests.cs b/tests/Avalonia.Base.UnitTests/Media/BoxShadowTests.cs similarity index 96% rename from tests/Avalonia.Visuals.UnitTests/Media/BoxShadowTests.cs rename to tests/Avalonia.Base.UnitTests/Media/BoxShadowTests.cs index e3b703baf2..5f1860d177 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/BoxShadowTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/BoxShadowTests.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class BoxShadowTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/BrushTests.cs index 1a2f7732dd..6edce79de1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs @@ -2,7 +2,7 @@ using System; using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class BrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/ColorTests.cs index 352e58abd1..1392635b32 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs @@ -2,7 +2,7 @@ using System; using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class ColorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs rename to tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs index 8ed5e408fa..1f8ae9bd8b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs @@ -4,7 +4,7 @@ using Avalonia.Media; using Avalonia.Media.Fonts; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class FontFamilyTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs rename to tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs index 2be0209c85..11ecac0039 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs @@ -3,7 +3,7 @@ using Avalonia.Media; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class FontManagerTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs similarity index 91% rename from tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs rename to tests/Avalonia.Base.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs index f2fcd51629..07415ac2d6 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.Fonts; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Fonts +namespace Avalonia.Base.UnitTests.Media.Fonts { public class FamilyNameCollectionTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyKeyTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyKeyTests.cs similarity index 95% rename from tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyKeyTests.cs rename to tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyKeyTests.cs index d99301ea6c..0cb70ea016 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyKeyTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyKeyTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.Fonts; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Fonts +namespace Avalonia.Base.UnitTests.Media.Fonts { public class FontFamilyKeyTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs rename to tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs index 7e1cc4cd96..1bb2300724 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using Avalonia.Media; using Avalonia.Media.Fonts; @@ -6,10 +7,8 @@ using Avalonia.Platform; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Fonts +namespace Avalonia.Base.UnitTests.Media.Fonts { - using System.Diagnostics; - public class FontFamilyLoaderTests : IDisposable { private const string AssetMyFontRegular = AssetLocation + ".MyFont Regular.ttf" + Assembly + FontName; @@ -100,7 +99,7 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts { var assetLoader = AvaloniaLocator.Current.GetService(); - var fontFamily = new FontFamily("resm:Avalonia.Visuals.UnitTests.Assets?assembly=Avalonia.Visuals.UnitTests#Noto Mono"); + var fontFamily = new FontFamily("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests#Noto Mono"); var fontAssets = FontFamilyLoader.LoadFontAssets(fontFamily.Key).ToArray(); diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GeometryGroupTests.cs b/tests/Avalonia.Base.UnitTests/Media/GeometryGroupTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/GeometryGroupTests.cs rename to tests/Avalonia.Base.UnitTests/Media/GeometryGroupTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GeometryTests.cs b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/GeometryTests.cs rename to tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs index 7b1ed7f977..ae8dc9dad2 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/GeometryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class GeometryTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs rename to tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs index ba334fdb74..cd0586233f 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs @@ -5,7 +5,7 @@ using Avalonia.UnitTests; using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class GlyphRunTests : TestWithServicesBase { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/ImageBrushTests.cs similarity index 78% rename from tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/ImageBrushTests.cs index ff7b94105a..2e872e7f7f 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/ImageBrushTests.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Media.Imaging; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class ImageBrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/LinearGradientBrushTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/LinearGradientBrushTests.cs index b3f78dfe8f..736bafba66 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/LinearGradientBrushTests.cs @@ -1,10 +1,9 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Media.Imaging; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class LinearGradientBrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs rename to tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index 6ef48b6161..98b1ff0e28 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -2,7 +2,7 @@ using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class MatrixTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs index ba8c490829..c829690eb4 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs @@ -1,14 +1,13 @@ +using System.Globalization; +using System.IO; using Avalonia.Media; +using Avalonia.Platform; using Avalonia.Visuals.Platform; +using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { - using System.Globalization; - using System.IO; - using Avalonia.Platform; - using Moq; - public class PathMarkupParserTests { [Fact] diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs b/tests/Avalonia.Base.UnitTests/Media/PathSegmentTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PathSegmentTests.cs index 0737b4dc88..41d2fdb349 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PathSegmentTests.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class PathSegmentTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Base.UnitTests/Media/PenTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PenTests.cs index 8c25019606..fecdd30834 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PenTests.cs @@ -3,7 +3,7 @@ using Avalonia.Media; using Avalonia.Media.Immutable; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class PenTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PixelRectTests.cs b/tests/Avalonia.Base.UnitTests/Media/PixelRectTests.cs similarity index 90% rename from tests/Avalonia.Visuals.UnitTests/Media/PixelRectTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PixelRectTests.cs index c6da9b77e9..61c410ca8e 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PixelRectTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PixelRectTests.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Xunit; +using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class PixelRectTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs b/tests/Avalonia.Base.UnitTests/Media/RectTests.cs similarity index 74% rename from tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs rename to tests/Avalonia.Base.UnitTests/Media/RectTests.cs index cd0c7e1ace..96473348cd 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/RectTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; -using Xunit; +using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class RectTests { @@ -13,4 +12,4 @@ namespace Avalonia.Visuals.UnitTests.Media Assert.Equal(expected, rect); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Visuals.UnitTests/Media/RectangleGeometryTests.cs b/tests/Avalonia.Base.UnitTests/Media/RectangleGeometryTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/Media/RectangleGeometryTests.cs rename to tests/Avalonia.Base.UnitTests/Media/RectangleGeometryTests.cs index 5af1bb572e..387b2f4a19 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/RectangleGeometryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/RectangleGeometryTests.cs @@ -1,11 +1,10 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class RectangleGeometryTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/SolidColorBrushTests.cs similarity index 82% rename from tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/SolidColorBrushTests.cs index bcf63f9660..c982d8c1c8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/SolidColorBrushTests.cs @@ -1,8 +1,7 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class SolidColorBrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextDecorationTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextDecorationTests.cs similarity index 95% rename from tests/Avalonia.Visuals.UnitTests/Media/TextDecorationTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextDecorationTests.cs index 60a41baaa7..c4537ea2f8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextDecorationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextDecorationTests.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class TextDecorationTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs index 2f1064be21..f6b01d737c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using Avalonia.Base.UnitTests.Media.TextFormatting; namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs index 4102de10ec..033e0e2242 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using Avalonia.Base.UnitTests.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; namespace Avalonia.Visuals.UnitTests.Media.TextFormatting diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BreakPairTable.txt similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BreakPairTable.txt diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs index c074e5a1f1..7ad2e71d77 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs @@ -5,7 +5,7 @@ using System.Net.Http; using System.Text.RegularExpressions; using Avalonia.Media.TextFormatting.Unicode; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public static class GraphemeBreakClassTrieGenerator { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs similarity index 96% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs index 848b60b341..e1e3e14ea7 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { /// /// This class is intended for use when the Unicode spec changes. Otherwise the containing tests are redundant. diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs index a90be6d519..1dbfbe4d24 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs @@ -8,7 +8,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Xunit; using Xunit.Abstractions; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public class LineBreakEnumeratorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs index 058de909df..33b4c8a269 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs @@ -5,7 +5,7 @@ using System.IO; using System.Linq; using System.Net.Http; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public abstract class TestDataGenerator : IEnumerable { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs index da9e494405..e2877abd83 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs @@ -6,7 +6,7 @@ using System.Text.RegularExpressions; using Avalonia.Media.TextFormatting.Unicode; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { internal static class UnicodeDataGenerator { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs index f122876322..f3aea83316 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public class UnicodeDataGeneratorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs index e483728423..1323ddfbd1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Net.Http; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { internal static class UnicodeEnumsGenerator { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs b/tests/Avalonia.Base.UnitTests/Media/TransformOperationsTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TransformOperationsTests.cs index e4f88706d8..0d84ba1700 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TransformOperationsTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.Transformation; using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class TransformOperationsTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs b/tests/Avalonia.Base.UnitTests/Media/TypefaceTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TypefaceTests.cs index e526172622..826c1f292b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TypefaceTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class TypefaceTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs b/tests/Avalonia.Base.UnitTests/Media/UnicodeRangeSegmentTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs rename to tests/Avalonia.Base.UnitTests/Media/UnicodeRangeSegmentTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs b/tests/Avalonia.Base.UnitTests/Media/UnicodeRangeTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs rename to tests/Avalonia.Base.UnitTests/Media/UnicodeRangeTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/RectTests.cs b/tests/Avalonia.Base.UnitTests/RectTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/RectTests.cs rename to tests/Avalonia.Base.UnitTests/RectTests.cs index a2b0569949..95a438b287 100644 --- a/tests/Avalonia.Visuals.UnitTests/RectTests.cs +++ b/tests/Avalonia.Base.UnitTests/RectTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RectTests { diff --git a/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs b/tests/Avalonia.Base.UnitTests/RelativePointTests.cs similarity index 89% rename from tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs rename to tests/Avalonia.Base.UnitTests/RelativePointTests.cs index c871b8ee87..c242a504b9 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs +++ b/tests/Avalonia.Base.UnitTests/RelativePointTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RelativePointTests { diff --git a/tests/Avalonia.Visuals.UnitTests/RelativeRectComparer.cs b/tests/Avalonia.Base.UnitTests/RelativeRectComparer.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/RelativeRectComparer.cs rename to tests/Avalonia.Base.UnitTests/RelativeRectComparer.cs index bf3bf39367..3e0acddb02 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativeRectComparer.cs +++ b/tests/Avalonia.Base.UnitTests/RelativeRectComparer.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RelativeRectComparer : IEqualityComparer { diff --git a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs b/tests/Avalonia.Base.UnitTests/RelativeRectTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs rename to tests/Avalonia.Base.UnitTests/RelativeRectTests.cs index 9753e0779a..ff53419d5c 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs +++ b/tests/Avalonia.Base.UnitTests/RelativeRectTests.cs @@ -1,8 +1,7 @@ using System; -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RelativeRectTests { diff --git a/tests/Avalonia.Visuals.UnitTests/RenderTests_Culling.cs b/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/RenderTests_Culling.cs rename to tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs index ba632b791a..c7421c5827 100644 --- a/tests/Avalonia.Visuals.UnitTests/RenderTests_Culling.cs +++ b/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs @@ -1,12 +1,12 @@ -using Moq; using Avalonia.Controls; using Avalonia.Media; -using Avalonia.Rendering; -using Xunit; using Avalonia.Platform; +using Avalonia.Rendering; using Avalonia.UnitTests; +using Moq; +using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RenderTests_Culling { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/CustomHitTestBorder.cs b/tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs similarity index 84% rename from tests/Avalonia.Visuals.UnitTests/Rendering/CustomHitTestBorder.cs rename to tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs index 3221f097ad..fece152713 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/CustomHitTestBorder.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs @@ -1,8 +1,7 @@ using Avalonia.Controls; -using Avalonia.Media; using Avalonia.Rendering; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { internal class CustomHitTestBorder : Border, ICustomHitTest { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs index e58eea42d8..c345b9a7ea 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reactive.Subjects; -using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Data; using Avalonia.Media; @@ -11,14 +10,12 @@ using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Threading; using Avalonia.UnitTests; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Avalonia.VisualTree; - using Moq; - using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class DeferredRendererTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs rename to tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs index d24d183709..33c7b3e0f8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs @@ -3,7 +3,6 @@ using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Shapes; -using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; @@ -13,7 +12,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class DeferredRendererTests_HitTesting { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs index 6859b2b139..84bb9b00cd 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class ImmediateRendererTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs rename to tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs index a3b0a0cdd5..1a9822e259 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs @@ -9,7 +9,7 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class ImmediateRendererTests_HitTesting { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/RenderLoopTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/RenderLoopTests.cs index e9aefc46e6..6855a378b4 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/RenderLoopTests.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Avalonia.Rendering; using Avalonia.Threading; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class RenderLoopTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs index 27aa0c55b5..8f307978f2 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs @@ -1,7 +1,5 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Media; -using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Avalonia.Utilities; @@ -9,7 +7,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class DeferredDrawingContextImplTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs similarity index 96% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs index 0cdc8827ad..2fac968206 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs @@ -2,11 +2,11 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class DrawOperationTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/LineNodeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/LineNodeTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/LineNodeTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/LineNodeTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index e317b43efc..01afe85b8b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -1,20 +1,19 @@ -using System; -using System.Linq; +using System.Linq; +using System.Reactive.Subjects; using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; +using Avalonia.Utilities; using Avalonia.VisualTree; -using Xunit; -using Avalonia.Layout; using Moq; -using Avalonia.Platform; -using System.Reactive.Subjects; -using Avalonia.Data; -using Avalonia.Utilities; -using Avalonia.Media.Imaging; +using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public partial class SceneBuilderTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs index 19b4c7e606..9f5a0363ed 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs @@ -1,16 +1,14 @@ -using System; -using System.Linq; +using System.Linq; +using System.Reactive.Subjects; using Avalonia.Controls; +using Avalonia.Data; using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -using Avalonia.Layout; -using System.Reactive.Subjects; -using Avalonia.Data; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public partial class SceneBuilderTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs similarity index 89% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs index fac6e1ceb6..9d25a3aa44 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs @@ -1,12 +1,11 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Controls; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class SceneLayersTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs similarity index 90% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs index bba0c8b8f3..18ff31f676 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs @@ -1,11 +1,10 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Controls; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class SceneTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index 4ec3630053..93266c428c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -1,11 +1,10 @@ -using System; -using Avalonia.Rendering.SceneGraph; +using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class VisualNodeTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs index 8d70b45842..349fd0a6d0 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs @@ -1,10 +1,9 @@ -using System; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Media; using Avalonia.Rendering.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.Utilities +namespace Avalonia.Base.UnitTests.Rendering.Utilities { public class TileBrushCalculatorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/SizeTests.cs b/tests/Avalonia.Base.UnitTests/SizeTests.cs similarity index 92% rename from tests/Avalonia.Visuals.UnitTests/SizeTests.cs rename to tests/Avalonia.Base.UnitTests/SizeTests.cs index 96197785b9..9b8125748a 100644 --- a/tests/Avalonia.Visuals.UnitTests/SizeTests.cs +++ b/tests/Avalonia.Base.UnitTests/SizeTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class SizeTests { diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs index d91d1fab9e..86b1b897d4 100644 --- a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs @@ -3,7 +3,7 @@ using Avalonia.Controls; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class ResourceDictionaryTests { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Child.cs similarity index 97% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Child.cs index d011c16ebb..66efe2cc7b 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Child.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; -using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Child { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Class.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Class.cs index 72383501be..376e2b23d0 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Class.cs @@ -1,14 +1,12 @@ -using System; -using System.Linq; +using System.Collections.Generic; using System.Reactive.Linq; using System.Threading.Tasks; -using Moq; using Avalonia.Controls; using Avalonia.Styling; +using Moq; using Xunit; -using System.Collections.Generic; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Class { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Descendent.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Descendent.cs index aacf2ce223..6b52936ec5 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Descendent.cs @@ -1,11 +1,11 @@ -using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Descendant { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Multiple.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Multiple.cs index 4436f02429..258a4a44a1 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Multiple.cs @@ -1,13 +1,13 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Templates; +using Avalonia.Styling; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Multiple { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Name.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Name.cs similarity index 96% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Name.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Name.cs index 308070c52f..46b97cf384 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Name.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Name.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Name { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Not.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Not.cs similarity index 97% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Not.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Not.cs index 23b711725a..fd1385ac47 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Not.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Not.cs @@ -1,9 +1,9 @@ -using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Not { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthChild.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthChild.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_NthChild.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthChild.cs index e1507be110..7f645be4a9 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthChild.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthChild.cs @@ -1,9 +1,10 @@ using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_NthChild { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthLastChild.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthLastChild.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_NthLastChild.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthLastChild.cs index 00a99523c7..821ed3d0ab 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthLastChild.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthLastChild.cs @@ -1,8 +1,9 @@ using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_NthLastChild { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_OfType.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_OfType.cs similarity index 95% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_OfType.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_OfType.cs index 2aef46575d..e86a323c71 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_OfType.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_OfType.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_OfType { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Or.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Or.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs index 2b55482593..fb5d54bd1f 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Or.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs @@ -1,7 +1,8 @@ using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Or { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_PropertyEquals.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_PropertyEquals.cs index 729b42196b..ee1b654ba8 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_PropertyEquals.cs @@ -1,10 +1,10 @@ -using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_PropertyEquals { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Template.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Template.cs index 13cb2578cf..8b4c988037 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Template.cs @@ -1,14 +1,13 @@ -using System; using System.Linq; -using System.Reactive.Linq; -using Moq; +using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.VisualTree; using Avalonia.Diagnostics; +using Avalonia.Styling; +using Avalonia.VisualTree; +using Moq; using Xunit; -using System.Threading.Tasks; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Template { diff --git a/tests/Avalonia.Styling.UnitTests/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SetterTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs index 901233801e..40d2cab4a3 100644 --- a/tests/Avalonia.Styling.UnitTests/SetterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs @@ -5,10 +5,11 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Converters; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SetterTests { diff --git a/tests/Avalonia.Styling.UnitTests/StyleActivatorExtensions.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs similarity index 97% rename from tests/Avalonia.Styling.UnitTests/StyleActivatorExtensions.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs index 22f4db79d1..f5b3bb40a3 100644 --- a/tests/Avalonia.Styling.UnitTests/StyleActivatorExtensions.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Avalonia.Reactive; using Avalonia.Styling.Activators; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { internal static class StyleActivatorExtensions { diff --git a/tests/Avalonia.Styling.UnitTests/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/StyleTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index 14710f44ba..8dedf3471f 100644 --- a/tests/Avalonia.Styling.UnitTests/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -3,11 +3,12 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Styling; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StyleTests { diff --git a/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/StyledElementTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs index fcbccba695..bb4d590060 100644 --- a/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs @@ -1,19 +1,17 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Reactive.Linq; -using Moq; -using Avalonia.Styling; -using Avalonia.UnitTests; -using Xunit; -using Avalonia.LogicalTree; using Avalonia.Controls; -using System.ComponentModel; -using Avalonia.Controls.Primitives; -using Avalonia.Markup.Xaml.Templates; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.LogicalTree; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Moq; +using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StyledElementTests { diff --git a/tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Resources.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Resources.cs index 05315380fd..69d37d1c54 100644 --- a/tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Resources.cs @@ -1,11 +1,11 @@ -using System; +using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Styling; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Controls.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StyledElementTests_Resources { diff --git a/tests/Avalonia.Styling.UnitTests/StylesTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/StylesTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs index 88ab124e01..a6777c9466 100644 --- a/tests/Avalonia.Styling.UnitTests/StylesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs @@ -1,9 +1,9 @@ -using System; using Avalonia.Controls; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StylesTests { diff --git a/tests/Avalonia.Styling.UnitTests/TestObservable.cs b/tests/Avalonia.Base.UnitTests/Styling/TestObservable.cs similarity index 90% rename from tests/Avalonia.Styling.UnitTests/TestObservable.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestObservable.cs index 7a07f12883..05cf7ef3e1 100644 --- a/tests/Avalonia.Styling.UnitTests/TestObservable.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestObservable.cs @@ -1,7 +1,7 @@ using System; using System.Reactive.Disposables; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class TestObservable : IObservable { diff --git a/tests/Avalonia.Styling.UnitTests/TestObserver.cs b/tests/Avalonia.Base.UnitTests/Styling/TestObserver.cs similarity index 96% rename from tests/Avalonia.Styling.UnitTests/TestObserver.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestObserver.cs index c18fa8618f..46f26804f1 100644 --- a/tests/Avalonia.Styling.UnitTests/TestObserver.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestObserver.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { internal class TestObserver : IObserver { diff --git a/tests/Avalonia.Styling.UnitTests/TestSelectors.cs b/tests/Avalonia.Base.UnitTests/Styling/TestSelectors.cs similarity index 90% rename from tests/Avalonia.Styling.UnitTests/TestSelectors.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestSelectors.cs index 44a087af19..bd60275d50 100644 --- a/tests/Avalonia.Styling.UnitTests/TestSelectors.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestSelectors.cs @@ -1,7 +1,7 @@ using System; using Avalonia.Styling; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public static class TestSelectors { diff --git a/tests/Avalonia.Styling.UnitTests/TestSubject.cs b/tests/Avalonia.Base.UnitTests/Styling/TestSubject.cs similarity index 96% rename from tests/Avalonia.Styling.UnitTests/TestSubject.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestSubject.cs index 7550b545b6..c162f24d59 100644 --- a/tests/Avalonia.Styling.UnitTests/TestSubject.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestSubject.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Reactive.Disposables; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { internal class TestSubject : IObserver, IObservable { diff --git a/tests/Avalonia.Visuals.UnitTests/TestVisual.cs b/tests/Avalonia.Base.UnitTests/TestVisual.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/TestVisual.cs rename to tests/Avalonia.Base.UnitTests/TestVisual.cs index 29d61ddefc..fc9b9e22fb 100644 --- a/tests/Avalonia.Visuals.UnitTests/TestVisual.cs +++ b/tests/Avalonia.Base.UnitTests/TestVisual.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using Avalonia.Rendering; using Avalonia.VisualTree; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class ParamEventArgs : EventArgs { diff --git a/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs b/tests/Avalonia.Base.UnitTests/ThicknessTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs rename to tests/Avalonia.Base.UnitTests/ThicknessTests.cs index e85a9d5b85..094acada26 100644 --- a/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs +++ b/tests/Avalonia.Base.UnitTests/ThicknessTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class ThicknessTests { @@ -37,4 +36,4 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new Thickness(1.2, 3.4, 5, 6), result); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Visuals.UnitTests/Utilities/ReadOnlySpanTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/ReadOnlySpanTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/Utilities/ReadOnlySpanTests.cs rename to tests/Avalonia.Base.UnitTests/Utilities/ReadOnlySpanTests.cs index 1afd84e546..da30ee9d02 100644 --- a/tests/Avalonia.Visuals.UnitTests/Utilities/ReadOnlySpanTests.cs +++ b/tests/Avalonia.Base.UnitTests/Utilities/ReadOnlySpanTests.cs @@ -2,7 +2,7 @@ using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Utilities +namespace Avalonia.Base.UnitTests.Utilities { public class ReadOnlySpanTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VectorTests.cs b/tests/Avalonia.Base.UnitTests/VectorTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VectorTests.cs rename to tests/Avalonia.Base.UnitTests/VectorTests.cs index f9a9e59436..154b4a8e6c 100644 --- a/tests/Avalonia.Visuals.UnitTests/VectorTests.cs +++ b/tests/Avalonia.Base.UnitTests/VectorTests.cs @@ -1,8 +1,7 @@ -using Xunit; -using Avalonia; using System; +using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class VectorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs b/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs rename to tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs index 781169cfa6..4863c0fa61 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs @@ -5,7 +5,7 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class VisualExtensionsTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Base.UnitTests/VisualTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VisualTests.cs rename to tests/Avalonia.Base.UnitTests/VisualTests.cs index 38131fbfca..382001229b 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTests.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class VisualTests { @@ -262,7 +262,7 @@ namespace Avalonia.Visuals.UnitTests LogCallback checkLogMessage = (level, area, src, mt, pv) => { - if (level >= Logging.LogEventLevel.Warning) + if (level >= Avalonia.Logging.LogEventLevel.Warning) { called = true; } @@ -285,7 +285,7 @@ namespace Avalonia.Visuals.UnitTests LogCallback checkLogMessage = (level, area, src, mt, pv) => { - if (level >= Logging.LogEventLevel.Warning) + if (level >= Avalonia.Logging.LogEventLevel.Warning) { called = true; } diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs rename to tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index 6100f2cb74..add8f7fd73 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -4,9 +4,9 @@ using System.IO; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; -namespace Avalonia.Visuals.UnitTests.VisualTree +namespace Avalonia.Base.UnitTests.VisualTree { class MockRenderInterface : IPlatformRenderInterface { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs b/tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs rename to tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs index 54d9ce40f0..d8158fd673 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs @@ -1,18 +1,16 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Controls.Shapes; -using Avalonia.VisualTree; -using Avalonia.Rendering; -using Xunit; using Avalonia.Media; -using Moq; -using Avalonia.UnitTests; using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Moq; +using Xunit; -namespace Avalonia.Visuals.UnitTests.VisualTree +namespace Avalonia.Base.UnitTests.VisualTree { public class TransformedBoundsTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs b/tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs rename to tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs index 139a7925b1..1020a3a4d6 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs @@ -8,7 +8,7 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests.VisualTree +namespace Avalonia.Base.UnitTests.VisualTree { public class VisualExtensions_GetVisualsAt { diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj index 2dbb054221..5d17808e0c 100644 --- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj +++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj @@ -6,15 +6,9 @@ false - - - - - - diff --git a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs index 59067e642f..6fb2a5ac24 100644 --- a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs +++ b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs @@ -2,7 +2,7 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Benchmarks { diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index ccdb0eed2f..5cbb3b2c49 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -4,7 +4,7 @@ using System.IO; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Benchmarks { diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj index 4aed8c60c3..12eb290fde 100644 --- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj +++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj @@ -18,14 +18,8 @@ - - - - - - diff --git a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj index f4c6434a68..f66b2b0457 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj +++ b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj @@ -18,15 +18,9 @@ - - - - - - diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj index cdadc3dcba..e7f1552370 100644 --- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj +++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj @@ -11,14 +11,8 @@ - - - - - - diff --git a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj index 636643d9e4..ad7e3ad504 100644 --- a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj +++ b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj @@ -12,14 +12,8 @@ - - - - - - diff --git a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj b/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj deleted file mode 100644 index 3b0b082093..0000000000 --- a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net6.0 - Library - true - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 3974559efc..0000000000 --- a/tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Input.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj b/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj deleted file mode 100644 index f6cfe51db4..0000000000 --- a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net6.0 - Library - true - latest - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index c5bf2d8657..0000000000 --- a/tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Interactivity.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj deleted file mode 100644 index a3c85c72b5..0000000000 --- a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - net6.0 - Library - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 6f62e38bda..0000000000 --- a/tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Layout.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 7e569f2eac..0c663e1a8f 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -12,14 +12,8 @@ - - - - - - diff --git a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj index fceea56273..fc15ad8230 100644 --- a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj +++ b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj @@ -13,14 +13,8 @@ - - - - - - diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index 14f6525dbe..a0efa7bdeb 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj index 5189e9335a..ffbcdd9e7c 100644 --- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -12,14 +12,8 @@ - - - - - - diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj index e37cd25009..ccde66a50e 100644 --- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj +++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj @@ -13,14 +13,8 @@ - - - - - - diff --git a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj deleted file mode 100644 index c5eb636a9a..0000000000 --- a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - net6.0 - Library - CS0067 - true - latest - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 3bdb8a2f2e..0000000000 --- a/tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Styling.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index c3cf6cd8d3..f54ccaa857 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -17,14 +17,8 @@ - - - - - - diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 2858a9f228..376121c269 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Moq; namespace Avalonia.UnitTests diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj deleted file mode 100644 index 57de90114e..0000000000 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - net6.0 - Library - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 2251cc6e92..0000000000 --- a/tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Visuals.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file From a2bcd8a9c95b2fc854939b8ce1ef8f854dfc0e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 14:39:56 +0200 Subject: [PATCH 406/820] Remove dependency on fluent theme. --- .../Styling/ResourceBenchmarks.cs | 17 ++++++----- tests/Avalonia.Benchmarks/TestStyles.cs | 29 +++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tests/Avalonia.Benchmarks/TestStyles.cs diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index babfaef6ab..2a048beefa 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -3,7 +3,6 @@ using Avalonia.Controls; using Avalonia.Platform; using Avalonia.PlatformSupport; using Avalonia.Styling; -using Avalonia.Themes.Fluent; using Avalonia.UnitTests; using BenchmarkDotNet.Attributes; using Moq; @@ -25,7 +24,7 @@ namespace Avalonia.Benchmarks.Styling renderInterface: new MockPlatformRenderInterface(), standardCursorFactory: Mock.Of(), styler: new Styler(), - theme: () => LoadTheme(), + theme: () => CreateTheme(), threadingInterface: new NullThreadingPlatform(), fontManagerImpl: new MockFontManagerImpl(), textShaperImpl: new MockTextShaperImpl(), @@ -34,7 +33,7 @@ namespace Avalonia.Benchmarks.Styling return UnitTestApplication.Start(services); } - private static Styles LoadTheme() + private static Styles CreateTheme() { AssetLoader.RegisterResUriParsers(); @@ -43,11 +42,11 @@ namespace Avalonia.Benchmarks.Styling var postHost = new Style(); postHost.Resources.Add("postTheme", null); - + return new Styles { preHost, - new FluentTheme(new Uri("avares://Avalonia.Benchmarks")), + new TestStyles(50, 3, 5), postHost }; } @@ -81,11 +80,13 @@ namespace Avalonia.Benchmarks.Styling current.Child = _searchStart; } + + private const int LookupCount = 100; [Benchmark] public void FindPreResource() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < LookupCount; ++i) { _searchStart.FindResource("preTheme"); } @@ -94,7 +95,7 @@ namespace Avalonia.Benchmarks.Styling [Benchmark] public void FindPostResource() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < LookupCount; ++i) { _searchStart.FindResource("postTheme"); } @@ -103,7 +104,7 @@ namespace Avalonia.Benchmarks.Styling [Benchmark] public void FindNotExistingResource() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < LookupCount; ++i) { _searchStart.FindResource("notPresent"); } diff --git a/tests/Avalonia.Benchmarks/TestStyles.cs b/tests/Avalonia.Benchmarks/TestStyles.cs new file mode 100644 index 0000000000..be2ad7d072 --- /dev/null +++ b/tests/Avalonia.Benchmarks/TestStyles.cs @@ -0,0 +1,29 @@ +using Avalonia.Styling; + +namespace Avalonia.Benchmarks +{ + public class TestStyles : Styles + { + public TestStyles(int childStylesCount, int childInnerStyleCount, int childResourceCount) + { + for (int i = 0; i < childStylesCount; i++) + { + var childStyles = new Styles(); + + for (int j = 0; j < childInnerStyleCount; j++) + { + var childStyle = new Style(); + + for (int k = 0; k < childResourceCount; k++) + { + childStyle.Resources.Add($"resource.{i}.{j}.{k}", null); + } + + childStyles.Add(childStyle); + } + + Add(childStyles); + } + } + } +} From 0ec26bcdc3e9b9f841993242ca662f2ea194aa4e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:40:38 -0400 Subject: [PATCH 407/820] Make empty ChildIndexChangedEventArgs ctor private --- src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs index afc6c1f5fc..497596fcc1 100644 --- a/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs +++ b/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs @@ -10,7 +10,7 @@ namespace Avalonia.LogicalTree { public static new ChildIndexChangedEventArgs Empty { get; } = new ChildIndexChangedEventArgs(); - public ChildIndexChangedEventArgs() + private ChildIndexChangedEventArgs() { } From 9bef2acafe18551bd0d717c316864677e6400814 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:25 -0400 Subject: [PATCH 408/820] Move pointerover logic out of input device layer --- src/Avalonia.Controls/TopLevel.cs | 25 +- src/Avalonia.Input/IInputRoot.cs | 3 - src/Avalonia.Input/IKeyboardDevice.cs | 6 - src/Avalonia.Input/IMouseDevice.cs | 2 + src/Avalonia.Input/IPointerDevice.cs | 16 +- src/Avalonia.Input/KeyboardDevice.cs | 2 +- src/Avalonia.Input/MouseDevice.cs | 328 +++--------------- src/Avalonia.Input/PointerEventArgs.cs | 2 + src/Avalonia.Input/PointerOverPreProcessor.cs | 210 +++++++++++ src/Avalonia.Input/Raw/RawDragEvent.cs | 2 +- src/Avalonia.Input/Raw/RawInputHelpers.cs | 27 ++ src/Avalonia.Input/Raw/RawPointerEventArgs.cs | 2 + src/Avalonia.Input/TouchDevice.cs | 47 ++- 13 files changed, 358 insertions(+), 314 deletions(-) create mode 100644 src/Avalonia.Input/PointerOverPreProcessor.cs create mode 100644 src/Avalonia.Input/Raw/RawInputHelpers.cs diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 55202dd20d..75a34659a2 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -15,7 +15,6 @@ using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Utilities; using Avalonia.VisualTree; -using JetBrains.Annotations; namespace Avalonia.Controls { @@ -87,6 +86,8 @@ namespace Avalonia.Controls private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler; private readonly IPlatformRenderInterface? _renderInterface; private readonly IGlobalStyles? _globalStyles; + private readonly PointerOverPreProcessor? _pointerOverPreProcessor; + private readonly IDisposable? _pointerOverPreProcessorSubscription; private Size _clientSize; private Size? _frameSize; private WindowTransparencyLevel _actualTransparencyLevel; @@ -195,6 +196,9 @@ namespace Avalonia.Controls } impl.LostFocus += PlatformImpl_LostFocus; + + _pointerOverPreProcessor = new PointerOverPreProcessor(this); + _pointerOverPreProcessorSubscription = _inputManager?.PreProcess.Subscribe(_pointerOverPreProcessor); } /// @@ -283,9 +287,7 @@ namespace Avalonia.Controls /// IKeyboardNavigationHandler IInputRoot.KeyboardNavigationHandler => _keyboardNavigationHandler!; - /// - /// Gets or sets the input element that the pointer is currently over. - /// + /// IInputElement? IInputRoot.PointerOverElement { get { return GetValue(PointerOverElementProperty); } @@ -378,10 +380,12 @@ namespace Avalonia.Controls Renderer?.Dispose(); Renderer = null!; - - (this as IInputRoot).MouseDevice?.TopLevelClosed(this); + + _pointerOverPreProcessor?.OnCompleted(); + _pointerOverPreProcessorSubscription?.Dispose(); + PlatformImpl = null; - + var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null); ((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs); @@ -515,12 +519,17 @@ namespace Avalonia.Controls /// The event args. private void HandleInput(RawInputEventArgs e) { + if (e is RawPointerEventArgs pointerArgs) + { + pointerArgs.InputHitTestResult = this.InputHitTest(pointerArgs.Position); + } + _inputManager?.ProcessInput(e); } private void SceneInvalidated(object? sender, SceneInvalidatedEventArgs e) { - (this as IInputRoot).MouseDevice?.SceneInvalidated(this, e.DirtyRect); + _pointerOverPreProcessor?.SceneInvalidated(e.DirtyRect); } void PlatformImpl_LostFocus() diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Input/IInputRoot.cs index 3e2b8cc477..98e8699573 100644 --- a/src/Avalonia.Input/IInputRoot.cs +++ b/src/Avalonia.Input/IInputRoot.cs @@ -1,5 +1,3 @@ -using JetBrains.Annotations; - namespace Avalonia.Input { /// @@ -30,7 +28,6 @@ namespace Avalonia.Input /// /// Gets associated mouse device /// - [CanBeNull] IMouseDevice? MouseDevice { get; } } } diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs index 9506dc36fb..d0e84e5ad0 100644 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ b/src/Avalonia.Input/IKeyboardDevice.cs @@ -50,12 +50,6 @@ namespace Avalonia.Input KeyboardMask = Alt | Control | Shift | Meta } - internal static class KeyModifiersUtils - { - public static KeyModifiers ConvertToKey(RawInputModifiers modifiers) => - (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); - } - public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement? FocusedElement { get; } diff --git a/src/Avalonia.Input/IMouseDevice.cs b/src/Avalonia.Input/IMouseDevice.cs index 272d1eb8d7..6b7f0e76e5 100644 --- a/src/Avalonia.Input/IMouseDevice.cs +++ b/src/Avalonia.Input/IMouseDevice.cs @@ -13,8 +13,10 @@ namespace Avalonia.Input [Obsolete("Use PointerEventArgs.GetPosition")] PixelPoint Position { get; } + [Obsolete] void TopLevelClosed(IInputRoot root); + [Obsolete] void SceneInvalidated(IInputRoot root, Rect rect); } } diff --git a/src/Avalonia.Input/IPointerDevice.cs b/src/Avalonia.Input/IPointerDevice.cs index 1f82cb1ed7..0096bb77bf 100644 --- a/src/Avalonia.Input/IPointerDevice.cs +++ b/src/Avalonia.Input/IPointerDevice.cs @@ -1,17 +1,31 @@ using System; using Avalonia.VisualTree; +using Avalonia.Input.Raw; namespace Avalonia.Input { public interface IPointerDevice : IInputDevice { + /// [Obsolete("Use IPointer")] IInputElement? Captured { get; } - + + /// [Obsolete("Use IPointer")] void Capture(IInputElement? control); + /// [Obsolete("Use PointerEventArgs.GetPosition")] Point GetPosition(IVisual relativeTo); + + /// + /// Gets a pointer for specific event args. + /// + /// + /// If pointer doesn't exist or wasn't yet created this method will return null. + /// + /// Raw pointer event args associated with the pointer. + /// The pointer. + IPointer? TryGetPointer(RawPointerEventArgs ev); } } diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs index 3df717b8c4..0600b54618 100644 --- a/src/Avalonia.Input/KeyboardDevice.cs +++ b/src/Avalonia.Input/KeyboardDevice.cs @@ -188,7 +188,7 @@ namespace Avalonia.Input RoutedEvent = routedEvent, Device = this, Key = keyInput.Key, - KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers), + KeyModifiers = keyInput.Modifiers.ToKeyModifiers(), Source = element, }; diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index a5d54bb047..5f8ab24b79 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -21,27 +21,17 @@ namespace Avalonia.Input private readonly Pointer _pointer; private bool _disposed; - private PixelPoint? _position; + private PixelPoint? _position; + private MouseButton _lastMouseDownButton; public MouseDevice(Pointer? pointer = null) { _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); } - - /// - /// Gets the control that is currently capturing by the mouse, if any. - /// - /// - /// When an element captures the mouse, it receives mouse input whether the cursor is - /// within the control's bounds or not. To set the mouse capture, call the - /// method. - /// + [Obsolete("Use IPointer instead")] public IInputElement? Captured => _pointer.Captured; - /// - /// Gets the mouse position, in screen coordinates. - /// [Obsolete("Use events instead")] public PixelPoint Position { @@ -49,15 +39,7 @@ namespace Avalonia.Input protected set => _position = value; } - /// - /// Captures mouse input to the specified control. - /// - /// The control. - /// - /// When an element captures the mouse, it receives mouse input whether the cursor is - /// within the control's bounds or not. The current mouse capture control is exposed - /// by the property. - /// + [Obsolete("Use IPointer instead")] public void Capture(IInputElement? control) { _pointer.Capture(control); @@ -90,39 +72,6 @@ namespace Avalonia.Input ProcessRawEvent(margs); } - public void TopLevelClosed(IInputRoot root) - { - ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); - } - - public void SceneInvalidated(IInputRoot root, Rect rect) - { - // Pointer is outside of the target area - if (_position == null ) - { - if (root.PointerOverElement != null) - ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); - return; - } - - - var clientPoint = root.PointToClient(_position.Value); - - if (rect.Contains(clientPoint)) - { - if (_pointer.Captured == null) - { - SetPointerOver(this, 0 /* TODO: proper timestamp */, root, clientPoint, - PointerPointProperties.None, KeyModifiers.None); - } - else - { - SetPointerOver(this, 0 /* TODO: proper timestamp */, root, _pointer.Captured, - PointerPointProperties.None, KeyModifiers.None); - } - } - } - int ButtonCount(PointerPointProperties props) { var rv = 0; @@ -138,7 +87,7 @@ namespace Avalonia.Input rv++; return rv; } - + private void ProcessRawEvent(RawPointerEventArgs e) { e = e ?? throw new ArgumentNullException(nameof(e)); @@ -147,15 +96,14 @@ namespace Avalonia.Input if(mouse._disposed) return; - if (e.Type == RawPointerEventType.NonClientLeftButtonDown) return; - _position = e.Root.PointToScreen(e.Position); var props = CreateProperties(e); - var keyModifiers = KeyModifiersUtils.ConvertToKey(e.InputModifiers); + var keyModifiers = e.InputModifiers.ToKeyModifiers(); switch (e.Type) { case RawPointerEventType.LeaveWindow: - LeaveWindow(mouse, e.Timestamp, e.Root, props, keyModifiers); + case RawPointerEventType.NonClientLeftButtonDown: + LeaveWindow(); break; case RawPointerEventType.LeftButtonDown: case RawPointerEventType.RightButtonDown: @@ -163,10 +111,9 @@ namespace Avalonia.Input case RawPointerEventType.XButton1Down: case RawPointerEventType.XButton2Down: if (ButtonCount(props) > 1) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); + e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else - e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, - props, keyModifiers); + e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.LeftButtonUp: case RawPointerEventType.RightButtonUp: @@ -174,82 +121,50 @@ namespace Avalonia.Input case RawPointerEventType.XButton1Up: case RawPointerEventType.XButton2Up: if (ButtonCount(props) != 0) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); + e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else - e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); + e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Move: - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); + e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); break; case RawPointerEventType.Wheel: - e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers); + e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Magnify: - e.Handled = GestureMagnify(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + e.Handled = GestureMagnify(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Rotate: - e.Handled = GestureRotate(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + e.Handled = GestureRotate(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Swipe: - e.Handled = GestureSwipe(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + e.Handled = GestureSwipe(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; } } - private void LeaveWindow(IMouseDevice device, ulong timestamp, IInputRoot root, PointerPointProperties properties, - KeyModifiers inputModifiers) + private void LeaveWindow() { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - _position = null; - ClearPointerOver(this, timestamp, root, properties, inputModifiers); } - PointerPointProperties CreateProperties(RawPointerEventArgs args) { - - var kind = PointerUpdateKind.Other; - - if (args.Type == RawPointerEventType.LeftButtonDown) - kind = PointerUpdateKind.LeftButtonPressed; - if (args.Type == RawPointerEventType.MiddleButtonDown) - kind = PointerUpdateKind.MiddleButtonPressed; - if (args.Type == RawPointerEventType.RightButtonDown) - kind = PointerUpdateKind.RightButtonPressed; - if (args.Type == RawPointerEventType.XButton1Down) - kind = PointerUpdateKind.XButton1Pressed; - if (args.Type == RawPointerEventType.XButton2Down) - kind = PointerUpdateKind.XButton2Pressed; - if (args.Type == RawPointerEventType.LeftButtonUp) - kind = PointerUpdateKind.LeftButtonReleased; - if (args.Type == RawPointerEventType.MiddleButtonUp) - kind = PointerUpdateKind.MiddleButtonReleased; - if (args.Type == RawPointerEventType.RightButtonUp) - kind = PointerUpdateKind.RightButtonReleased; - if (args.Type == RawPointerEventType.XButton1Up) - kind = PointerUpdateKind.XButton1Released; - if (args.Type == RawPointerEventType.XButton2Up) - kind = PointerUpdateKind.XButton2Released; - - return new PointerPointProperties(args.InputModifiers, kind); + return new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()); } - private MouseButton _lastMouseDownButton; private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, PointerPointProperties properties, - KeyModifiers inputModifiers) + KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? root.InputHitTest(p); - if (hit != null) + if (source != null) { - _pointer.Capture(hit); - var source = GetSource(hit); + _pointer.Capture(source); if (source != null) { var settings = AvaloniaLocator.Current.GetService(); @@ -275,23 +190,14 @@ namespace Avalonia.Input return false; } - private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, - KeyModifiers inputModifiers, Lazy?>? intermediatePoints) + private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, + PointerPointProperties properties, KeyModifiers inputModifiers, Lazy?>? intermediatePoints, + IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - IInputElement? source; - - if (_pointer.Captured == null) - { - source = SetPointerOver(this, timestamp, root, p, properties, inputModifiers); - } - else - { - SetPointerOver(this, timestamp, root, _pointer.Captured, properties, inputModifiers); - source = _pointer.Captured; - } + var source = _pointer.Captured ?? hitTest; if (source is object) { @@ -306,13 +212,12 @@ namespace Avalonia.Input } private bool MouseUp(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, - KeyModifiers inputModifiers) + KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); - var source = GetSource(hit); + var source = _pointer.Captured ?? hitTest; if (source is not null) { @@ -329,13 +234,12 @@ namespace Avalonia.Input private bool MouseWheel(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, - Vector delta, KeyModifiers inputModifiers) + Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); - var source = GetSource(hit); + var source = _pointer.Captured ?? hitTest; // KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform. // If Shift-Key is pressed and X is close to 0 we swap the Vector. @@ -356,16 +260,15 @@ namespace Avalonia.Input } private bool GestureMagnify(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? hitTest; - if (hit != null) + if (source != null) { - var source = GetSource(hit); var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureMagnifyEvent, source, _pointer, root, p, timestamp, props, inputModifiers, delta); @@ -377,16 +280,15 @@ namespace Avalonia.Input } private bool GestureRotate(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? hitTest; - if (hit != null) + if (source != null) { - var source = GetSource(hit); var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureRotateEvent, source, _pointer, root, p, timestamp, props, inputModifiers, delta); @@ -398,16 +300,15 @@ namespace Avalonia.Input } private bool GestureSwipe(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? hitTest; - if (hit != null) + if (source != null) { - var source = GetSource(hit); var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureSwipeEvent, source, _pointer, root, p, timestamp, props, inputModifiers, delta); @@ -418,154 +319,27 @@ namespace Avalonia.Input return false; } - private IInteractive? GetSource(IVisual? hit) - { - if (hit is null) - return null; - - return _pointer.Captured ?? - (hit as IInteractive) ?? - hit.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); - } - - private IInputElement? HitTest(IInputElement root, Point p) - { - root = root ?? throw new ArgumentNullException(nameof(root)); - - return _pointer.Captured ?? root.InputHitTest(p); - } - - PointerEventArgs CreateSimpleEvent(RoutedEvent ev, ulong timestamp, IInteractive? source, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - return new PointerEventArgs(ev, source, _pointer, null, default, - timestamp, properties, inputModifiers); - } - - private void ClearPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var element = root.PointerOverElement; - var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, element, properties, inputModifiers); - - if (element!=null && !element.IsAttachedToVisualTree) - { - // element has been removed from visual tree so do top down cleanup - if (root.IsPointerOver) - ClearChildrenPointerOver(e, root,true); - } - while (element != null) - { - e.Source = element; - e.Handled = false; - element.RaiseEvent(e); - element = (IInputElement?)element.VisualParent; - } - - root.PointerOverElement = null; - } - - private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element,bool clearRoot) + public void Dispose() { - foreach (IInputElement el in element.VisualChildren) - { - if (el.IsPointerOver) - { - ClearChildrenPointerOver(e, el, true); - break; - } - } - if(clearRoot) - { - e.Source = element; - e.Handled = false; - element.RaiseEvent(e); - } + _disposed = true; + _pointer?.Dispose(); } - private IInputElement? SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties properties, - KeyModifiers inputModifiers) + [Obsolete] + public void TopLevelClosed(IInputRoot root) { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var element = root.InputHitTest(p); - - if (element != root.PointerOverElement) - { - if (element != null) - { - SetPointerOver(device, timestamp, root, element, properties, inputModifiers); - } - else - { - ClearPointerOver(device, timestamp, root, properties, inputModifiers); - } - } - - return element; + // no-op } - private void SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, IInputElement element, - PointerPointProperties properties, - KeyModifiers inputModifiers) + [Obsolete] + public void SceneInvalidated(IInputRoot root, Rect rect) { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - element = element ?? throw new ArgumentNullException(nameof(element)); - - IInputElement? branch = null; - - IInputElement? el = element; - - while (el != null) - { - if (el.IsPointerOver) - { - branch = el; - break; - } - el = (IInputElement?)el.VisualParent; - } - - el = root.PointerOverElement; - - var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, el, properties, inputModifiers); - if (el!=null && branch!=null && !el.IsAttachedToVisualTree) - { - ClearChildrenPointerOver(e,branch,false); - } - - while (el != null && el != branch) - { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement?)el.VisualParent; - } - - el = root.PointerOverElement = element; - e.RoutedEvent = InputElement.PointerEnterEvent; - - while (el != null && el != branch) - { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement?)el.VisualParent; - } + // no-op } - public void Dispose() + public IPointer? TryGetPointer(RawPointerEventArgs ev) { - _disposed = true; - _pointer?.Dispose(); + return _pointer; } } } diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs index 0604d09dc4..5495802920 100644 --- a/src/Avalonia.Input/PointerEventArgs.cs +++ b/src/Avalonia.Input/PointerEventArgs.cs @@ -63,6 +63,8 @@ namespace Avalonia.Input } public Point GetPosition(IVisual relativeTo) => _ev.GetPosition(relativeTo); + + public IPointer? TryGetPointer(RawPointerEventArgs ev) => _ev.Pointer; } public IPointer Pointer { get; } diff --git a/src/Avalonia.Input/PointerOverPreProcessor.cs b/src/Avalonia.Input/PointerOverPreProcessor.cs new file mode 100644 index 0000000000..a38364197d --- /dev/null +++ b/src/Avalonia.Input/PointerOverPreProcessor.cs @@ -0,0 +1,210 @@ +using System; +using Avalonia.Input.Raw; + +namespace Avalonia.Input +{ + internal class PointerOverPreProcessor : IObserver + { + private IPointerDevice? _lastActivePointerDevice; + private (IPointer pointer, PixelPoint position)? _lastPointer; + + private readonly IInputRoot _inputRoot; + + public PointerOverPreProcessor(IInputRoot inputRoot) + { + _inputRoot = inputRoot ?? throw new ArgumentNullException(nameof(inputRoot)); + } + + public void OnCompleted() + { + ClearPointerOver(); + } + + public void OnError(Exception error) + { + } + + public void OnNext(RawInputEventArgs value) + { + if (value is RawPointerEventArgs args + && args.Root == _inputRoot + && value.Device is IPointerDevice pointerDevice) + { + if (pointerDevice != _lastActivePointerDevice) + { + ClearPointerOver(); + + // Set last active device before processing input, because ClearPointerOver might be called and clear last device. + _lastActivePointerDevice = pointerDevice; + } + + if (args.Type is RawPointerEventType.LeaveWindow or RawPointerEventType.NonClientLeftButtonDown + && _lastPointer is (var lastPointer, var lastPosition)) + { + _lastPointer = null; + ClearPointerOver(lastPointer, args.Root, 0, args.Root.PointToClient(lastPosition), + new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()), + args.InputModifiers.ToKeyModifiers()); + } + else if (pointerDevice.TryGetPointer(args) is IPointer pointer + && pointer.Type != PointerType.Touch) + { + var element = pointer.Captured ?? args.InputHitTestResult; + + SetPointerOver(pointer, args.Root, element, args.Timestamp, args.Position, + new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()), + args.InputModifiers.ToKeyModifiers()); + } + } + } + + public void SceneInvalidated(Rect dirtyRect) + { + // Pointer is outside of the target area + if (_lastPointer is (var pointer, var position)) + { + var clientPoint = _inputRoot.PointToClient(position); + + if (dirtyRect.Contains(clientPoint)) + { + SetPointerOver(pointer, _inputRoot, _inputRoot.InputHitTest(clientPoint), 0, clientPoint, PointerPointProperties.None, KeyModifiers.None); + } + else if (!_inputRoot.Bounds.Contains(clientPoint)) + { + ClearPointerOver(pointer, _inputRoot, 0, new Point(-1, -1), PointerPointProperties.None, KeyModifiers.None); + } + } + } + + private void ClearPointerOver() + { + if (_lastPointer is (var pointer, var _)) + { + ClearPointerOver(pointer, _inputRoot, 0, new Point(-1, -1), PointerPointProperties.None, KeyModifiers.None); + } + _lastPointer = null; + _lastActivePointerDevice = null; + } + + private void ClearPointerOver(IPointer pointer, IInputRoot root, + ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers) + { + var element = root.PointerOverElement; + if (element is null) + { + return; + } + + // Do not pass rootVisual, when we have unknown (negative) position, + // so GetPosition won't return invalid values. + var hasPosition = position.X >= 0 && position.Y >= 0; + var e = new PointerEventArgs(InputElement.PointerLeaveEvent, element, pointer, + hasPosition ? root : null, hasPosition ? position : default, + timestamp, properties, inputModifiers); + + if (element != null && !element.IsAttachedToVisualTree) + { + // element has been removed from visual tree so do top down cleanup + if (root.IsPointerOver) + { + ClearChildrenPointerOver(e, root, true); + } + } + while (element != null) + { + e.Source = element; + e.Handled = false; + element.RaiseEvent(e); + element = (IInputElement?)element.VisualParent; + } + + root.PointerOverElement = null; + _lastActivePointerDevice = null; + _lastPointer = null; + } + + private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element, bool clearRoot) + { + foreach (IInputElement el in element.VisualChildren) + { + if (el.IsPointerOver) + { + ClearChildrenPointerOver(e, el, true); + break; + } + } + if (clearRoot) + { + e.Source = element; + e.Handled = false; + element.RaiseEvent(e); + } + } + + private void SetPointerOver(IPointer pointer, IInputRoot root, IInputElement? element, + ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers) + { + var pointerOverElement = root.PointerOverElement; + + if (element != pointerOverElement) + { + if (element != null) + { + SetPointerOverToElement(pointer, root, element, timestamp, position, properties, inputModifiers); + } + else + { + ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers); + } + } + } + + private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element, + ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers) + { + IInputElement? branch = null; + + IInputElement? el = element; + + while (el != null) + { + if (el.IsPointerOver) + { + branch = el; + break; + } + el = (IInputElement?)el.VisualParent; + } + + el = root.PointerOverElement; + + var e = new PointerEventArgs(InputElement.PointerLeaveEvent, el, pointer, root, position, + timestamp, properties, inputModifiers); + if (el != null && branch != null && !el.IsAttachedToVisualTree) + { + ClearChildrenPointerOver(e, branch, false); + } + + while (el != null && el != branch) + { + e.Source = el; + e.Handled = false; + el.RaiseEvent(e); + el = (IInputElement?)el.VisualParent; + } + + el = root.PointerOverElement = element; + _lastPointer = (pointer, root.PointToScreen(position)); + + e.RoutedEvent = InputElement.PointerEnterEvent; + + while (el != null && el != branch) + { + e.Source = el; + e.Handled = false; + el.RaiseEvent(e); + el = (IInputElement?)el.VisualParent; + } + } + } +} diff --git a/src/Avalonia.Input/Raw/RawDragEvent.cs b/src/Avalonia.Input/Raw/RawDragEvent.cs index 6e9ce20ff1..652bad7115 100644 --- a/src/Avalonia.Input/Raw/RawDragEvent.cs +++ b/src/Avalonia.Input/Raw/RawDragEvent.cs @@ -20,7 +20,7 @@ namespace Avalonia.Input.Raw Location = location; Data = data; Effects = effects; - KeyModifiers = KeyModifiersUtils.ConvertToKey(modifiers); + KeyModifiers = modifiers.ToKeyModifiers(); #pragma warning disable CS0618 // Type or member is obsolete Modifiers = (InputModifiers)modifiers; #pragma warning restore CS0618 // Type or member is obsolete diff --git a/src/Avalonia.Input/Raw/RawInputHelpers.cs b/src/Avalonia.Input/Raw/RawInputHelpers.cs new file mode 100644 index 0000000000..9d329bae59 --- /dev/null +++ b/src/Avalonia.Input/Raw/RawInputHelpers.cs @@ -0,0 +1,27 @@ +using Avalonia.Input.Raw; + +namespace Avalonia.Input +{ + internal static class RawInputHelpers + { + public static KeyModifiers ToKeyModifiers(this RawInputModifiers modifiers) => + (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); + + public static PointerUpdateKind ToUpdateKind(this RawPointerEventType type) => type switch + { + RawPointerEventType.LeftButtonDown => PointerUpdateKind.LeftButtonPressed, + RawPointerEventType.LeftButtonUp => PointerUpdateKind.LeftButtonReleased, + RawPointerEventType.RightButtonDown => PointerUpdateKind.RightButtonPressed, + RawPointerEventType.RightButtonUp => PointerUpdateKind.RightButtonReleased, + RawPointerEventType.MiddleButtonDown => PointerUpdateKind.MiddleButtonPressed, + RawPointerEventType.MiddleButtonUp => PointerUpdateKind.MiddleButtonReleased, + RawPointerEventType.XButton1Down => PointerUpdateKind.XButton1Pressed, + RawPointerEventType.XButton1Up => PointerUpdateKind.XButton1Released, + RawPointerEventType.XButton2Down => PointerUpdateKind.XButton2Pressed, + RawPointerEventType.XButton2Up => PointerUpdateKind.XButton2Released, + RawPointerEventType.TouchBegin => PointerUpdateKind.LeftButtonPressed, + RawPointerEventType.TouchEnd => PointerUpdateKind.LeftButtonReleased, + _ => PointerUpdateKind.Other + }; + } +} diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index c157fa059c..8b9d7c161d 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -120,6 +120,8 @@ namespace Avalonia.Input.Raw /// only valid for Move and TouchUpdate /// public Lazy?>? IntermediatePoints { get; set; } + + internal IInputElement? InputHitTestResult { get; set; } } public struct RawPointerPoint diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs index 20cafb9e8e..54dcc4051e 100644 --- a/src/Avalonia.Input/TouchDevice.cs +++ b/src/Avalonia.Input/TouchDevice.cs @@ -3,24 +3,26 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Input.Raw; using Avalonia.Platform; +using Avalonia.VisualTree; namespace Avalonia.Input { /// /// Handles raw touch events + /// /// /// This class is supposed to be used on per-toplevel basis, don't use a shared one /// - /// - public class TouchDevice : IInputDevice, IDisposable + public class TouchDevice : IPointerDevice, IDisposable { private readonly Dictionary _pointers = new Dictionary(); private bool _disposed; private int _clickCount; private Rect _lastClickRect; private ulong _lastClickTime; - KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) => - (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); + private Pointer? _lastPointer; + + IInputElement? IPointerDevice.Captured => _lastPointer?.Captured; RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) { @@ -30,6 +32,10 @@ namespace Avalonia.Input return rv; } + void IPointerDevice.Capture(IInputElement? control) => _lastPointer?.Capture(control); + + Point IPointerDevice.GetPosition(IVisual relativeTo) => default; + public void ProcessRawEvent(RawInputEventArgs ev) { if (ev.Handled || _disposed) @@ -39,15 +45,18 @@ namespace Avalonia.Input { if (args.Type == RawPointerEventType.TouchEnd) return; - var hit = args.Root.InputHitTest(args.Position); + var hit = args.InputHitTestResult; _pointers[args.TouchPointId] = pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Touch, _pointers.Count == 0); pointer.Capture(hit); } - + _lastPointer = pointer; var target = pointer.Captured ?? args.Root; + var updateKind = args.Type.ToUpdateKind(); + var keyModifier = args.InputModifiers.ToKeyModifiers(); + if (args.Type == RawPointerEventType.TouchBegin) { if (_pointers.Count > 1) @@ -73,9 +82,8 @@ namespace Avalonia.Input target.RaiseEvent(new PointerPressedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), - PointerUpdateKind.LeftButtonPressed), - GetKeyModifiers(args.InputModifiers), _clickCount)); + new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), + keyModifier, _clickCount)); } if (args.Type == RawPointerEventType.TouchEnd) @@ -85,10 +93,10 @@ namespace Avalonia.Input { target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, false), - PointerUpdateKind.LeftButtonReleased), - GetKeyModifiers(args.InputModifiers), MouseButton.Left)); + new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), + keyModifier, MouseButton.Left)); } + _lastPointer = null; } if (args.Type == RawPointerEventType.TouchCancel) @@ -96,18 +104,16 @@ namespace Avalonia.Input _pointers.Remove(args.TouchPointId); using (pointer) pointer.Capture(null); + _lastPointer = null; } if (args.Type == RawPointerEventType.TouchUpdate) { - var modifiers = GetModifiers(args.InputModifiers, pointer.IsPrimary); target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), PointerUpdateKind.Other), - GetKeyModifiers(args.InputModifiers), args.IntermediatePoints)); + new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), + keyModifier, args.IntermediatePoints)); } - - } public void Dispose() @@ -121,5 +127,12 @@ namespace Avalonia.Input p.Dispose(); } + public IPointer? TryGetPointer(RawPointerEventArgs ev) + { + return ev is RawTouchEventArgs args + && _pointers.TryGetValue(args.TouchPointId, out var pointer) + ? pointer + : null; + } } } From 8fba608156ecc275f90a44d6b7af1aaa2a7e15f5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:35 -0400 Subject: [PATCH 409/820] Add more tests for pointerover --- .../TopLevelTests.cs | 21 +- .../MouseDeviceTests.cs | 177 ------ .../PointerOverTests.cs | 533 ++++++++++++++++++ 3 files changed, 536 insertions(+), 195 deletions(-) create mode 100644 tests/Avalonia.Input.UnitTests/PointerOverTests.cs diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 9c2d760733..db6349cc5a 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -193,6 +193,9 @@ namespace Avalonia.Controls.UnitTests public void Impl_Input_Should_Pass_Input_To_InputManager() { var inputManagerMock = new Mock(); + inputManagerMock.DefaultValue = DefaultValue.Mock; + inputManagerMock.SetupAllProperties(); + var services = TestServices.StyledWindow.With(inputManager: inputManagerMock.Object); using (UnitTestApplication.Start(services)) @@ -249,24 +252,6 @@ namespace Avalonia.Controls.UnitTests } } - [Fact] - public void Close_Should_Notify_MouseDevice() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var impl = new Mock(); - var mouseDevice = new Mock(); - impl.SetupAllProperties(); - impl.Setup(x => x.MouseDevice).Returns(mouseDevice.Object); - - var target = new TestTopLevel(impl.Object); - - impl.Object.Closed(); - - mouseDevice.Verify(x => x.TopLevelClosed(target)); - } - } - [Fact] public void Close_Should_Dispose_LayoutManager() { diff --git a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs b/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs index 223f458f25..e8f8244d83 100644 --- a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs +++ b/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs @@ -1,13 +1,10 @@ using Avalonia.Controls; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.UnitTests; -using Avalonia.VisualTree; using Moq; using System; -using System.Collections.Generic; using Xunit; namespace Avalonia.Input.UnitTests @@ -34,160 +31,6 @@ namespace Avalonia.Input.UnitTests } #pragma warning restore CS0618 // Type or member is obsolete - [Fact] - public void MouseMove_Should_Update_IsPointerOver() - { - var renderer = new Mock(); - - using (TestApplication(renderer.Object)) - { - var inputManager = InputManager.Instance; - - Canvas canvas; - Border border; - Decorator decorator; - - var root = new TestRoot - { - MouseDevice = new MouseDevice(), - Renderer = renderer.Object, - Child = new Panel - { - Children = - { - (canvas = new Canvas()), - (border = new Border - { - Child = decorator = new Decorator(), - }) - } - } - }; - - SetHit(renderer, decorator); - SendMouseMove(inputManager, root); - - Assert.True(decorator.IsPointerOver); - Assert.True(border.IsPointerOver); - Assert.False(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - - SetHit(renderer, canvas); - SendMouseMove(inputManager, root); - - Assert.False(decorator.IsPointerOver); - Assert.False(border.IsPointerOver); - Assert.True(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - } - } - - [Fact] - public void IsPointerOver_Should_Be_Updated_When_Child_Sets_Handled_True() - { - var renderer = new Mock(); - - using (TestApplication(renderer.Object)) - { - var inputManager = InputManager.Instance; - - Canvas canvas; - Border border; - Decorator decorator; - - var root = new TestRoot - { - MouseDevice = new MouseDevice(), - Renderer = renderer.Object, - Child = new Panel - { - Children = - { - (canvas = new Canvas()), - (border = new Border - { - Child = decorator = new Decorator(), - }) - } - } - }; - - SetHit(renderer, canvas); - SendMouseMove(inputManager, root); - - Assert.False(decorator.IsPointerOver); - Assert.False(border.IsPointerOver); - Assert.True(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - - // Ensure that e.Handled is reset between controls. - decorator.PointerEnter += (s, e) => e.Handled = true; - - SetHit(renderer, decorator); - SendMouseMove(inputManager, root); - - Assert.True(decorator.IsPointerOver); - Assert.True(border.IsPointerOver); - Assert.False(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - } - } - - [Fact] - public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order() - { - var renderer = new Mock(); - var result = new List<(object, string)>(); - - void HandleEvent(object sender, PointerEventArgs e) - { - result.Add((sender, e.RoutedEvent.Name)); - } - - using (TestApplication(renderer.Object)) - { - var inputManager = InputManager.Instance; - - Canvas canvas; - Border border; - Decorator decorator; - - var root = new TestRoot - { - MouseDevice = new MouseDevice(), - Renderer = renderer.Object, - Child = new Panel - { - Children = - { - (canvas = new Canvas()), - (border = new Border - { - Child = decorator = new Decorator(), - }) - } - } - }; - - SetHit(renderer, canvas); - SendMouseMove(inputManager, root); - - AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator); - SetHit(renderer, decorator); - SendMouseMove(inputManager, root); - - Assert.Equal( - new[] - { - ((object)canvas, "PointerLeave"), - ((object)decorator, "PointerEnter"), - ((object)border, "PointerEnter"), - }, - result); - } - } - - [Fact] public void GetPosition_Should_Respect_Control_RenderTransform() { @@ -216,17 +59,6 @@ namespace Avalonia.Input.UnitTests } } - private void AddEnterLeaveHandlers( - EventHandler handler, - params IControl[] controls) - { - foreach (var c in controls) - { - c.PointerEnter += handler; - c.PointerLeave += handler; - } - } - private void SendMouseMove(IInputManager inputManager, TestRoot root, Point p = new Point()) { inputManager.ProcessInput(new RawPointerEventArgs( @@ -238,15 +70,6 @@ namespace Avalonia.Input.UnitTests RawInputModifiers.None)); } - private void SetHit(Mock renderer, IControl hit) - { - renderer.Setup(x => x.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(new[] { hit }); - - renderer.Setup(x => x.HitTestFirst(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(hit); - } - private IDisposable TestApplication(IRenderer renderer) { return UnitTestApplication.Start( diff --git a/tests/Avalonia.Input.UnitTests/PointerOverTests.cs b/tests/Avalonia.Input.UnitTests/PointerOverTests.cs new file mode 100644 index 0000000000..14acf9f946 --- /dev/null +++ b/tests/Avalonia.Input.UnitTests/PointerOverTests.cs @@ -0,0 +1,533 @@ +#nullable enable +using System; +using System.Collections.Generic; + +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.UnitTests; +using Avalonia.VisualTree; + +using Moq; + +using Xunit; + +namespace Avalonia.Input.UnitTests +{ + public class PointerOverTests + { + // https://github.com/AvaloniaUI/Avalonia/issues/2821 + [Fact] + public void Close_Should_Remove_PointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock().Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(canvas.IsPointerOver); + + impl.Object.Closed!(); + + Assert.False(canvas.IsPointerOver); + } + + [Fact] + public void MouseMove_Should_Update_IsPointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock().Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(decorator.IsPointerOver); + Assert.True(border.IsPointerOver); + Assert.False(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.False(decorator.IsPointerOver); + Assert.False(border.IsPointerOver); + Assert.True(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + } + + + [Fact] + public void TouchMove_Should_Not_Set_IsPointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock(pointerType: PointerType.Touch).Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.False(canvas.IsPointerOver); + Assert.False(root.IsPointerOver); + } + + [Fact] + public void HitTest_Should_Be_Ignored_If_Element_Captured() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var pointer = new Mock(); + var device = CreatePointerDeviceMock(pointer.Object).Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, canvas); + pointer.SetupGet(p => p.Captured).Returns(decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(decorator.IsPointerOver); + Assert.True(border.IsPointerOver); + Assert.False(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + } + + [Fact] + public void IsPointerOver_Should_Be_Updated_When_Child_Sets_Handled_True() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock().Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.False(decorator.IsPointerOver); + Assert.False(border.IsPointerOver); + Assert.True(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + + // Ensure that e.Handled is reset between controls. + root.PointerMoved += (s, e) => e.Handled = true; + decorator.PointerEnter += (s, e) => e.Handled = true; + + SetHit(renderer, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(decorator.IsPointerOver); + Assert.True(border.IsPointerOver); + Assert.False(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + } + + [Fact] + public void Pointer_Enter_Move_Leave_Should_Be_Followed() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + var result = new List<(object?, string)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name)); + } + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + AddEnterLeaveHandlers(HandleEvent, canvas, decorator); + + // Enter decorator + SetHit(renderer, decorator); + SetMove(deviceMock, root, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + // Leave decorator + SetHit(renderer, canvas); + SetMove(deviceMock, root, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + Assert.Equal( + new[] + { + ((object?)decorator, "PointerEnter"), + (decorator, "PointerMove"), + (decorator, "PointerLeave"), + (canvas, "PointerEnter"), + (canvas, "PointerMove") + }, + result); + } + + [Fact] + public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + var result = new List<(object?, string)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name)); + } + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator); + + SetHit(renderer, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + Assert.Equal( + new[] + { + ((object?)canvas, "PointerLeave"), + (decorator, "PointerEnter"), + (border, "PointerEnter"), + }, + result); + } + + // https://github.com/AvaloniaUI/Avalonia/issues/7896 + [Fact] + public void PointerEnter_Leave_Should_Set_Correct_Position() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var expectedPosition = new Point(15, 15); + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + var result = new List<(object?, string, Point)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name, e.GetPosition(null))); + } + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + AddEnterLeaveHandlers(HandleEvent, root, canvas); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition)); + + SetHit(renderer, null); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition)); + + Assert.Equal( + new[] + { + ((object?)canvas, "PointerEnter", expectedPosition), + (root, "PointerEnter", expectedPosition), + (canvas, "PointerLeave", expectedPosition), + (root, "PointerLeave", expectedPosition) + }, + result); + } + + [Fact] + public void Render_Invalidation_Should_Affect_PointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + + var invalidateRect = new Rect(0, 0, 15, 15); + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + // Let input know about latest device. + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + Assert.True(canvas.IsPointerOver); + + SetHit(renderer, canvas); + renderer.Raise(r => r.SceneInvalidated += null, new SceneInvalidatedEventArgs((IRenderRoot)root, invalidateRect)); + Assert.True(canvas.IsPointerOver); + + // Raise SceneInvalidated again, but now hide element from the hittest. + SetHit(renderer, null); + renderer.Raise(r => r.SceneInvalidated += null, new SceneInvalidatedEventArgs((IRenderRoot)root, invalidateRect)); + Assert.False(canvas.IsPointerOver); + } + + // https://github.com/AvaloniaUI/Avalonia/issues/7748 + [Fact] + public void LeaveWindow_Should_Reset_PointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + + var lastClientPosition = new Point(1, 5); + var invalidateRect = new Rect(0, 0, 15, 15); + var result = new List<(object?, string, Point)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name, e.GetPosition(null))); + } + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + AddEnterLeaveHandlers(HandleEvent, root, canvas); + + // Init pointer over. + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, lastClientPosition)); + Assert.True(canvas.IsPointerOver); + + // Send LeaveWindow. + impl.Object.Input!(new RawPointerEventArgs(deviceMock.Object, 0, root, RawPointerEventType.LeaveWindow, new Point(), default)); + Assert.False(canvas.IsPointerOver); + + Assert.Equal( + new[] + { + ((object?)canvas, "PointerEnter", lastClientPosition), + (root, "PointerEnter", lastClientPosition), + (canvas, "PointerLeave", lastClientPosition), + (root, "PointerLeave", lastClientPosition), + }, + result); + } + + private static void AddEnterLeaveHandlers( + EventHandler handler, + params IInputElement[] controls) + { + foreach (var c in controls) + { + c.PointerEnter += handler; + c.PointerLeave += handler; + c.PointerMoved += handler; + } + } + + private static void SetHit(Mock renderer, IControl? hit) + { + renderer.Setup(x => x.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(hit is null ? Array.Empty() : new[] { hit }); + + renderer.Setup(x => x.HitTestFirst(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(hit); + } + + private static void SetMove(Mock deviceMock, IInputRoot root, IInputElement element) + { + deviceMock.Setup(d => d.ProcessRawEvent(It.IsAny())) + .Callback(() => element.RaiseEvent(CreatePointerMovedArgs(root, element))); + } + + private static Mock CreateTopLevelImplMock(IRenderer renderer) + { + var impl = new Mock(); + impl.DefaultValue = DefaultValue.Mock; + impl.SetupAllProperties(); + impl.SetupGet(r => r.RenderScaling).Returns(1); + impl.Setup(r => r.CreateRenderer(It.IsAny())).Returns(renderer); + impl.Setup(r => r.PointToScreen(It.IsAny())).Returns(p => new PixelPoint((int)p.X, (int)p.Y)); + impl.Setup(r => r.PointToClient(It.IsAny())).Returns(p => new Point(p.X, p.Y)); + return impl; + } + + private static IInputRoot CreateInputRoot(IWindowImpl impl, IControl child) + { + var root = new Window(impl) + { + Width = 100, + Height = 100, + Content = child, + Template = new FuncControlTemplate((w, _) => new ContentPresenter + { + Content = w.Content + }) + }; + root.Show(); + return root; + } + + private static IInputRoot CreateInputRoot(IRenderer renderer, IControl child) + { + return CreateInputRoot(CreateTopLevelImplMock(renderer).Object, child); + } + + private static RawPointerEventArgs CreateRawPointerMovedArgs( + IPointerDevice pointerDevice, + IInputRoot root, + Point? positition = null) + { + return new RawPointerEventArgs(pointerDevice, 0, root, RawPointerEventType.Move, + positition ?? default, default); + } + + private static PointerEventArgs CreatePointerMovedArgs( + IInputRoot root, IInputElement? source, Point? positition = null) + { + return new PointerEventArgs(InputElement.PointerMovedEvent, source, new Mock().Object, root, + positition ?? default, default, PointerPointProperties.None, KeyModifiers.None); + } + + private static Mock CreatePointerDeviceMock( + IPointer? pointer = null, + PointerType pointerType = PointerType.Mouse) + { + if (pointer is null) + { + var pointerMock = new Mock(); + pointerMock.SetupGet(p => p.Type).Returns(pointerType); + pointer = pointerMock.Object; + } + + var pointerDevice = new Mock(); + pointerDevice.Setup(d => d.TryGetPointer(It.IsAny())) + .Returns(pointer); + + return pointerDevice; + } + } +} From 45916251515cbe354833366fb91c43decf1541c6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:38 -0400 Subject: [PATCH 410/820] Api compat --- src/Avalonia.Input/ApiCompatBaseline.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 93c4cba5a6..6e68e7aed9 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -14,6 +14,7 @@ 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 Avalonia.Input.IPointer Avalonia.Input.IPointerDevice.TryGetPointer(Avalonia.Input.Raw.RawPointerEventArgs)' 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. From 3c8cc3610f5559f8b8f2610faf8ca8ab0fdb3cb6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:56 -0400 Subject: [PATCH 411/820] Use InputHitTest instead of GetInputElementsAt+Linq in dev tools --- .../Diagnostics/ViewModels/MainViewModel.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index e08c5bc8dd..d92bbb742b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -7,7 +7,6 @@ using Avalonia.Diagnostics.Models; using Avalonia.Input; using Avalonia.Metadata; using Avalonia.Threading; -using System.Reactive.Linq; using System.Linq; namespace Avalonia.Diagnostics.ViewModels @@ -59,8 +58,8 @@ namespace Avalonia.Diagnostics.ViewModels .Subscribe(e => { PointerOverRoot = e.Root; - PointerOverElement = e.Root.GetInputElementsAt(e.Position).FirstOrDefault(); - }); + PointerOverElement = e.Root.InputHitTest(e.Position); + }); #nullable restore } Console = new ConsoleViewModel(UpdateConsoleContext); From 0bb22cff12d804b5a7993ea2e9ef6f0876060b3b Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 01:07:11 +0800 Subject: [PATCH 412/820] SubtypesFactory generator --- Avalonia.sln | 29 ++- build/SourceGenerators.props | 6 + src/Avalonia.Base/Animation/Easings/Easing.cs | 35 ++-- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/Input/KeyGesture.cs | 8 + .../Remote/HtmlTransport/HtmlTransport.cs | 8 + .../Avalonia.SourceGenerator.csproj | 17 ++ .../SubtypesFactoryGenerator.cs | 167 ++++++++++++++++++ src/Shared/SourceGeneratorAttributes.cs | 17 ++ 9 files changed, 265 insertions(+), 23 deletions(-) create mode 100644 build/SourceGenerators.props create mode 100644 src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj create mode 100644 src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs create mode 100644 src/Shared/SourceGeneratorAttributes.cs diff --git a/Avalonia.sln b/Avalonia.sln index a216229a5b..39788160b2 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -39,6 +39,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs + src\Shared\RawEventGrouping.cs = src\Shared\RawEventGrouping.cs + src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" @@ -209,10 +211,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject Global @@ -1929,6 +1932,30 @@ Global {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.Build.0 = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.ActiveCfg = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.Build.0 = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props new file mode 100644 index 0000000000..f9eab24d08 --- /dev/null +++ b/build/SourceGenerators.props @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Avalonia.Base/Animation/Easings/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs index c721772f3e..6dfc4c86b3 100644 --- a/src/Avalonia.Base/Animation/Easings/Easing.cs +++ b/src/Avalonia.Base/Animation/Easings/Easing.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Linq; +using Avalonia.SourceGenerator; namespace Avalonia.Animation.Easings { @@ -10,14 +11,15 @@ namespace Avalonia.Animation.Easings /// Base class for all Easing classes. /// [TypeConverter(typeof(EasingTypeConverter))] - public abstract class Easing : IEasing + public abstract partial class Easing : IEasing { /// public abstract double Ease(double progress); - static Dictionary? _easingTypes; + private const string Namespace = "Avalonia.Animation.Easings"; - static readonly Type s_thisType = typeof(Easing); + [SubtypesFactory(typeof(Easing), Namespace)] + private static partial bool TryCreateEasingInstance(string type, [NotNullWhen(true)] out Easing? instance); /// /// Parses a Easing type string. @@ -26,33 +28,22 @@ namespace Avalonia.Animation.Easings /// Returns the instance of the parsed type. public static Easing Parse(string e) { +#if NETSTANDARD2_0 + if (e.Contains(",")) +#else if (e.Contains(',')) +#endif { return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture)); } - if (_easingTypes == null) + if (TryCreateEasingInstance(e, out var easing)) { - _easingTypes = new Dictionary(); - - // Fetch the built-in easings. - var derivedTypes = typeof(Easing).Assembly.GetTypes() - .Where(p => p.Namespace == s_thisType.Namespace) - .Where(p => p.IsSubclassOf(s_thisType)) - .Select(p => p); - - foreach (var easingType in derivedTypes) - _easingTypes.Add(easingType.Name, easingType); - } - - if (_easingTypes.ContainsKey(e)) - { - var type = _easingTypes[e]; - return (Easing)Activator.CreateInstance(type)!; + return easing; } else { - throw new FormatException($"Easing \"{e}\" was not found in {s_thisType.Namespace} namespace."); + throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace."); } } } diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 5b79d3cd7b..8e4755b4b7 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -16,4 +16,5 @@ + diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 0adbe73263..6f302a3ae7 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -155,7 +155,11 @@ namespace Avalonia.Input if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) return rv; +#if NETSTANDARD2_0 return (Key)Enum.Parse(typeof(Key), key, true); +#else + return Enum.Parse(key, true); +#endif } private static KeyModifiers ParseModifier(ReadOnlySpan modifier) @@ -172,7 +176,11 @@ namespace Avalonia.Input return KeyModifiers.Meta; } +#if NETSTANDARD2_0 return (KeyModifiers)Enum.Parse(typeof(KeyModifiers), modifier.ToString(), true); +#else + return Enum.Parse(modifier.ToString(), true); +#endif } private Key ResolveNumPadOperationKey(Key key) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 6b1934ed06..7965e5d0d8 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -320,15 +320,23 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') +#if NETSTANDARD2_0 .Select(x => (InputProtocol.InputModifiers)Enum.Parse( typeof(InputProtocol.InputModifiers), x, true)) +#else + .Select(x => Enum.Parse(x, true)) +#endif .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None +#if NETSTANDARD2_0 : (InputProtocol.MouseButton)Enum.Parse( typeof(InputProtocol.MouseButton), buttonText, true); +#else + : Enum.Parse(buttonText, true); +#endif private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj b/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj new file mode 100644 index 0000000000..97e58f8a64 --- /dev/null +++ b/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + enable + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs new file mode 100644 index 0000000000..7bc240853e --- /dev/null +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -0,0 +1,167 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Avalonia.SourceGenerator +{ + internal class GenerateSubtypesSyntaxReceiver : ISyntaxReceiver + { + public List<(MethodDeclarationSyntax, AttributeSyntax)> CandidateMethods { get; } = new(); + public List Types { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is MethodDeclarationSyntax declarationSyntax) + { + foreach (var attribute in declarationSyntax.AttributeLists.SelectMany(i => i.Attributes)) + { + CandidateMethods.Add((declarationSyntax, attribute)); + } + } + + if (syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax) + { + Types.Add(syntaxNode); + } + } + } + + [Generator] + internal class SubtypesFactoryGenerator : ISourceGenerator + { + private readonly GenerateSubtypesSyntaxReceiver _receiver = new(); + private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; + + public void Execute(GeneratorExecutionContext context) + { + var methods = new List<(IMethodSymbol, ITypeSymbol, string)>(); + + foreach (var (method, attribute) in _receiver.CandidateMethods) + { + var semanticModel = context.Compilation.GetSemanticModel(method.SyntaxTree); + var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); + if (attributeTypeInfo.Type is null || + attributeTypeInfo.Type.ToString() != s_attributeName || + attribute.ArgumentList is null) + { + continue; + } + + var arguments = attribute.ArgumentList.Arguments; + if (arguments.Count != 2) + { + continue; + } + + if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || + arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) + { + continue; + } + + var type = semanticModel.GetTypeInfo(typeOfExpr.Type); + var ns = semanticModel.GetConstantValue(arguments[1].Expression); + var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); + + if (type.Type is not ITypeSymbol baseType || + ns.HasValue is false || + ns.Value is not string nsValue || + methodDeclInfo is not IMethodSymbol methodSymbol || + methodSymbol.Parameters.Length != 2 || + methodSymbol.Parameters[1].RefKind != RefKind.Out) + { + continue; + } + + methods.Add((methodSymbol, baseType, nsValue)); + } + + var types = new List(); + foreach (var type in _receiver.Types) + { + var semanticModel = context.Compilation.GetSemanticModel(type.SyntaxTree); + var decl = semanticModel.GetDeclaredSymbol(type); + if (decl is ITypeSymbol typeSymbol) + { + types.Add(typeSymbol); + } + } + + GenerateSubTypes(context, methods, types); + } + + private bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) + { + if (type.BaseType is null) + { + return false; + } + + if (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType)) + { + return true; + } + + return IsSubtypeOf(type.BaseType, baseType); + } + + private void GenerateSubTypes( + GeneratorExecutionContext context, + List<(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace)> methods, + List types) + { + foreach (var (method, baseType, @namespace) in methods) + { + var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); + var type = method.ContainingType; + var isGeneric = type.TypeParameters.Length > 0; + var isClass = type.TypeKind == TypeKind.Class; + + if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) + { + continue; + } + + var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); + + var methodDeclText = methodDecl + .WithAttributeLists(new SyntaxList()) + .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); + + var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; + var source = $@"using System; +using System.Collections.Generic; + +namespace {method.ContainingNamespace} +{{ + {typeDecl} + {{ + {methodDeclText} + {{ + var hasMatch = false; + (hasMatch, {method.Parameters[1].Name}) = {method.Parameters[0].Name} switch + {{ +{string.Join("\n", candidateTypes.Select(i => $" \"{i.Name}\" => (true, ({method.Parameters[1].Type})new {i}()),"))} + _ => (false, default({method.Parameters[1].Type})) + }}; + + return hasMatch; + }} + }} +}}"; + + context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + } + } + + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => _receiver); + } + } +} diff --git a/src/Shared/SourceGeneratorAttributes.cs b/src/Shared/SourceGeneratorAttributes.cs new file mode 100644 index 0000000000..fdb5977d23 --- /dev/null +++ b/src/Shared/SourceGeneratorAttributes.cs @@ -0,0 +1,17 @@ +using System; + +namespace Avalonia.SourceGenerator +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class SubtypesFactoryAttribute : Attribute + { + public SubtypesFactoryAttribute(Type baseType, string @namespace) + { + BaseType = baseType; + Namespace = @namespace; + } + + public string Namespace { get; } + public Type BaseType { get; } + } +} From 9296ada3432010edb0201f99b81daeb498f89809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 19:12:02 +0200 Subject: [PATCH 413/820] Post merge fixup. --- src/Avalonia.Controls/TopLevel.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 271129f3ac..d27a020300 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -91,7 +91,7 @@ namespace Avalonia.Controls private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager? _layoutManager; private Border? _transparencyFallbackBorder; - private WeakEventSubscriber _resourcesChangesSubscriber; + private WeakEventSubscriber? _resourcesChangesSubscriber; /// /// Initializes static members of the class. @@ -300,11 +300,6 @@ namespace Avalonia.Controls /// IMouseDevice? IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; - void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, ResourcesChangedEventArgs e) - { - ((ILogical)this).NotifyResourcesChanged(e); - } - /// /// Gets or sets a value indicating whether access keys are shown in the window. /// From 6700fd851b8196b34d053bd4956906a6ee6f6c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 19:12:16 +0200 Subject: [PATCH 414/820] Add a benchmark case for affects render. --- .../Visuals/VisualAffectsRenderBenchmarks.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs diff --git a/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs new file mode 100644 index 0000000000..4a7c842249 --- /dev/null +++ b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs @@ -0,0 +1,45 @@ +using Avalonia.Controls; +using Avalonia.Media; +using BenchmarkDotNet.Attributes; + +namespace Avalonia.Benchmarks.Visuals; + +[MemoryDiagnoser] +public class VisualAffectsRenderBenchmarks +{ + private readonly TestVisual _target; + private readonly IPen _pen; + + public VisualAffectsRenderBenchmarks() + { + _target = new TestVisual(); + _pen = new Pen(Brushes.Black); + } + + [Benchmark] + public void SetPropertyThatAffectsRender() + { + _target.Pen = _pen; + _target.Pen = null; + } + + private class TestVisual : Visual + { + /// + /// Defines the property. + /// + public static readonly StyledProperty PenProperty = + AvaloniaProperty.Register(nameof(Pen)); + + public IPen Pen + { + get { return GetValue(PenProperty); } + set { SetValue(PenProperty, value); } + } + + static TestVisual() + { + AffectsRender(PenProperty); + } + } +} From 276afe4271cc7e95bf182bd0589eaa0bd27709f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 19:12:44 +0200 Subject: [PATCH 415/820] Reduce allocations when creating weak event subscribers. --- .../Utilities/IWeakEventSubscriber.cs | 21 +++++++++++++++++-- src/Avalonia.Base/Visual.cs | 12 +++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs index 57060853c8..6cf8c605a7 100644 --- a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs @@ -11,7 +11,7 @@ public interface IWeakEventSubscriber where TEventArgs : EventArg void OnEvent(object? sender, WeakEvent ev, TEventArgs e); } -public class WeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs +public sealed class WeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs { public event Action? Event; @@ -19,4 +19,21 @@ public class WeakEventSubscriber : IWeakEventSubscriber { Event?.Invoke(sender, ev, e); } -} \ No newline at end of file +} + +public sealed class TargetWeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs +{ + private readonly TTarget _target; + private readonly Action _dispatchFunc; + + public TargetWeakEventSubscriber(TTarget target, Action dispatchFunc) + { + _target = target; + _dispatchFunc = dispatchFunc; + } + + void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, TEventArgs e) + { + _dispatchFunc(_target, sender, ev, e); + } +} diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index e25cb854cf..4fd21f02f9 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -108,7 +108,7 @@ namespace Avalonia private IRenderRoot? _visualRoot; private IVisual? _visualParent; private bool _hasMirrorTransform; - private WeakEventSubscriber? _affectsRenderWeakSubscriber; + private TargetWeakEventSubscriber? _affectsRenderWeakSubscriber; /// /// Initializes static members of the class. @@ -383,11 +383,11 @@ namespace Avalonia { if (sender._affectsRenderWeakSubscriber == null) { - sender._affectsRenderWeakSubscriber = new WeakEventSubscriber(); - sender._affectsRenderWeakSubscriber.Event += delegate - { - sender.InvalidateVisual(); - }; + sender._affectsRenderWeakSubscriber = new TargetWeakEventSubscriber( + sender, static (target, _, _, _) => + { + target.InvalidateVisual(); + }); } InvalidatedWeakEvent.Subscribe(newValue, sender._affectsRenderWeakSubscriber); } From 832afd2b46be359b9ea25e550f39902439d584a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 22:38:51 +0200 Subject: [PATCH 416/820] Apply optimization to Pen as well. --- src/Avalonia.Base/Media/Pen.cs | 15 ++++++++------- .../Visuals/VisualAffectsRenderBenchmarks.cs | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/Media/Pen.cs b/src/Avalonia.Base/Media/Pen.cs index f0a0d24248..bda0e5cb99 100644 --- a/src/Avalonia.Base/Media/Pen.cs +++ b/src/Avalonia.Base/Media/Pen.cs @@ -48,7 +48,7 @@ namespace Avalonia.Media private EventHandler? _invalidated; private IAffectsRender? _subscribedToBrush; private IAffectsRender? _subscribedToDashes; - private WeakEventSubscriber? _weakSubscriber; + private TargetWeakEventSubscriber? _weakSubscriber; /// /// Initializes a new instance of the class. @@ -217,13 +217,14 @@ namespace Avalonia.Media { if (_weakSubscriber == null) { - _weakSubscriber = new WeakEventSubscriber(); - _weakSubscriber.Event += (_, ev, __) => - { - if (ev == InvalidatedWeakEvent) - _invalidated?.Invoke(this, EventArgs.Empty); - }; + _weakSubscriber = new TargetWeakEventSubscriber( + this, static (target, _, ev, _) => + { + if (ev == InvalidatedWeakEvent) + target._invalidated?.Invoke(target, EventArgs.Empty); + }); } + InvalidatedWeakEvent.Subscribe(affectsRender, _weakSubscriber); field = affectsRender; } diff --git a/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs index 4a7c842249..5b85a068d6 100644 --- a/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs @@ -33,8 +33,8 @@ public class VisualAffectsRenderBenchmarks public IPen Pen { - get { return GetValue(PenProperty); } - set { SetValue(PenProperty, value); } + get => GetValue(PenProperty); + set => SetValue(PenProperty, value); } static TestVisual() From 7beafdc1d84f453211a537a614c66924bf2a098d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 22:50:30 +0200 Subject: [PATCH 417/820] Refactor TopLevel and ItemsRepeater handling. --- .../Repeater/ItemsRepeater.cs | 21 ++++++++++--------- src/Avalonia.Controls/TopLevel.cs | 13 ++++++------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 97129f887d..c59037a30b 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -60,6 +60,7 @@ namespace Avalonia.Controls private readonly ViewManager _viewManager; private readonly ViewportManager _viewportManager; + private readonly TargetWeakEventSubscriber _layoutWeakSubscriber; private IEnumerable? _items; private VirtualizingLayoutContext? _layoutContext; private EventHandler? _childIndexChanged; @@ -68,25 +69,25 @@ namespace Avalonia.Controls private ItemsRepeaterElementPreparedEventArgs? _elementPreparedArgs; private ItemsRepeaterElementClearingEventArgs? _elementClearingArgs; private ItemsRepeaterElementIndexChangedEventArgs? _elementIndexChangedArgs; - private WeakEventSubscriber _layoutWeakSubscriber = new(); /// /// Initializes a new instance of the class. /// public ItemsRepeater() { + _layoutWeakSubscriber = new TargetWeakEventSubscriber( + this, static (target, _, ev, _) => + { + if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent) + target.InvalidateArrange(); + else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) + target.InvalidateMeasure(); + }); + _viewManager = new ViewManager(this); _viewportManager = new ViewportManager(this); KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once); - - _layoutWeakSubscriber.Event += (_, ev, e) => - { - if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent) - InvalidateArrange(); - else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) - InvalidateMeasure(); - }; - + OnLayoutChanged(null, Layout); } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index d27a020300..34e87e18cc 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -91,7 +91,7 @@ namespace Avalonia.Controls private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager? _layoutManager; private Border? _transparencyFallbackBorder; - private WeakEventSubscriber? _resourcesChangesSubscriber; + private TargetWeakEventSubscriber? _resourcesChangesSubscriber; /// /// Initializes static members of the class. @@ -191,11 +191,12 @@ namespace Avalonia.Controls if (((IStyleHost)this).StylingParent is IResourceHost applicationResources) { - _resourcesChangesSubscriber = new(); - _resourcesChangesSubscriber.Event += (_, __, e) => - { - ((ILogical)this).NotifyResourcesChanged(e); - }; + _resourcesChangesSubscriber = new TargetWeakEventSubscriber( + this, static (target, _, _, e) => + { + ((ILogical)target).NotifyResourcesChanged(e); + }); + ResourcesChangedWeakEvent.Subscribe(applicationResources, _resourcesChangesSubscriber); } From 705a2813ab7b4a676a35eeca0cdb1f0822c87be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 22:53:22 +0200 Subject: [PATCH 418/820] Undo formatting change. --- src/Avalonia.Controls/Repeater/ItemsRepeater.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index c59037a30b..c63adb0bf6 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -87,7 +87,6 @@ namespace Avalonia.Controls _viewManager = new ViewManager(this); _viewportManager = new ViewportManager(this); KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once); - OnLayoutChanged(null, Layout); } From e6044ea33069d994b65aa05aad7c775e7686d8d9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Apr 2022 23:49:52 +0200 Subject: [PATCH 419/820] Remove Avalonia.AndroidTestApplication. It's no longer needed. --- Avalonia.sln | 21 +--- .../Avalonia.AndroidTestApplication.csproj | 79 ------------- .../MainActivity.cs | 104 ------------------ .../Properties/AndroidManifest.xml | 4 - .../Resources/AboutResources.txt | 50 --------- .../Resources/drawable/Icon.png | Bin 4147 -> 0 bytes .../Resources/values/Strings.xml | 6 - 7 files changed, 1 insertion(+), 263 deletions(-) delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj delete mode 100644 src/Android/Avalonia.AndroidTestApplication/MainActivity.cs delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Resources/drawable/Icon.png delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml diff --git a/Avalonia.sln b/Avalonia.sln index a216229a5b..64ed214e1f 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -55,8 +55,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.iOS", "src\iOS\Avalonia.iOS\Avalonia.iOS.csproj", "{4488AD85-1495-4809-9AA4-DDFE0A48527E}" @@ -209,7 +207,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" EndProject @@ -629,22 +627,6 @@ Global {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.Build.0 = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhone.ActiveCfg = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Build.0 = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Deploy.0 = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -1970,7 +1952,6 @@ Global {8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098} {7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} - {FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} {4488AD85-1495-4809-9AA4-DDFE0A48527E} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1} {E1AA3DBF-9056-4530-9376-18119A7A3FFE} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj deleted file mode 100644 index ee41b9f354..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ /dev/null @@ -1,79 +0,0 @@ - - - net6.0-android - 21 - Exe - enable - com.Avalonia.AndroidTestApplication - 1 - 1.0 - apk - true - portable - - - - - Resources\drawable\Icon.png - - - - - True - True - True - True - - - - - - - True - - - - True - - - - - - - - - {7b92af71-6287-4693-9dcb-bd5b6e927e23} - Avalonia.Android - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {7062ae20-5dcc-4442-9645-8195bdece63e} - Avalonia.Diagnostics - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - - - - - - - diff --git a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs deleted file mode 100644 index 8f4beb2737..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using Android.App; -using Android.Content.PM; -using Avalonia.Android; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Input.TextInput; -using Avalonia.Markup.Xaml; -using Avalonia.Media; -using Avalonia.Styling; -using Avalonia.Themes.Default; - -namespace Avalonia.AndroidTestApplication -{ - [Activity(Label = "Main", - MainLauncher = true, - Icon = "@drawable/icon", - Theme = "@style/Theme.AppCompat.NoActionBar", - ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, - LaunchMode = LaunchMode.SingleInstance/*, - ScreenOrientation = ScreenOrientation.Landscape*/)] - public class MainActivity : AvaloniaActivity - { - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) - { - return base.CustomizeAppBuilder(builder); - } - } - - public class App : Application - { - public override void Initialize() - { - Styles.Add(new SimpleTheme(new Uri("avares://Avalonia.AndroidTestApplication"))); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) - { - singleViewLifetime.MainView = CreateSimpleWindow(); - } - - base.OnFrameworkInitializationCompleted(); - } - - // This provides a simple UI tree for testing input handling, drawing, etc - public static ContentControl CreateSimpleWindow() - { - ContentControl window = new ContentControl() - { - Background = Brushes.Red, - Content = new StackPanel - { - Margin = new Thickness(30), - Background = Brushes.Yellow, - Children = - { - new TextBlock - { - Text = "TEXT BLOCK", - Width = 300, - Height = 40, - Background = Brushes.White, - Foreground = Brushes.Black - }, - - new Button - { - Content = "BUTTON", - Width = 150, - Height = 40, - Background = Brushes.LightGreen, - Foreground = Brushes.Black - }, - - CreateTextBox(TextInputContentType.Normal), - CreateTextBox(TextInputContentType.Password), - CreateTextBox(TextInputContentType.Email), - CreateTextBox(TextInputContentType.Url), - CreateTextBox(TextInputContentType.Digits), - CreateTextBox(TextInputContentType.Number), - } - } - }; - - return window; - } - - private static TextBox CreateTextBox(TextInputContentType contentType) - { - var textBox = new TextBox() - { - Margin = new Thickness(20, 10), - Watermark = contentType.ToString(), - BorderThickness = new Thickness(3), - FontSize = 20, - [TextInputOptions.ContentTypeProperty] = contentType - }; - - return textBox; - } - } -} diff --git a/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml b/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml deleted file mode 100644 index ad8134f628..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt b/src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt deleted file mode 100644 index 194ae28a59..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt +++ /dev/null @@ -1,50 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.xml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable-hdpi/ - icon.png - - drawable-ldpi/ - icon.png - - drawable-mdpi/ - icon.png - - layout/ - main.xml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called -"Resource" that contains the tokens for each one of the resources included. For example, -for the above Resources layout, this is what the Resource class would expose: - -public class Resource { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main -to reference the layout/main.xml file, or Resource.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/drawable/Icon.png b/src/Android/Avalonia.AndroidTestApplication/Resources/drawable/Icon.png deleted file mode 100644 index 8074c4c571b8cd19e27f4ee5545df367420686d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4147 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 diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml b/src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml deleted file mode 100644 index c8dca13c35..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - Hello World, Click Me! - Avalonia.AndroidTestApplication - \ No newline at end of file From a71ddcc8700cdd9a589b2385c0e801d55df0928c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Apr 2022 23:50:16 +0200 Subject: [PATCH 420/820] Fix project files broken by #5831. --- .../ControlCatalog.Android.csproj | 44 +-------------- .../ControlCatalog.iOS.csproj | 53 +------------------ 2 files changed, 4 insertions(+), 93 deletions(-) diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 8d2ead054c..04c67e84e8 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -39,49 +39,9 @@ - + - - {7b92af71-6287-4693-9dcb-bd5b6e927e23} - Avalonia.Android - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {7062ae20-5dcc-4442-9645-8195bdece63e} - Avalonia.Diagnostics - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {6417e941-21bc-467b-a771-0de389353ce6} - Avalonia.Markup - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - {d0a739b9-3c68-4ba6-a328-41606954b6bd} - ControlCatalog - - - - - - + \ No newline at end of file diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index d4f10b3580..513ac44f83 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -7,59 +7,10 @@ True iossimulator-x64 - + - - - - - - - - - {4488ad85-1495-4809-9aa4-ddfe0a48527e} - Avalonia.iOS - - - {3E53A01A-B331-47F3-B828-4A5717E77A24} - Avalonia.Markup.Xaml - - - {6417E941-21BC-467B-A771-0DE389353CE6} - Avalonia.Markup - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {7062AE20-5DCC-4442-9645-8195BDECE63E} - Avalonia.Diagnostics - - - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} - Avalonia.Themes.Default - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - {d0a739b9-3c68-4ba6-a328-41606954b6bd} - ControlCatalog - - - - - - - - + \ No newline at end of file From c1644eb21455bb2658ed67c52c5425139c0cb672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 22:11:48 +0000 Subject: [PATCH 421/820] Bump async in /src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/caolan/async/releases) - [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md) - [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: async dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../Remote/HtmlTransport/webapp/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json index 08ede477c3..403bb5a59a 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json @@ -377,9 +377,9 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" From 98ba0f529bcbf708276b6fda48e4130e10a76751 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 10:44:54 +0200 Subject: [PATCH 422/820] Initial implementation of nested styles. No XAML/parser support yet. --- src/Avalonia.Base/Styling/ChildSelector.cs | 4 +- .../Styling/DescendentSelector.cs | 4 +- src/Avalonia.Base/Styling/NestingSelector.cs | 29 ++++ src/Avalonia.Base/Styling/NotSelector.cs | 4 +- src/Avalonia.Base/Styling/NthChildSelector.cs | 2 +- src/Avalonia.Base/Styling/OrSelector.cs | 4 +- .../Styling/PropertyEqualsSelector.cs | 2 +- src/Avalonia.Base/Styling/Selector.cs | 31 ++-- src/Avalonia.Base/Styling/Selectors.cs | 9 ++ src/Avalonia.Base/Styling/Style.cs | 22 ++- src/Avalonia.Base/Styling/StyleChildren.cs | 29 ++++ src/Avalonia.Base/Styling/TemplateSelector.cs | 4 +- .../Styling/TypeNameAndClassSelector.cs | 2 +- .../Styling/SelectorTests_Nesting.cs | 132 ++++++++++++++++++ 14 files changed, 253 insertions(+), 25 deletions(-) create mode 100644 src/Avalonia.Base/Styling/NestingSelector.cs create mode 100644 src/Avalonia.Base/Styling/StyleChildren.cs create mode 100644 tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs diff --git a/src/Avalonia.Base/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs index 5c92182b80..8675ca8820 100644 --- a/src/Avalonia.Base/Styling/ChildSelector.cs +++ b/src/Avalonia.Base/Styling/ChildSelector.cs @@ -37,13 +37,13 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var controlParent = ((ILogical)control).LogicalParent; if (controlParent != null) { - var parentMatch = _parent.Match((IStyleable)controlParent, subscribe); + var parentMatch = _parent.Match((IStyleable)controlParent, parent, subscribe); if (parentMatch.Result == SelectorMatchResult.Sometimes) { diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs index dde88b3436..b164ad1c69 100644 --- a/src/Avalonia.Base/Styling/DescendentSelector.cs +++ b/src/Avalonia.Base/Styling/DescendentSelector.cs @@ -35,7 +35,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var c = (ILogical)control; var descendantMatches = new OrActivatorBuilder(); @@ -46,7 +46,7 @@ namespace Avalonia.Styling if (c is IStyleable) { - var match = _parent.Match((IStyleable)c, subscribe); + var match = _parent.Match((IStyleable)c, parent, subscribe); if (match.Result == SelectorMatchResult.Sometimes) { diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs new file mode 100644 index 0000000000..1be54dea3c --- /dev/null +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -0,0 +1,29 @@ +using System; + +namespace Avalonia.Styling +{ + /// + /// The `&` nesting style selector. + /// + internal class NestingSelector : Selector + { + public override bool InTemplate => false; + public override bool IsCombinator => false; + public override Type? TargetType => null; + + public override string ToString() => "&"; + + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) + { + if (parent is Style s && s.Selector is Selector selector) + { + return selector.Match(control, null, subscribe); + } + + throw new InvalidOperationException( + "Nesting selector was specified but cannot determine parent selector."); + } + + protected override Selector? MovePrevious() => null; + } +} diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index ab4e9d5d7f..c4beca6b9e 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -45,9 +45,9 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { - var innerResult = _argument.Match(control, subscribe); + var innerResult = _argument.Match(control, parent, subscribe); switch (innerResult.Result) { diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs index aff34ea17c..cbb5e64772 100644 --- a/src/Avalonia.Base/Styling/NthChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthChildSelector.cs @@ -48,7 +48,7 @@ namespace Avalonia.Styling public int Step { get; } public int Offset { get; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (!(control is ILogical logical)) { diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index 3d6db9b01e..a34712caaf 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -65,14 +65,14 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var activators = new OrActivatorBuilder(); var neverThisInstance = false; foreach (var selector in _selectors) { - var match = selector.Match(control, subscribe); + var match = selector.Match(control, parent, subscribe); switch (match.Result) { diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs index 1cd1a650ef..34474fb7ab 100644 --- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs @@ -74,7 +74,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (subscribe) { diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index 0740e0f891..008ce7f3a5 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -33,22 +33,25 @@ namespace Avalonia.Styling /// Tries to match the selector with a control. /// /// The control. + /// + /// The parent style, if the style containing the selector is a nested style. + /// /// /// Whether the match should subscribe to changes in order to track the match over time, /// or simply return an immediate result. /// /// A . - public SelectorMatch Match(IStyleable control, bool subscribe = true) + public SelectorMatch Match(IStyleable control, IStyle? parent = null, bool subscribe = true) { // First match the selector until a combinator is found. Selectors are stored from // right-to-left, so MatchUntilCombinator reverses this order because the type selector // will be on the left. - var match = MatchUntilCombinator(control, this, subscribe, out var combinator); + var match = MatchUntilCombinator(control, this, parent, subscribe, out var combinator); // If the pre-combinator selector matches, we can now match the combinator, if any. if (match.IsMatch && combinator is object) { - match = match.And(combinator.Match(control, subscribe)); + match = match.And(combinator.Match(control, parent, subscribe)); // If we have a combinator then we can never say that we always match a control of // this type, because by definition the combinator matches on things outside of the @@ -68,12 +71,15 @@ namespace Avalonia.Styling /// Evaluates the selector for a match. /// /// The control. + /// + /// The parent style, if the style containing the selector is a nested style. + /// /// /// Whether the match should subscribe to changes in order to track the match over time, /// or simply return an immediate result. /// /// A . - protected abstract SelectorMatch Evaluate(IStyleable control, bool subscribe); + protected abstract SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe); /// /// Moves to the previous selector. @@ -83,13 +89,18 @@ namespace Avalonia.Styling private static SelectorMatch MatchUntilCombinator( IStyleable control, Selector start, + IStyle? parent, bool subscribe, out Selector? combinator) { combinator = null; var activators = new AndActivatorBuilder(); - var result = Match(control, start, subscribe, ref activators, ref combinator); + var foundNested = false; + var result = Match(control, start, parent, subscribe, ref activators, ref combinator, ref foundNested); + + if (parent is not null && !foundNested) + throw new InvalidOperationException("Nesting selector '&' must appear in child selector."); return result == SelectorMatchResult.Sometimes ? new SelectorMatch(activators.Get()) : @@ -99,9 +110,11 @@ namespace Avalonia.Styling private static SelectorMatchResult Match( IStyleable control, Selector selector, + IStyle? parent, bool subscribe, ref AndActivatorBuilder activators, - ref Selector? combinator) + ref Selector? combinator, + ref bool foundNested) { var previous = selector.MovePrevious(); @@ -110,7 +123,7 @@ namespace Avalonia.Styling // opportunity to exit early. if (previous != null && !previous.IsCombinator) { - var previousMatch = Match(control, previous, subscribe, ref activators, ref combinator); + var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator, ref foundNested); if (previousMatch < SelectorMatchResult.Sometimes) { @@ -118,8 +131,10 @@ namespace Avalonia.Styling } } + foundNested |= selector is NestingSelector; + // Match this selector. - var match = selector.Evaluate(control, subscribe); + var match = selector.Evaluate(control, parent, subscribe); if (!match.IsMatch) { diff --git a/src/Avalonia.Base/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs index 7c66469cf1..a036c140c2 100644 --- a/src/Avalonia.Base/Styling/Selectors.cs +++ b/src/Avalonia.Base/Styling/Selectors.cs @@ -109,6 +109,15 @@ namespace Avalonia.Styling } } + public static Selector Nesting(this Selector? previous) + { + if (previous is not null) + throw new InvalidOperationException( + "Nesting selector '&' must appear at the start of the style selector."); + + return new NestingSelector(); + } + /// /// Returns a selector which inverts the results of selector argument. /// diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 00819ef7be..a3a7ea29d3 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -4,8 +4,6 @@ using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Metadata; -#nullable enable - namespace Avalonia.Styling { /// @@ -14,6 +12,7 @@ namespace Avalonia.Styling public class Style : AvaloniaObject, IStyle, IResourceProvider { private IResourceHost? _owner; + private StyleChildren? _children; private IResourceDictionary? _resources; private List? _setters; private List? _animations; @@ -34,6 +33,14 @@ namespace Avalonia.Styling Selector = selector(null); } + /// + /// Gets the children of the style. + /// + public IList Children => _children ??= new(this); + + /// + /// Gets the or Application that hosts the style. + /// public IResourceHost? Owner { get => _owner; @@ -47,6 +54,11 @@ namespace Avalonia.Styling } } + /// + /// Gets the parent style if this style is hosted in a collection. + /// + public Style? Parent { get; private set; } + /// /// Gets or sets a dictionary of style resources. /// @@ -90,7 +102,7 @@ namespace Avalonia.Styling public IList Animations => _animations ??= new List(); bool IResourceNode.HasResources => _resources?.Count > 0; - IReadOnlyList IStyle.Children => Array.Empty(); + IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); public event EventHandler? OwnerChanged; @@ -98,7 +110,7 @@ namespace Avalonia.Styling { target = target ?? throw new ArgumentNullException(nameof(target)); - var match = Selector is object ? Selector.Match(target) : + var match = Selector is object ? Selector.Match(target, Parent) : target == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; if (match.IsMatch && (_setters is object || _animations is object)) @@ -156,5 +168,7 @@ namespace Avalonia.Styling _resources?.RemoveOwner(owner); } } + + internal void SetParent(Style? parent) => Parent = parent; } } diff --git a/src/Avalonia.Base/Styling/StyleChildren.cs b/src/Avalonia.Base/Styling/StyleChildren.cs new file mode 100644 index 0000000000..21ac3c4072 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleChildren.cs @@ -0,0 +1,29 @@ +using System.Collections.ObjectModel; + +namespace Avalonia.Styling +{ + internal class StyleChildren : Collection + { + private readonly Style _owner; + + public StyleChildren(Style owner) => _owner = owner; + + protected override void InsertItem(int index, IStyle item) + { + base.InsertItem(index, item); + (item as Style)?.SetParent(_owner); + } + + protected override void RemoveItem(int index) + { + (Items[index] as Style)?.SetParent(null); + base.RemoveItem(index); + } + + protected override void SetItem(int index, IStyle item) + { + base.SetItem(index, item); + (item as Style)?.SetParent(_owner); + } + } +} diff --git a/src/Avalonia.Base/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs index e8051efa6d..6f0f9e0900 100644 --- a/src/Avalonia.Base/Styling/TemplateSelector.cs +++ b/src/Avalonia.Base/Styling/TemplateSelector.cs @@ -36,7 +36,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var templatedParent = control.TemplatedParent as IStyleable; @@ -45,7 +45,7 @@ namespace Avalonia.Styling return SelectorMatch.NeverThisInstance; } - return _parent.Match(templatedParent, subscribe); + return _parent.Match(templatedParent, parent, subscribe); } protected override Selector? MovePrevious() => null; diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index ef48c4a8cd..ae9b14635f 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -94,7 +94,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (TargetType != null) { diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs new file mode 100644 index 0000000000..cee7e748ba --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -0,0 +1,132 @@ +using System; +using Avalonia.Controls; +using Avalonia.Styling; +using Avalonia.Styling.Activators; +using Xunit; + +namespace Avalonia.Base.UnitTests.Styling +{ + public class SelectorTests_Nesting + { + [Fact] + public void Parent_Selector_Doesnt_Match_OfType() + { + var control = new Control2(); + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("foo"))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); + } + + [Fact] + public void Nested_Class_Selector() + { + var control = new Control1 { Classes = { "foo" } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("foo"))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Clear(); + Assert.False(sink.Active); + } + + [Fact] + public void Nesting_With_No_Parent_Style_Fails() + { + var control = new Control1(); + var style = new Style(x => x.Nesting().OfType()); + + Assert.Throws(() => style.Selector.Match(control, null)); + } + + [Fact] + public void Nesting_With_No_Parent_Selector_Fails() + { + var control = new Control1(); + Style nested; + var parent = new Style + { + Children = + { + (nested = new Style(x => x.Nesting().Class("foo"))), + } + }; + + Assert.Throws(() => nested.Selector.Match(control, parent)); + } + + [Fact] + public void Nesting_Must_Appear_At_Start_Of_Selector() + { + var control = new Control1(); + Assert.Throws(() => new Style(x => x.OfType().Nesting())); + } + + [Fact] + public void Nesting_Must_Appear() + { + var control = new Control1(); + Style nested; + var parent = new Style + { + Children = + { + (nested = new Style(x => x.OfType().Class("foo"))), + } + }; + + Assert.Throws(() => nested.Selector.Match(control, parent)); + } + + [Fact] + public void Nesting_Must_Appear_In_All_Or_Arguments() + { + var control = new Control1(); + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Class("bar")))) + } + }; + + Assert.Throws(() => nested.Selector.Match(control, parent)); + } + + public class Control1 : Control + { + } + + public class Control2 : Control + { + } + + private class ActivatorSink : IStyleActivatorSink + { + public ActivatorSink(IStyleActivator source) => source.Subscribe(this); + public bool Active { get; private set; } + public void OnNext(bool value, int tag) => Active = value; + } + } +} From a506737a0f72ee1e56face79bfdbdd6e1bb0ffdd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 11:16:12 +0200 Subject: [PATCH 423/820] Parse nesting selector. --- .../Markup/Parsers/SelectorGrammar.cs | 30 +++- .../Parsers/SelectorGrammarTests.cs | 139 ++++++++++++++++++ 2 files changed, 162 insertions(+), 7 deletions(-) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index a9fc18474c..6562afc7ff 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -46,7 +46,7 @@ namespace Avalonia.Markup.Parsers switch (state) { case State.Start: - state = ParseStart(ref r); + (state, syntax) = ParseStart(ref r); break; case State.Middle: (state, syntax) = ParseMiddle(ref r, end); @@ -93,27 +93,31 @@ namespace Avalonia.Markup.Parsers return selector; } - private static State ParseStart(ref CharacterReader r) + private static (State, ISyntax?) ParseStart(ref CharacterReader r) { r.SkipWhitespace(); if (r.End) { - return State.End; + return (State.End, null); } if (r.TakeIf(':')) { - return State.Colon; + return (State.Colon, null); } else if (r.TakeIf('.')) { - return State.Class; + return (State.Class, null); } else if (r.TakeIf('#')) { - return State.Name; + return (State.Name, null); + } + else if (r.TakeIf('&')) + { + return (State.CanHaveType, new NestingSyntax()); } - return State.TypeName; + return (State.TypeName, null); } private static (State, ISyntax?) ParseMiddle(ref CharacterReader r, char? end) @@ -142,6 +146,10 @@ namespace Avalonia.Markup.Parsers { return (State.Start, new CommaSyntax()); } + else if (r.TakeIf('&')) + { + return (State.CanHaveType, new NestingSyntax()); + } else if (end.HasValue && !r.End && r.Peek == end.Value) { return (State.End, null); @@ -635,5 +643,13 @@ namespace Avalonia.Markup.Parsers return obj is CommaSyntax or; } } + + public class NestingSyntax : ISyntax + { + public override bool Equals(object? obj) + { + return obj is NestingSyntax; + } + } } } diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs index 6fbf024ff1..5c59101307 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs @@ -469,6 +469,145 @@ namespace Avalonia.Markup.UnitTests.Parsers result); } + [Fact] + public void Nesting_Class() + { + var result = SelectorGrammar.Parse("&.foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void Nesting_Child_Class() + { + var result = SelectorGrammar.Parse("& > .foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.ChildSyntax { }, + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void Nesting_Descendant_Class() + { + var result = SelectorGrammar.Parse("& .foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.DescendantSyntax { }, + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void Nesting_Template_Class() + { + var result = SelectorGrammar.Parse("& /template/ .foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.TemplateSyntax { }, + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void OfType_Template_Nesting() + { + var result = SelectorGrammar.Parse("Button /template/ &"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, + new SelectorGrammar.TemplateSyntax { }, + new SelectorGrammar.NestingSyntax(), + }, + result); + } + + [Fact] + public void Nesting_Property() + { + var result = SelectorGrammar.Parse("&[Foo=bar]"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.PropertySyntax { Property = "Foo", Value = "bar" }, + }, + result); + } + + [Fact] + public void Not_Nesting() + { + var result = SelectorGrammar.Parse(":not(&)"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NotSyntax + { + Argument = new[] { new SelectorGrammar.NestingSyntax() }, + } + }, + result); + } + + + [Fact] + public void Nesting_NthChild() + { + var result = SelectorGrammar.Parse("&:nth-child(2n+1)"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.NthChildSyntax() + { + Step = 2, + Offset = 1 + } + }, + result); + } + + [Fact] + public void Nesting_Comma_Nesting_Class() + { + var result = SelectorGrammar.Parse("&, &.foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.CommaSyntax(), + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + [Fact] public void Namespace_Alone_Fails() { From 3b24669d084c1801c3f94eb294d7e95ba2194bfc Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 17:27:39 +0800 Subject: [PATCH 424/820] Add missing props reference to sln --- Avalonia.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia.sln b/Avalonia.sln index e5baee7911..8e94eb4e63 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -113,6 +113,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\SharedVersion.props = build\SharedVersion.props build\SharpDX.props = build\SharpDX.props build\SkiaSharp.props = build\SkiaSharp.props + build\SourceGenerators.props = build\SourceGenerators.props build\SourceLink.props = build\SourceLink.props build\System.Drawing.Common.props = build\System.Drawing.Common.props build\System.Memory.props = build\System.Memory.props @@ -214,6 +215,7 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject Global From 7418fd7dc39aa91ea70dc4dcc2a4c5246ec71993 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:19:07 +0800 Subject: [PATCH 425/820] Apply code review feedbacks --- Avalonia.sln | 2 +- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/Input/KeyGesture.cs | 12 ++---------- .../Avalonia.DesignerSupport.csproj | 1 + .../Remote/HtmlTransport/HtmlTransport.cs | 14 ++------------ src/Shared/EnumParserHelper.cs | 19 +++++++++++++++++++ 6 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 src/Shared/EnumParserHelper.cs diff --git a/Avalonia.sln b/Avalonia.sln index 8e94eb4e63..1f59a94e2e 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -38,8 +38,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + src\Shared\EnumParserHelper.cs = src\Shared\EnumParserHelper.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs - src\Shared\RawEventGrouping.cs = src\Shared\RawEventGrouping.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection EndProject diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 8e4755b4b7..f0d54d831a 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 6f302a3ae7..d7fe89afda 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -155,11 +155,7 @@ namespace Avalonia.Input if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) return rv; -#if NETSTANDARD2_0 - return (Key)Enum.Parse(typeof(Key), key, true); -#else - return Enum.Parse(key, true); -#endif + return EnumParserHelper.ParseEnum(key, true); } private static KeyModifiers ParseModifier(ReadOnlySpan modifier) @@ -176,11 +172,7 @@ namespace Avalonia.Input return KeyModifiers.Meta; } -#if NETSTANDARD2_0 - return (KeyModifiers)Enum.Parse(typeof(KeyModifiers), modifier.ToString(), true); -#else - return Enum.Parse(modifier.ToString(), true); -#endif + return EnumParserHelper.ParseEnum(modifier.ToString(), true); } private Key ResolveNumPadOperationKey(Key key) diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 0270000d8c..66a7a6389e 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 7965e5d0d8..11a1efb9c7 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -320,23 +320,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') -#if NETSTANDARD2_0 - .Select(x => (InputProtocol.InputModifiers)Enum.Parse( - typeof(InputProtocol.InputModifiers), x, true)) -#else - .Select(x => Enum.Parse(x, true)) -#endif + .Select(x => EnumParserHelper.ParseEnum(x, true)) .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None -#if NETSTANDARD2_0 - : (InputProtocol.MouseButton)Enum.Parse( - typeof(InputProtocol.MouseButton), buttonText, true); -#else - : Enum.Parse(buttonText, true); -#endif + : EnumParserHelper.ParseEnum(buttonText, true); private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Shared/EnumParserHelper.cs b/src/Shared/EnumParserHelper.cs new file mode 100644 index 0000000000..09d6a0cb7d --- /dev/null +++ b/src/Shared/EnumParserHelper.cs @@ -0,0 +1,19 @@ +using System; + +namespace Avalonia +{ + internal class EnumParserHelper + { +#if NET6_0 + public static T ParseEnum(ReadOnlySpan key, bool ignoreCase) where T : struct + { + return Enum.Parse(key, ignoreCase); + } +#else + public static T ParseEnum(string key, bool ignoreCase) where T : struct + { + return (T)Enum.Parse(typeof(T), key, ignoreCase); + } +#endif + } +} From 67b18b8e3fdb7ed507dbae6f6834faff50a7d4ba Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:22:10 +0800 Subject: [PATCH 426/820] Rename EnumParserHelper --- Avalonia.sln | 2 +- src/Avalonia.Base/Avalonia.Base.csproj | 2 +- src/Avalonia.Base/Input/KeyGesture.cs | 4 ++-- .../Avalonia.DesignerSupport.csproj | 2 +- .../Remote/HtmlTransport/HtmlTransport.cs | 4 ++-- src/Shared/{EnumParserHelper.cs => EnumHelper.cs} | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) rename src/Shared/{EnumParserHelper.cs => EnumHelper.cs} (51%) diff --git a/Avalonia.sln b/Avalonia.sln index 1f59a94e2e..8a48566653 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -38,7 +38,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - src\Shared\EnumParserHelper.cs = src\Shared\EnumParserHelper.cs + src\Shared\EnumHelper.cs = src\Shared\EnumHelper.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index f0d54d831a..a9a606de8d 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index d7fe89afda..cb6d12ac0d 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -155,7 +155,7 @@ namespace Avalonia.Input if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) return rv; - return EnumParserHelper.ParseEnum(key, true); + return EnumHelper.Parse(key, true); } private static KeyModifiers ParseModifier(ReadOnlySpan modifier) @@ -172,7 +172,7 @@ namespace Avalonia.Input return KeyModifiers.Meta; } - return EnumParserHelper.ParseEnum(modifier.ToString(), true); + return EnumHelper.Parse(modifier.ToString(), true); } private Key ResolveNumPadOperationKey(Key key) diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 66a7a6389e..586d13f179 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 11a1efb9c7..794bc94256 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -320,13 +320,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') - .Select(x => EnumParserHelper.ParseEnum(x, true)) + .Select(x => EnumHelper.Parse(x, true)) .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None - : EnumParserHelper.ParseEnum(buttonText, true); + : EnumHelper.Parse(buttonText, true); private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Shared/EnumParserHelper.cs b/src/Shared/EnumHelper.cs similarity index 51% rename from src/Shared/EnumParserHelper.cs rename to src/Shared/EnumHelper.cs index 09d6a0cb7d..8511e712fa 100644 --- a/src/Shared/EnumParserHelper.cs +++ b/src/Shared/EnumHelper.cs @@ -2,15 +2,15 @@ namespace Avalonia { - internal class EnumParserHelper + internal class EnumHelper { #if NET6_0 - public static T ParseEnum(ReadOnlySpan key, bool ignoreCase) where T : struct + public static T Parse(ReadOnlySpan key, bool ignoreCase) where T : struct { return Enum.Parse(key, ignoreCase); } #else - public static T ParseEnum(string key, bool ignoreCase) where T : struct + public static T Parse(string key, bool ignoreCase) where T : struct { return (T)Enum.Parse(typeof(T), key, ignoreCase); } From 4f6da322fa4ac370d320f50a84aa9bcb5e5dd405 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:23:18 +0800 Subject: [PATCH 427/820] PrivateAssets --- build/SourceGenerators.props | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props index f9eab24d08..d000af1bf6 100644 --- a/build/SourceGenerators.props +++ b/build/SourceGenerators.props @@ -1,6 +1,10 @@ - + From d416b4de45413987330ef856035a02d07e177ad1 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:30:08 +0800 Subject: [PATCH 428/820] NET6_0 -> NET6_0_OR_GREATER --- src/Shared/EnumHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/EnumHelper.cs b/src/Shared/EnumHelper.cs index 8511e712fa..fddcf7ebc5 100644 --- a/src/Shared/EnumHelper.cs +++ b/src/Shared/EnumHelper.cs @@ -4,7 +4,7 @@ namespace Avalonia { internal class EnumHelper { -#if NET6_0 +#if NET6_0_OR_GREATER public static T Parse(ReadOnlySpan key, bool ignoreCase) where T : struct { return Enum.Parse(key, ignoreCase); From 578f5fc1cbd2abb42f85fb3bf798395721de6c57 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Fri, 15 Apr 2022 13:31:37 +0300 Subject: [PATCH 429/820] Fix PlatformSupport.UnitTests --- nukebuild/Build.cs | 1 + src/Avalonia.PlatformSupport/AssetLoader.cs | 8 ++++---- .../Avalonia.PlatformSupport.csproj | 2 +- .../Internal/AssemblyDescriptor.cs | 10 +++++++++- .../Internal/AssemblyDescriptorResolver.cs | 11 ++++++++--- .../AssetLoaderTests.cs | 6 +++--- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 9fcb9d6b7f..f0f677b844 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -221,6 +221,7 @@ partial class Build : NukeBuild RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); + RunCoreTest("Avalonia.PlatformSupport.UnitTests"); }); Target RunRenderTests => _ => _ diff --git a/src/Avalonia.PlatformSupport/AssetLoader.cs b/src/Avalonia.PlatformSupport/AssetLoader.cs index fb03ec2f6e..0e33c3d4c7 100644 --- a/src/Avalonia.PlatformSupport/AssetLoader.cs +++ b/src/Avalonia.PlatformSupport/AssetLoader.cs @@ -14,14 +14,14 @@ namespace Avalonia.PlatformSupport /// public class AssetLoader : IAssetLoader { - private static AssemblyDescriptorResolver s_assemblyDescriptorResolver = new(); + private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver(); private AssemblyDescriptor? _defaultResmAssembly; /// /// Introduced for tests. /// - internal static void SetAssemblyDescriptorResolver(AssemblyDescriptorResolver resolver) => + internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) => s_assemblyDescriptorResolver = resolver; /// @@ -182,13 +182,13 @@ namespace Avalonia.PlatformSupport throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri)); } - private (AssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) + private (IAssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) { var asm = s_assemblyDescriptorResolver.GetAssembly(uri.Authority); return (asm, uri.GetUnescapeAbsolutePath()); } - private AssemblyDescriptor? GetAssembly(Uri? uri) + private IAssemblyDescriptor? GetAssembly(Uri? uri) { if (uri != null) { diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj index 420ac0796c..5336f1e630 100644 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj @@ -19,6 +19,6 @@ - + diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs index a3de7f2b8a..64ffec8482 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs @@ -6,7 +6,15 @@ using Avalonia.Utilities; namespace Avalonia.PlatformSupport.Internal; -internal class AssemblyDescriptor +internal interface IAssemblyDescriptor +{ + Assembly Assembly { get; } + Dictionary? Resources { get; } + Dictionary? AvaloniaResources { get; } + string? Name { get; } +} + +internal class AssemblyDescriptor : IAssemblyDescriptor { public AssemblyDescriptor(Assembly assembly) { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs index a78051a9c4..28ae35d57d 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs @@ -5,11 +5,16 @@ using System.Reflection; namespace Avalonia.PlatformSupport.Internal; -internal class AssemblyDescriptorResolver +internal interface IAssemblyDescriptorResolver { - private readonly Dictionary _assemblyNameCache = new(); + IAssemblyDescriptor GetAssembly(string name); +} + +internal class AssemblyDescriptorResolver: IAssemblyDescriptorResolver +{ + private readonly Dictionary _assemblyNameCache = new(); - public AssemblyDescriptor GetAssembly(string name) + public IAssemblyDescriptor GetAssembly(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); diff --git a/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs b/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs index f950fb7e99..dfd195073b 100644 --- a/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs +++ b/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs @@ -16,7 +16,7 @@ public class AssetLoaderTests static AssetLoaderTests() { - var resolver = Mock.Of(); + var resolver = Mock.Of(); var descriptor = CreateAssemblyDescriptor(AssemblyNameWithWhitespace); Mock.Get(resolver).Setup(x => x.GetAssembly(AssemblyNameWithWhitespace)).Returns(descriptor); @@ -49,13 +49,13 @@ public class AssetLoaderTests Assert.Equal(AssemblyNameWithNonAscii, assemblyActual?.FullName); } - private static AssemblyDescriptor CreateAssemblyDescriptor(string assemblyName) + private static IAssemblyDescriptor CreateAssemblyDescriptor(string assemblyName) { var assembly = Mock.Of(); Mock.Get(assembly).Setup(x => x.GetName()).Returns(new AssemblyName(assemblyName)); Mock.Get(assembly).Setup(x => x.FullName).Returns(assemblyName); - var descriptor = Mock.Of(); + var descriptor = Mock.Of(); Mock.Get(descriptor).Setup(x => x.Assembly).Returns(assembly); return descriptor; } From a91bad4d3b7a9047c544876c8a4c8899980989a1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 13:55:57 +0200 Subject: [PATCH 430/820] Remove nesting selector validation. Needs to be added later. --- src/Avalonia.Base/Styling/Selector.cs | 13 +-- src/Avalonia.Base/Styling/Selectors.cs | 4 - src/Avalonia.Base/Styling/Style.cs | 12 +- .../Styling/SelectorTests_Nesting.cs | 105 ++++++++++++------ .../Styling/StyleTests.cs | 42 +++++++ 5 files changed, 130 insertions(+), 46 deletions(-) diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index 008ce7f3a5..6f7f754b7b 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -96,11 +96,7 @@ namespace Avalonia.Styling combinator = null; var activators = new AndActivatorBuilder(); - var foundNested = false; - var result = Match(control, start, parent, subscribe, ref activators, ref combinator, ref foundNested); - - if (parent is not null && !foundNested) - throw new InvalidOperationException("Nesting selector '&' must appear in child selector."); + var result = Match(control, start, parent, subscribe, ref activators, ref combinator); return result == SelectorMatchResult.Sometimes ? new SelectorMatch(activators.Get()) : @@ -113,8 +109,7 @@ namespace Avalonia.Styling IStyle? parent, bool subscribe, ref AndActivatorBuilder activators, - ref Selector? combinator, - ref bool foundNested) + ref Selector? combinator) { var previous = selector.MovePrevious(); @@ -123,7 +118,7 @@ namespace Avalonia.Styling // opportunity to exit early. if (previous != null && !previous.IsCombinator) { - var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator, ref foundNested); + var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator); if (previousMatch < SelectorMatchResult.Sometimes) { @@ -131,8 +126,6 @@ namespace Avalonia.Styling } } - foundNested |= selector is NestingSelector; - // Match this selector. var match = selector.Evaluate(control, parent, subscribe); diff --git a/src/Avalonia.Base/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs index a036c140c2..476d86cd11 100644 --- a/src/Avalonia.Base/Styling/Selectors.cs +++ b/src/Avalonia.Base/Styling/Selectors.cs @@ -111,10 +111,6 @@ namespace Avalonia.Styling public static Selector Nesting(this Selector? previous) { - if (previous is not null) - throw new InvalidOperationException( - "Nesting selector '&' must appear at the start of the style selector."); - return new NestingSelector(); } diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index a3a7ea29d3..e79678b20b 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -169,6 +169,16 @@ namespace Avalonia.Styling } } - internal void SetParent(Style? parent) => Parent = parent; + internal void SetParent(Style? parent) + { + if (parent?.Selector is not null) + { + if (Selector is null) + throw new InvalidOperationException("Nested styles must have a selector."); + // TODO: Validate that selector contains & in the right place. + } + + Parent = parent; + } } } diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index cee7e748ba..eeb2fad996 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -9,7 +9,7 @@ namespace Avalonia.Base.UnitTests.Styling public class SelectorTests_Nesting { [Fact] - public void Parent_Selector_Doesnt_Match_OfType() + public void Nesting_Class_Doesnt_Match_Parent_Selector() { var control = new Control2(); Style nested; @@ -26,43 +26,50 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Nested_Class_Selector() + public void Or_Nesting_Class_Doesnt_Match_Parent_Selector() { - var control = new Control1 { Classes = { "foo" } }; + var control = new Control2(); Style nested; var parent = new Style(x => x.OfType()) { Children = { - (nested = new Style(x => x.Nesting().Class("foo"))), + (nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Nesting().Class("bar")))), } }; var match = nested.Selector.Match(control, parent); - Assert.Equal(SelectorMatchResult.Sometimes, match.Result); - - var sink = new ActivatorSink(match.Activator); - - Assert.True(sink.Active); - control.Classes.Clear(); - Assert.False(sink.Active); + Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); } [Fact] - public void Nesting_With_No_Parent_Style_Fails() + public void Or_Nesting_Child_OfType_Does_Not_Match_Parent_Selector() { var control = new Control1(); - var style = new Style(x => x.Nesting().OfType()); + var panel = new DockPanel { Children = { control } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => Selectors.Or( + x.Nesting().Child().OfType(), + x.Nesting().Child().OfType()))), + } + }; - Assert.Throws(() => style.Selector.Match(control, null)); + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.NeverThisInstance, match.Result); } [Fact] - public void Nesting_With_No_Parent_Selector_Fails() + public void Nesting_Class_Matches() { - var control = new Control1(); + var control = new Control1 { Classes = { "foo" } }; Style nested; - var parent = new Style + var parent = new Style(x => x.OfType()) { Children = { @@ -70,44 +77,80 @@ namespace Avalonia.Base.UnitTests.Styling } }; - Assert.Throws(() => nested.Selector.Match(control, parent)); + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Clear(); + Assert.False(sink.Active); } [Fact] - public void Nesting_Must_Appear_At_Start_Of_Selector() + public void Or_Nesting_Class_Matches() { - var control = new Control1(); - Assert.Throws(() => new Style(x => x.OfType().Nesting())); + var control = new Control1 { Classes = { "foo" } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Nesting().Class("bar")))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Clear(); + Assert.False(sink.Active); } [Fact] - public void Nesting_Must_Appear() + public void Or_Nesting_Child_OfType_Matches() { - var control = new Control1(); + var control = new Control1 { Classes = { "foo" } }; + var panel = new Panel { Children = { control } }; Style nested; - var parent = new Style + var parent = new Style(x => x.OfType()) { Children = { - (nested = new Style(x => x.OfType().Class("foo"))), + (nested = new Style(x => Selectors.Or( + x.Nesting().Child().OfType(), + x.Nesting().Child().OfType()))), } }; - Assert.Throws(() => nested.Selector.Match(control, parent)); + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.AlwaysThisInstance, match.Result); } [Fact] - public void Nesting_Must_Appear_In_All_Or_Arguments() + public void Nesting_With_No_Parent_Style_Fails() + { + var control = new Control1(); + var style = new Style(x => x.Nesting().OfType()); + + Assert.Throws(() => style.Selector.Match(control, null)); + } + + [Fact] + public void Nesting_With_No_Parent_Selector_Fails() { var control = new Control1(); Style nested; - var parent = new Style(x => x.OfType()) + var parent = new Style { Children = { - (nested = new Style(x => Selectors.Or( - x.Nesting().Class("foo"), - x.Class("bar")))) + (nested = new Style(x => x.Nesting().Class("foo"))), } }; diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index 8dedf3471f..7aa86a1328 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -722,6 +722,48 @@ namespace Avalonia.Base.UnitTests.Styling resources.Verify(x => x.AddOwner(host.Object), Times.Once); } + [Fact] + public void Nested_Style_Can_Be_Added() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(x => x.Nesting().Class("foo")); + + parent.Children.Add(nested); + + Assert.Same(parent, nested.Parent); + } + + [Fact] + public void Nested_Or_Style_Can_Be_Added() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Nesting().Class("bar"))); + + parent.Children.Add(nested); + + Assert.Same(parent, nested.Parent); + } + + [Fact] + public void Nested_Style_Without_Selector_Throws() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(); + + Assert.Throws(() => parent.Children.Add(nested)); + } + + [Fact(Skip = "TODO")] + public void Nested_Style_Without_Nesting_Operator_Throws() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(x => x.Class("foo")); + + Assert.Throws(() => parent.Children.Add(nested)); + } + private class Class1 : Control { public static readonly StyledProperty FooProperty = From fd5e2169c143fb38b32edb1f2e2825bb2a35a513 Mon Sep 17 00:00:00 2001 From: Tako Date: Fri, 15 Apr 2022 14:56:02 +0300 Subject: [PATCH 431/820] reimplementation and some tests --- src/Avalonia.Controls/ComboBox.cs | 19 +---- src/Avalonia.Controls/ItemsControl.cs | 1 - src/Avalonia.Controls/StackPanel.cs | 2 +- .../ComboBoxTests.cs | 75 +++++++++++++++++++ 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 582f90e472..cbf9b35a05 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -455,24 +455,7 @@ namespace Avalonia.Controls { if (ItemCount >= 1) { - if (SelectedIndex == -1) - { - if (MoveSelection(NavigationDirection.First, WrapSelection) == false) - { - // MoveSelection works only with indexes starting from 0 - // so to make it search further than the first item we need to set SelectedIndex to 0. - SelectedIndex = 0; - var isSelectionMoved = MoveSelection(NavigationDirection.Next, WrapSelection); - if (isSelectionMoved == false) - { - SelectedIndex = -1; - } - } - } - else - { - MoveSelection(NavigationDirection.Next, WrapSelection); - } + MoveSelection(NavigationDirection.Next, WrapSelection); } } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 0cd72dc91c..afe4a08446 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -508,7 +508,6 @@ namespace Avalonia.Controls do { result = container.GetControl(direction, c, wrap); - from = from ?? result; if (result != null && result.Focusable && diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index feb425a9c3..50c48d2bb0 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -123,7 +123,7 @@ namespace Avalonia.Controls index = Children.Count - 1; break; case NavigationDirection.Next: - if (index != -1) ++index; + ++index; break; case NavigationDirection.Previous: if (index != -1) --index; diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index cb2fd11175..98695fe88e 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -36,6 +36,81 @@ namespace Avalonia.Controls.UnitTests Assert.False(target.IsDropDownOpen); } + [Fact] + public void WrapSelection_Should_Work() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var items = new[] + { + new ComboBoxItem() { Content = "bla" }, + new ComboBoxItem() { Content = "dd" }, + new ComboBoxItem() { Content = "sdf", IsEnabled = false } + }; + var target = new ComboBox + { + Items = items, + Template = GetTemplate(), + WrapSelection = true + }; + var root = new TestRoot(target); + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.Focus(); + Assert.Equal(target.SelectedIndex, -1); + Assert.True(target.IsFocused); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Up, + }); + Assert.Equal(target.SelectedIndex, 1); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down, + }); + Assert.Equal(target.SelectedIndex, 0); + } + } + + [Fact] + public void Focuses_Next_Item_On_Key_Down() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var items = new[] + { + new ComboBoxItem() { Content = "bla" }, + new ComboBoxItem() { Content = "dd", IsEnabled = false }, + new ComboBoxItem() { Content = "sdf" } + }; + var target = new ComboBox + { + Items = items, + Template = GetTemplate() + }; + var root = new TestRoot(target); + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.Focus(); + Assert.Equal(target.SelectedIndex, -1); + Assert.True(target.IsFocused); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down, + }); + Assert.Equal(target.SelectedIndex, 0); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down, + }); + Assert.Equal(target.SelectedIndex, 2); + } + } + [Fact] public void SelectionBoxItem_Is_Rectangle_With_VisualBrush_When_Selection_Is_Control() { From 646ce23bb4bdfdce57f9d9052e5904440e094dfa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 14:29:00 +0200 Subject: [PATCH 432/820] Initial XAML implementation of nested styles. --- src/Avalonia.Base/Styling/Style.cs | 8 +++++ .../AvaloniaXamlIlSelectorTransformer.cs | 24 ++++++++++++++ .../Parsers/SelectorGrammarTests.cs | 1 - .../Xaml/StyleTests.cs | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index e79678b20b..6020dfe25f 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -120,6 +120,14 @@ namespace Avalonia.Styling instance.Start(); } + if (_children is not null) + { + foreach (var child in _children) + { + child.TryAttach(target, host); + } + } + return match.Result; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index 4c4df1f53a..70209fb3ad 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -151,6 +151,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers results.Add(result); result = initialNode; break; + case SelectorGrammar.NestingSyntax: + var parentTargetType = context.ParentNodes().OfType().FirstOrDefault(); + + if (parentTargetType is null) + throw new XamlParseException($"Cannot find parent style for nested selector.", node); + + result = new XamlIlNestingSelector(result, parentTargetType.TargetType.GetClrType()); + break; default: throw new XamlParseException($"Unsupported selector grammar '{i.GetType()}'.", node); } @@ -474,4 +482,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers m => m.Name == "Or" && m.Parameters.Count == 1 && m.Parameters[0].Name.StartsWith("IReadOnlyList")); } } + + class XamlIlNestingSelector : XamlIlSelectorNode + { + public XamlIlNestingSelector(XamlIlSelectorNode previous, IXamlType targetType) + : base(previous) + { + TargetType = targetType; + } + + public override IXamlType TargetType { get; } + protected override void DoEmit(XamlEmitContext context, IXamlILEmitter codeGen) + { + EmitCall(context, codeGen, + m => m.Name == "Nesting" && m.Parameters.Count == 1); + } + } } diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs index 5c59101307..685f9eab6f 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs @@ -573,7 +573,6 @@ namespace Avalonia.Markup.UnitTests.Parsers result); } - [Fact] public void Nesting_NthChild() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs index 022ff0c3a4..9301647411 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs @@ -617,5 +617,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color); } } + + [Fact] + public void Can_Use_Nested_Styles() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var foo = window.FindControl("foo"); + + Assert.Null(foo.Background); + + foo.Classes.Add("foo"); + + Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color); + } + } } } From 2a2e4ca6db9651a334de740033eb507aea6f3c3b Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 23:53:10 +0800 Subject: [PATCH 433/820] Apply code review feedbacks --- Avalonia.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/Avalonia.sln b/Avalonia.sln index 8a48566653..1e2a3c6027 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -38,7 +38,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - src\Shared\EnumHelper.cs = src\Shared\EnumHelper.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection From be8bcd2be7bb31321af83a6c0ba99f87a07d67cc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 15 Apr 2022 12:06:45 -0400 Subject: [PATCH 434/820] Update src/Avalonia.Base/Input/PointerOverPreProcessor.cs --- src/Avalonia.Base/Input/PointerOverPreProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs index a38364197d..d22252893d 100644 --- a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs +++ b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs @@ -60,7 +60,6 @@ namespace Avalonia.Input public void SceneInvalidated(Rect dirtyRect) { - // Pointer is outside of the target area if (_lastPointer is (var pointer, var position)) { var clientPoint = _inputRoot.PointToClient(position); From 7530e20be04366fd621bca9650faba41294cdbb7 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 00:21:42 +0800 Subject: [PATCH 435/820] Move EnumHelper to Avalonia.Base --- src/Avalonia.Base/Avalonia.Base.csproj | 1 - src/Avalonia.Base/Input/KeyGesture.cs | 1 + src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 + src/{Shared => Avalonia.Base/Utilities}/EnumHelper.cs | 2 +- src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj | 1 - .../Remote/HtmlTransport/HtmlTransport.cs | 1 + 6 files changed, 4 insertions(+), 3 deletions(-) rename src/{Shared => Avalonia.Base/Utilities}/EnumHelper.cs (93%) diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index a9a606de8d..8e4755b4b7 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -7,7 +7,6 @@ - diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index cb6d12ac0d..3b7a828b86 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using Avalonia.Utilities; namespace Avalonia.Input { diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 4d7f487e00..a0560924e7 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -21,6 +21,7 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Shared/EnumHelper.cs b/src/Avalonia.Base/Utilities/EnumHelper.cs similarity index 93% rename from src/Shared/EnumHelper.cs rename to src/Avalonia.Base/Utilities/EnumHelper.cs index fddcf7ebc5..c857033ef1 100644 --- a/src/Shared/EnumHelper.cs +++ b/src/Avalonia.Base/Utilities/EnumHelper.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia +namespace Avalonia.Utilities { internal class EnumHelper { diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 586d13f179..0270000d8c 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -19,7 +19,6 @@ - diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 794bc94256..f100be5d5b 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Viewport; +using Avalonia.Utilities; using InputProtocol = Avalonia.Remote.Protocol.Input; namespace Avalonia.DesignerSupport.Remote.HtmlTransport From 43a3841dcbdc224c528f95c9f25eb585b141c8fc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 18:02:43 +0200 Subject: [PATCH 436/820] Add/remove resource owner. --- src/Avalonia.Base/Styling/StyleChildren.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Styling/StyleChildren.cs b/src/Avalonia.Base/Styling/StyleChildren.cs index 21ac3c4072..64d838e4ce 100644 --- a/src/Avalonia.Base/Styling/StyleChildren.cs +++ b/src/Avalonia.Base/Styling/StyleChildren.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using Avalonia.Controls; namespace Avalonia.Styling { @@ -16,7 +17,10 @@ namespace Avalonia.Styling protected override void RemoveItem(int index) { - (Items[index] as Style)?.SetParent(null); + var item = Items[index]; + if (_owner.Owner is IResourceHost host) + (item as IResourceProvider)?.RemoveOwner(host); + (item as Style)?.SetParent(null); base.RemoveItem(index); } @@ -24,6 +28,8 @@ namespace Avalonia.Styling { base.SetItem(index, item); (item as Style)?.SetParent(_owner); + if (_owner.Owner is IResourceHost host) + (item as IResourceProvider)?.AddOwner(host); } } } From 37f678e96a7b35c2d0c79d6871d5e06fd540948b Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:05:10 -0400 Subject: [PATCH 437/820] Fix missing color alpha rounding --- src/Avalonia.Base/Media/Color.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index eaa886ccbd..9ad75b3290 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -375,7 +375,7 @@ namespace Avalonia.Media byte.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out byte blue) && TryInternalParse(components[3], out double alpha)) { - color = new Color((byte)(alpha * 255), red, green, blue); + color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue); return true; } } From c7e0a68f2779361ac23bf13003860ed5634c8278 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:07:01 -0400 Subject: [PATCH 438/820] Support RGB component percentages in CSS format parsing --- src/Avalonia.Base/Media/Color.cs | 43 +++++++++++++++---- .../Media/ColorTests.cs | 24 ++++++++--- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 9ad75b3290..2f0def58a4 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -360,9 +360,9 @@ namespace Avalonia.Media if (components.Length == 3) // RGB { - if (byte.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out byte red) && - byte.TryParse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture, out byte green) && - byte.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out byte blue)) + if (InternalTryParseByte(components[0], out byte red) && + InternalTryParseByte(components[1], out byte green) && + InternalTryParseByte(components[2], out byte blue)) { color = new Color(0xFF, red, green, blue); return true; @@ -370,18 +370,45 @@ namespace Avalonia.Media } else if (components.Length == 4) // RGBA { - if (byte.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out byte red) && - byte.TryParse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture, out byte green) && - byte.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out byte blue) && - TryInternalParse(components[3], out double alpha)) + if (InternalTryParseByte(components[0], out byte red) && + InternalTryParseByte(components[1], out byte green) && + InternalTryParseByte(components[2], out byte blue) && + InternalTryParseDouble(components[3], out double alpha)) { color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue); return true; } } + // Local function to specially parse a byte value with an optional percentage sign + bool InternalTryParseByte(string inString, out byte outByte) + { + // The percent sign, if it exists, must be at the end of the number + int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); + + if (percentIndex >= 0) + { + var result = double.TryParse( + inString.Substring(0, percentIndex), + NumberStyles.Number, + CultureInfo.InvariantCulture, + out double percentage); + + outByte = (byte)Math.Round((percentage / 100.0) * 255.0); + return result; + } + else + { + return byte.TryParse( + inString, + NumberStyles.Number, + CultureInfo.InvariantCulture, + out outByte); + } + } + // Local function to specially parse a double value with an optional percentage sign - bool TryInternalParse(string inString, out double outDouble) + bool InternalTryParseDouble(string inString, out double outDouble) { // The percent sign, if it exists, must be at the end of the number int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); diff --git a/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs index 1392635b32..36929d5e95 100644 --- a/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs @@ -216,8 +216,8 @@ namespace Avalonia.Base.UnitTests.Media Tuple.Create("hsl(-1000, -1000, -1000)", new HslColor(1, 0, 0, 0)), // Clamps to min Tuple.Create("hsl(-1000, -1000%, -1000%)", new HslColor(1, 0, 0, 0)), // Clamps to min - Tuple.Create("hsl(1000, 1000, 1000)", new HslColor(1, 0, 1, 1)), // Clamps to max - Tuple.Create("hsl(1000, 1000%, 1000%)", new HslColor(1, 0, 1, 1)), // Clamps to max + Tuple.Create("hsl(1000, 1000, 1000)", new HslColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) + Tuple.Create("hsl(1000, 1000%, 1000%)", new HslColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) Tuple.Create("hsl(300, 0.8, 0.2)", new HslColor(1.0, 300, 0.8, 0.2)), Tuple.Create("hsl(300, 80%, 20%)", new HslColor(1.0, 300, 0.8, 0.2)), @@ -262,8 +262,8 @@ namespace Avalonia.Base.UnitTests.Media Tuple.Create("hsv(-1000, -1000, -1000)", new HsvColor(1, 0, 0, 0)), // Clamps to min Tuple.Create("hsv(-1000, -1000%, -1000%)", new HsvColor(1, 0, 0, 0)), // Clamps to min - Tuple.Create("hsv(1000, 1000, 1000)", new HsvColor(1, 0, 1, 1)), // Clamps to max - Tuple.Create("hsv(1000, 1000%, 1000%)", new HsvColor(1, 0, 1, 1)), // Clamps to max + Tuple.Create("hsv(1000, 1000, 1000)", new HsvColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) + Tuple.Create("hsv(1000, 1000%, 1000%)", new HsvColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) Tuple.Create("hsv(300, 0.8, 0.2)", new HsvColor(1.0, 300, 0.8, 0.2)), Tuple.Create("hsv(300, 80%, 20%)", new HsvColor(1.0, 300, 0.8, 0.2)), @@ -303,8 +303,20 @@ namespace Avalonia.Base.UnitTests.Media Tuple.Create("#123456", new Color(0xff, 0x12, 0x34, 0x56)), Tuple.Create("rgb(100, 30, 45)", new Color(255, 100, 30, 45)), - Tuple.Create("rgba(100, 30, 45, 0.9)", new Color(229, 100, 30, 45)), - Tuple.Create("rgba(100, 30, 45, 90%)", new Color(229, 100, 30, 45)), + Tuple.Create("rgba(100, 30, 45, 0.9)", new Color(230, 100, 30, 45)), + Tuple.Create("rgba(100, 30, 45, 90%)", new Color(230, 100, 30, 45)), + + Tuple.Create("rgb(255,0,0)", new Color(255, 255, 0, 0)), + Tuple.Create("rgb(0,255,0)", new Color(255, 0, 255, 0)), + Tuple.Create("rgb(0,0,255)", new Color(255, 0, 0, 255)), + + Tuple.Create("rgb(100%, 0, 0)", new Color(255, 255, 0, 0)), + Tuple.Create("rgb(0, 100%, 0)", new Color(255, 0, 255, 0)), + Tuple.Create("rgb(0, 0, 100%)", new Color(255, 0, 0, 255)), + + Tuple.Create("rgba(0, 0, 100%, 50%)", new Color(128, 0, 0, 255)), + Tuple.Create("rgba(50%, 10%, 80%, 50%)", new Color(128, 128, 26, 204)), + Tuple.Create("rgba(50%, 10%, 80%, 0.5)", new Color(128, 128, 26, 204)), // HSL Tuple.Create("hsl(296, 85%, 12%)", new Color(255, 53, 5, 57)), From 467f78b58d3ee2554a79df40693e260cd736d3ca Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:16:17 -0400 Subject: [PATCH 439/820] Improve color format length checking for performance --- src/Avalonia.Base/Media/Color.cs | 22 ++++++++++++++-------- src/Avalonia.Base/Media/HslColor.cs | 11 +++++++++-- src/Avalonia.Base/Media/HsvColor.cs | 11 +++++++++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 2f0def58a4..dc22f87d49 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -166,7 +166,10 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + // Note: The length checks are also an important optimization. + // The shortest possible CSS format is "rbg(0,0,0)", Length = 10. + + if (s.Length >= 10 && (s[0] == 'r' || s[0] == 'R') && (s[1] == 'g' || s[1] == 'G') && (s[2] == 'b' || s[2] == 'B') && @@ -175,7 +178,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'l' || s[2] == 'L') && @@ -185,7 +188,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'v' || s[2] == 'V') && @@ -229,7 +232,10 @@ namespace Avalonia.Media // At this point all parsing uses strings var str = s.ToString(); - if (s.Length > 5 && + // Note: The length checks are also an important optimization. + // The shortest possible CSS format is "rbg(0,0,0)", Length = 10. + + if (s.Length >= 10 && (s[0] == 'r' || s[0] == 'R') && (s[1] == 'g' || s[1] == 'G') && (s[2] == 'b' || s[2] == 'B') && @@ -238,7 +244,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'l' || s[2] == 'L') && @@ -248,7 +254,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'v' || s[2] == 'V') && @@ -342,14 +348,14 @@ namespace Avalonia.Media return false; } - if (workingString.Length > 6 && + if (workingString.Length >= 11 && workingString.StartsWith("rgba(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.Length > 5 && + if (workingString.Length >= 10 && workingString.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index e27a4f3106..622d3ac2b7 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -241,19 +241,26 @@ namespace Avalonia.Media return false; } - if (workingString.Length > 6 && + // Note: The length checks are also an important optimization. + // The shortest possible format is "hsl(0,0,0)", Length = 10. + + if (workingString.Length >= 11 && workingString.StartsWith("hsla(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.Length > 5 && + if (workingString.Length >= 10 && workingString.StartsWith("hsl(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); } + else + { + return false; + } string[] components = workingString.Split(','); diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 164aeb1df1..1e718b5739 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -241,19 +241,26 @@ namespace Avalonia.Media return false; } - if (workingString.Length > 6 && + // Note: The length checks are also an important optimization. + // The shortest possible format is "hsv(0,0,0)", Length = 10. + + if (workingString.Length >= 11 && workingString.StartsWith("hsva(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.Length > 5 && + if (workingString.Length >= 10 && workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); } + else + { + return false; + } string[] components = workingString.Split(','); From 02e7e8702ed3e9866701755edb0d4814da7ac88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Sat, 16 Apr 2022 01:24:32 +0200 Subject: [PATCH 440/820] Allow for parsing relative points during compile time. (#7987) * Allow for parsing relative points during compile time. * Revert extra target framework. --- src/Avalonia.Base/RelativePoint.cs | 15 +++++++++--- .../Avalonia.Build.Tasks.csproj | 3 +++ .../Properties/launchSettings.json | 10 ++++++++ .../AvaloniaXamlIlLanguageParseIntrinsics.cs | 23 +++++++++++++++++++ .../AvaloniaXamlIlWellKnownTypes.cs | 7 ++++++ 5 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Build.Tasks/Properties/launchSettings.json diff --git a/src/Avalonia.Base/RelativePoint.cs b/src/Avalonia.Base/RelativePoint.cs index 4550dbd54b..e1fd0093b6 100644 --- a/src/Avalonia.Base/RelativePoint.cs +++ b/src/Avalonia.Base/RelativePoint.cs @@ -1,7 +1,8 @@ using System; using System.Globalization; - +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -10,7 +11,10 @@ namespace Avalonia /// Defines the reference point units of an or /// . /// - public enum RelativeUnit +#if !BUILDTASK + public +#endif + enum RelativeUnit { /// /// The point is expressed as a fraction of the containing element's size. @@ -26,7 +30,10 @@ namespace Avalonia /// /// Defines a point that may be defined relative to a containing element. /// - public readonly struct RelativePoint : IEquatable +#if !BUILDTASK + public +#endif + readonly struct RelativePoint : IEquatable { /// /// A point at the top left of the containing element. @@ -49,7 +56,9 @@ namespace Avalonia static RelativePoint() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(RelativePoint).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 6267c74df9..e9b99c9aa8 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -95,6 +95,9 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Build.Tasks/Properties/launchSettings.json b/src/Avalonia.Build.Tasks/Properties/launchSettings.json new file mode 100644 index 0000000000..e9f5af46d6 --- /dev/null +++ b/src/Avalonia.Build.Tasks/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Compile Sandbox": { + "commandName": "Project", + "executablePath": "$(SolutionDir)\\src\\Avalonia.Build.Tasks\\bin\\Debug\\net6.0\\Avalonia.Build.Tasks.exe", + "commandLineArgs": "$(SolutionDir)\\samples\\Sandbox\\obj\\Debug\\net6.0\\Avalonia\\original.dll $(SolutionDir)\\samples\\Sandbox\\bin\\Debug\\net6.0\\Sandbox.dll.refs $(SolutionDir)\\out.dll" + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index 88529ae3a0..d907bcbef9 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -160,6 +160,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return true; } + if (type.Equals(types.RelativePoint)) + { + try + { + var relativePoint = RelativePoint.Parse(text); + + var relativePointTypeRef = new XamlAstClrTypeReference(node, types.RelativePoint, false); + + result = new XamlAstNewClrObjectNode(node, relativePointTypeRef, types.RelativePointFullConstructor, new List + { + new XamlConstantNode(node, types.XamlIlTypes.Double, relativePoint.Point.X), + new XamlConstantNode(node, types.XamlIlTypes.Double, relativePoint.Point.Y), + new XamlConstantNode(node, types.RelativeUnit, (int) relativePoint.Unit), + }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a relative point", node); + } + } + if (type.Equals(types.GridLength)) { try diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 99072ace02..76f3cc071f 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -71,6 +71,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlConstructor MatrixFullConstructor { get; } public IXamlType CornerRadius { get; } public IXamlConstructor CornerRadiusFullConstructor { get; } + public IXamlType RelativeUnit { get; } + public IXamlType RelativePoint { get; } + public IXamlConstructor RelativePointFullConstructor { get; } public IXamlType GridLength { get; } public IXamlConstructor GridLengthConstructorValueType { get; } public IXamlType Color { get; } @@ -175,6 +178,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers (Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); (CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); + RelativeUnit = cfg.TypeSystem.GetType("Avalonia.RelativeUnit"); + RelativePoint = cfg.TypeSystem.GetType("Avalonia.RelativePoint"); + RelativePointFullConstructor = RelativePoint.GetConstructor(new List { XamlIlTypes.Double, XamlIlTypes.Double, RelativeUnit }); + GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength"); GridLengthConstructorValueType = GridLength.GetConstructor(new List { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") }); Color = cfg.TypeSystem.GetType("Avalonia.Media.Color"); From 024dd47943e2b3c2461be965e8c102ff7fa87339 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:39:05 -0400 Subject: [PATCH 441/820] Improve color comments --- src/Avalonia.Base/Media/Color.cs | 6 +++++ src/Avalonia.Base/Media/HslColor.cs | 38 ++++++++++++++++++++++++++--- src/Avalonia.Base/Media/HsvColor.cs | 38 ++++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index dc22f87d49..208e359f80 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -277,6 +277,9 @@ namespace Avalonia.Media return false; } + /// + /// Parses the given span of characters representing a hex color value into a new . + /// private static bool TryParseHexFormat(ReadOnlySpan s, out Color color) { static bool TryParseCore(ReadOnlySpan input, ref Color color) @@ -331,6 +334,9 @@ namespace Avalonia.Media return TryParseCore(input, ref color); } + /// + /// Parses the given string representing a CSS color value into a new . + /// private static bool TryParseCssFormat(string s, out Color color) { color = default; diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index 622d3ac2b7..cd98c72e3f 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -12,6 +12,7 @@ namespace Avalonia.Media { /// /// Defines a color using the hue/saturation/lightness (HSL) model. + /// This uses a cylindrical-coordinate representation of a color. /// #if !BUILDTASK public @@ -98,24 +99,53 @@ namespace Avalonia.Media } /// - /// Gets the Alpha (transparency) component in the range from 0..1. + /// Gets the Alpha (transparency) component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully transparent. + /// 1 is fully opaque. + /// + /// public double A { get; } /// - /// Gets the Hue component in the range from 0..360. + /// Gets the Hue component in the range from 0..360 (degrees). + /// This is the color's location, in degrees, on a color wheel/circle from 0 to 360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. /// + /// + /// + /// 0/360 degrees is Red. + /// 60 degrees is Yellow. + /// 120 degrees is Green. + /// 180 degrees is Cyan. + /// 240 degrees is Blue. + /// 300 degrees is Magenta. + /// + /// public double H { get; } /// - /// Gets the Saturation component in the range from 0..1. + /// Gets the Saturation component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is a shade of gray (no color). + /// 1 is the full color. + /// + /// public double S { get; } /// - /// Gets the Lightness component in the range from 0..1. + /// Gets the Lightness component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully black. + /// 1 is fully white. + /// + /// public double L { get; } /// diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 1e718b5739..1ef0bcc742 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -12,6 +12,7 @@ namespace Avalonia.Media { /// /// Defines a color using the hue/saturation/value (HSV) model. + /// This uses a cylindrical-coordinate representation of a color. /// #if !BUILDTASK public @@ -98,24 +99,53 @@ namespace Avalonia.Media } /// - /// Gets the Alpha (transparency) component in the range from 0..1. + /// Gets the Alpha (transparency) component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully transparent. + /// 1 is fully opaque. + /// + /// public double A { get; } /// - /// Gets the Hue component in the range from 0..360. + /// Gets the Hue component in the range from 0..360 (degrees). + /// This is the color's location, in degrees, on a color wheel/circle from 0 to 360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. /// + /// + /// + /// 0/360 degrees is Red. + /// 60 degrees is Yellow. + /// 120 degrees is Green. + /// 180 degrees is Cyan. + /// 240 degrees is Blue. + /// 300 degrees is Magenta. + /// + /// public double H { get; } /// - /// Gets the Saturation component in the range from 0..1. + /// Gets the Saturation component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is a shade of gray (no color). + /// 1 is the full color. + /// + /// public double S { get; } /// - /// Gets the Value component in the range from 0..1. + /// Gets the Value (or Brightness/Intensity) component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully black and shows no color. + /// 1 is the brightest and shows full color. + /// + /// public double V { get; } /// From a7dc94b3cde14fa5faa8b5d38f2a07341a7702c3 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:54:11 -0400 Subject: [PATCH 442/820] More strictly match color prefixes --- src/Avalonia.Base/Media/Color.cs | 12 +++++++++++- src/Avalonia.Base/Media/HslColor.cs | 10 ++++++++-- src/Avalonia.Base/Media/HsvColor.cs | 10 ++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 208e359f80..cb90404f6d 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -339,6 +339,8 @@ namespace Avalonia.Media /// private static bool TryParseCssFormat(string s, out Color color) { + bool prefixMatched = false; + color = default; if (s is null) @@ -359,13 +361,21 @@ namespace Avalonia.Media workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); + prefixMatched = true; } - if (workingString.Length >= 10 && + if (prefixMatched == false && + workingString.Length >= 10 && workingString.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); + prefixMatched = true; + } + + if (prefixMatched == false) + { + return false; } string[] components = workingString.Split(','); diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index cd98c72e3f..e8a4d6f94f 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -256,6 +256,8 @@ namespace Avalonia.Media /// True if parsing was successful; otherwise, false. public static bool TryParse(string s, out HslColor hslColor) { + bool prefixMatched = false; + hslColor = default; if (s is null) @@ -279,15 +281,19 @@ namespace Avalonia.Media workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); + prefixMatched = true; } - if (workingString.Length >= 10 && + if (prefixMatched == false && + workingString.Length >= 10 && workingString.StartsWith("hsl(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); + prefixMatched = true; } - else + + if (prefixMatched == false) { return false; } diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 1ef0bcc742..924ef4778b 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -256,6 +256,8 @@ namespace Avalonia.Media /// True if parsing was successful; otherwise, false. public static bool TryParse(string s, out HsvColor hsvColor) { + bool prefixMatched = false; + hsvColor = default; if (s is null) @@ -279,15 +281,19 @@ namespace Avalonia.Media workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); + prefixMatched = true; } - if (workingString.Length >= 10 && + if (prefixMatched == false && + workingString.Length >= 10 && workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); + prefixMatched = true; } - else + + if (prefixMatched == false) { return false; } From 248b3c87c34fb5679a8cb120211ed94066081bfa Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 21:15:08 -0400 Subject: [PATCH 443/820] Use more standard x:Double resource --- src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 592581e975..6f7b14ce25 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -18,7 +18,7 @@ - 12 + 12 32 From 53fc90a108ef8216e3b54d50f289e9dc3fe3f9c9 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 21:16:26 -0400 Subject: [PATCH 444/820] Remove Microsoft copyright An audit of git blame shows the style has now been fully rewritten for Avalonia --- .../Controls/CalendarDatePicker.xaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 6f7b14ce25..c6f70d05e1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -1,10 +1,3 @@ - - Date: Fri, 15 Apr 2022 21:37:23 -0400 Subject: [PATCH 445/820] Add TemplatePart attributes for DataGrid controls --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 8 ++++++++ src/Avalonia.Controls.DataGrid/DataGridCell.cs | 1 + src/Avalonia.Controls.DataGrid/DataGridRow.cs | 5 +++++ src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs | 6 ++++++ src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs | 1 + 5 files changed, 21 insertions(+) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 9b67c9b096..aaac3f8f9c 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -32,6 +32,14 @@ namespace Avalonia.Controls /// /// Displays data in a customizable grid. /// + [TemplatePart(DATAGRID_elementBottomRightCornerHeaderName, typeof(IVisual))] + [TemplatePart(DATAGRID_elementColumnHeadersPresenterName, typeof(DataGridColumnHeadersPresenter))] + [TemplatePart(DATAGRID_elementFrozenColumnScrollBarSpacerName, typeof(Control))] + [TemplatePart(DATAGRID_elementHorizontalScrollbarName, typeof(ScrollBar))] + [TemplatePart(DATAGRID_elementRowsPresenterName, typeof(DataGridRowsPresenter))] + [TemplatePart(DATAGRID_elementTopLeftCornerHeaderName, typeof(ContentControl))] + [TemplatePart(DATAGRID_elementTopRightCornerHeaderName, typeof(ContentControl))] + [TemplatePart(DATAGRID_elementVerticalScrollbarName, typeof(ScrollBar))] [PseudoClasses(":invalid", ":empty-rows", ":empty-columns")] public partial class DataGrid : TemplatedControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs index e3f150f5c4..67183781d3 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs @@ -13,6 +13,7 @@ namespace Avalonia.Controls /// /// Represents an individual cell. /// + [TemplatePart(DATAGRIDCELL_elementRightGridLine, typeof(Rectangle))] [PseudoClasses(":selected", ":current", ":edited", ":invalid")] public class DataGridCell : ContentControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index a6faec752d..db5d428942 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -21,6 +21,11 @@ namespace Avalonia.Controls /// /// Represents a row. /// + [TemplatePart(DATAGRIDROW_elementBottomGridLine, typeof(Rectangle))] + [TemplatePart(DATAGRIDROW_elementCells, typeof(DataGridCellsPresenter))] + [TemplatePart(DATAGRIDROW_elementDetails, typeof(DataGridDetailsPresenter))] + [TemplatePart(DATAGRIDROW_elementRoot, typeof(Panel))] + [TemplatePart(DATAGRIDROW_elementRowHeader, typeof(DataGridRowHeader))] [PseudoClasses(":selected", ":editing", ":invalid")] public class DataGridRow : TemplatedControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs index 49ca23d34c..a3dfa44fc9 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs @@ -14,6 +14,12 @@ using System.Reactive.Linq; namespace Avalonia.Controls { + [TemplatePart(DATAGRIDROWGROUPHEADER_expanderButton, typeof(ToggleButton))] + [TemplatePart(DATAGRIDROWGROUPHEADER_indentSpacer, typeof(Control))] + [TemplatePart(DATAGRIDROWGROUPHEADER_itemCountElement, typeof(TextBlock))] + [TemplatePart(DATAGRIDROWGROUPHEADER_propertyNameElement, typeof(TextBlock))] + [TemplatePart(DataGridRow.DATAGRIDROW_elementRoot, typeof(Panel))] + [TemplatePart(DataGridRow.DATAGRIDROW_elementRowHeader, typeof(DataGridRowHeader))] [PseudoClasses(":pressed", ":current", ":expanded")] public class DataGridRowGroupHeader : TemplatedControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs index 510072174f..03299bbf35 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs @@ -13,6 +13,7 @@ namespace Avalonia.Controls.Primitives /// /// Represents an individual row header. /// + [TemplatePart(DATAGRIDROWHEADER_elementRootName, typeof(Control))] [PseudoClasses(":invalid", ":selected", ":editing", ":current")] public class DataGridRowHeader : ContentControl { From 18e7a11a023b5283373303030b353a2fed8bb1c9 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:41:16 +0800 Subject: [PATCH 446/820] Implement IncrementalGenerator --- src/Avalonia.Base/Animation/Easings/Easing.cs | 11 +- .../IsExternalInit.cs | 14 ++ .../SubtypesFactoryGenerator.cs | 234 +++++++++--------- 3 files changed, 131 insertions(+), 128 deletions(-) create mode 100644 src/Avalonia.SourceGenerator/IsExternalInit.cs diff --git a/src/Avalonia.Base/Animation/Easings/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs index 6dfc4c86b3..d4f817ccc3 100644 --- a/src/Avalonia.Base/Animation/Easings/Easing.cs +++ b/src/Avalonia.Base/Animation/Easings/Easing.cs @@ -37,14 +37,9 @@ namespace Avalonia.Animation.Easings return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture)); } - if (TryCreateEasingInstance(e, out var easing)) - { - return easing; - } - else - { - throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace."); - } + return TryCreateEasingInstance(e, out var easing) + ? easing + : throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace."); } } } diff --git a/src/Avalonia.SourceGenerator/IsExternalInit.cs b/src/Avalonia.SourceGenerator/IsExternalInit.cs new file mode 100644 index 0000000000..c6ddf762ad --- /dev/null +++ b/src/Avalonia.SourceGenerator/IsExternalInit.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [ExcludeFromCodeCoverage, DebuggerNonUserCode] + internal static class IsExternalInit + { + } +} diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index 7bc240853e..6ba1f6ac18 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -1,140 +1,48 @@ -using System.Collections.Generic; +using System; +using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Avalonia.SourceGenerator { - internal class GenerateSubtypesSyntaxReceiver : ISyntaxReceiver + [Generator(LanguageNames.CSharp)] + public class SubtypesFactoryGenerator : IIncrementalGenerator { - public List<(MethodDeclarationSyntax, AttributeSyntax)> CandidateMethods { get; } = new(); - public List Types { get; } = new(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is MethodDeclarationSyntax declarationSyntax) - { - foreach (var attribute in declarationSyntax.AttributeLists.SelectMany(i => i.Attributes)) - { - CandidateMethods.Add((declarationSyntax, attribute)); - } - } - - if (syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax) - { - Types.Add(syntaxNode); - } - } - } - - [Generator] - internal class SubtypesFactoryGenerator : ISourceGenerator - { - private readonly GenerateSubtypesSyntaxReceiver _receiver = new(); + private record MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; - public void Execute(GeneratorExecutionContext context) + private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) { - var methods = new List<(IMethodSymbol, ITypeSymbol, string)>(); - - foreach (var (method, attribute) in _receiver.CandidateMethods) - { - var semanticModel = context.Compilation.GetSemanticModel(method.SyntaxTree); - var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); - if (attributeTypeInfo.Type is null || - attributeTypeInfo.Type.ToString() != s_attributeName || - attribute.ArgumentList is null) - { - continue; - } - - var arguments = attribute.ArgumentList.Arguments; - if (arguments.Count != 2) - { - continue; - } - - if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || - arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) - { - continue; - } - - var type = semanticModel.GetTypeInfo(typeOfExpr.Type); - var ns = semanticModel.GetConstantValue(arguments[1].Expression); - var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); - - if (type.Type is not ITypeSymbol baseType || - ns.HasValue is false || - ns.Value is not string nsValue || - methodDeclInfo is not IMethodSymbol methodSymbol || - methodSymbol.Parameters.Length != 2 || - methodSymbol.Parameters[1].RefKind != RefKind.Out) - { - continue; - } - - methods.Add((methodSymbol, baseType, nsValue)); - } - - var types = new List(); - foreach (var type in _receiver.Types) - { - var semanticModel = context.Compilation.GetSemanticModel(type.SyntaxTree); - var decl = semanticModel.GetDeclaredSymbol(type); - if (decl is ITypeSymbol typeSymbol) - { - types.Add(typeSymbol); - } - } - - GenerateSubTypes(context, methods, types); + return type.BaseType is not null && (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType) || IsSubtypeOf(type.BaseType, baseType)); } - private bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) + private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray types) { - if (type.BaseType is null) - { - return false; - } + var (method, baseType, @namespace) = methodTarget; + var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); + var type = method.ContainingType; + var isGeneric = type.TypeParameters.Length > 0; + var isClass = type.TypeKind == TypeKind.Class; - if (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType)) + if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) { - return true; + return; } - return IsSubtypeOf(type.BaseType, baseType); - } + var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); - private void GenerateSubTypes( - GeneratorExecutionContext context, - List<(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace)> methods, - List types) - { - foreach (var (method, baseType, @namespace) in methods) - { - var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); - var type = method.ContainingType; - var isGeneric = type.TypeParameters.Length > 0; - var isClass = type.TypeKind == TypeKind.Class; - - if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) - { - continue; - } + var methodDeclText = methodDecl + .WithAttributeLists(new SyntaxList()) + .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); - var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); - - var methodDeclText = methodDecl - .WithAttributeLists(new SyntaxList()) - .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) - .WithBody(null) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) - .WithoutTrivia().ToString(); - - var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; - var source = $@"using System; + var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; + var source = $@"using System; using System.Collections.Generic; namespace {method.ContainingNamespace} @@ -155,13 +63,99 @@ namespace {method.ContainingNamespace} }} }}"; - context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + } + + private static MethodTarget? PopulateMethodTargets(GeneratorSyntaxContext context, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + if (context.Node is MethodDeclarationSyntax method) + { + var attributes = method.AttributeLists.SelectMany(i => i.Attributes); + var semanticModel = context.SemanticModel; + foreach (var attribute in attributes) + { + var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); + if (attributeTypeInfo.Type is null || + attributeTypeInfo.Type.ToString() != s_attributeName || + attribute.ArgumentList is null) + { + continue; + } + + var arguments = attribute.ArgumentList.Arguments; + if (arguments.Count != 2) + { + continue; + } + + if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || + arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) + { + continue; + } + + var type = semanticModel.GetTypeInfo(typeOfExpr.Type); + var ns = semanticModel.GetConstantValue(arguments[1].Expression); + var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); + + if (type.Type is not ITypeSymbol baseType || + ns.HasValue is false || + ns.Value is not string nsValue || + methodDeclInfo is not IMethodSymbol methodSymbol || + methodSymbol.Parameters.Length != 2 || + methodSymbol.Parameters[1].RefKind != RefKind.Out) + { + continue; + } + + return new MethodTarget(methodSymbol, baseType, nsValue); + } } + + return null; } - public void Initialize(GeneratorInitializationContext context) + public void Initialize(IncrementalGeneratorInitializationContext context) { - context.RegisterForSyntaxNotifications(() => _receiver); + var typesProvider = context.SyntaxProvider.CreateSyntaxProvider( + static (syntaxNode, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax; + }, + static (syntaxContext, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxContext.Node is ClassDeclarationSyntax or StructDeclarationSyntax && + syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.Node) is ITypeSymbol typeSymbol + ? typeSymbol : null; + }) + .SelectMany((type, token) => + { + token.ThrowIfCancellationRequested(); + return type is null ? Array.Empty() : new ITypeSymbol[] { type }; + }); + + var methodsProvider = context.SyntaxProvider.CreateSyntaxProvider( + static (syntaxNode, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax or MethodDeclarationSyntax { AttributeLists.Count: > 0 }; + }, PopulateMethodTargets) + .SelectMany((method, token) => + { + token.ThrowIfCancellationRequested(); + return method is null ? Array.Empty() : new MethodTarget[] { method }; + }); + + var generateContext = methodsProvider.Combine(typesProvider.Collect()); + + context.RegisterSourceOutput(generateContext, static (sourceContext, source) => + { + sourceContext.CancellationToken.ThrowIfCancellationRequested(); + GenerateSubTypes(sourceContext, source.Left, source.Right); + }); } } } From b4a5d2e88a51e52b9fadb3f1bfe1969d73ea8826 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:45:25 +0800 Subject: [PATCH 447/820] Use record struct --- src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index 6ba1f6ac18..dbdef5e089 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -11,7 +11,7 @@ namespace Avalonia.SourceGenerator [Generator(LanguageNames.CSharp)] public class SubtypesFactoryGenerator : IIncrementalGenerator { - private record MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); + private record struct MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) @@ -146,7 +146,7 @@ namespace {method.ContainingNamespace} .SelectMany((method, token) => { token.ThrowIfCancellationRequested(); - return method is null ? Array.Empty() : new MethodTarget[] { method }; + return method is null ? Array.Empty() : new MethodTarget[] { method.Value }; }); var generateContext = methodsProvider.Combine(typesProvider.Collect()); From de771d6fc49cdd1b8d5043e4a6b0730ff5350698 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:46:33 +0800 Subject: [PATCH 448/820] Remove unnecessary nodes in MethodTargets --- src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index dbdef5e089..10c18580b3 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -141,7 +141,7 @@ namespace {method.ContainingNamespace} static (syntaxNode, token) => { token.ThrowIfCancellationRequested(); - return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax or MethodDeclarationSyntax { AttributeLists.Count: > 0 }; + return syntaxNode is MethodDeclarationSyntax { AttributeLists.Count: > 0 }; }, PopulateMethodTargets) .SelectMany((method, token) => { From d7a098b75e39c84db552aa620e4bd34d2d448758 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:51:05 +0800 Subject: [PATCH 449/820] Avoid reparse source in codegen stage --- .../SubtypesFactoryGenerator.cs | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index 10c18580b3..4fc9397e7a 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -11,7 +11,7 @@ namespace Avalonia.SourceGenerator [Generator(LanguageNames.CSharp)] public class SubtypesFactoryGenerator : IIncrementalGenerator { - private record struct MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); + private record struct MethodTarget(IMethodSymbol Method, string MethodDecl, ITypeSymbol BaseType, string Namespace); private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) @@ -21,26 +21,12 @@ namespace Avalonia.SourceGenerator private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray types) { - var (method, baseType, @namespace) = methodTarget; + var (method, methodDecl, baseType, @namespace) = methodTarget; var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); var type = method.ContainingType; var isGeneric = type.TypeParameters.Length > 0; var isClass = type.TypeKind == TypeKind.Class; - if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) - { - return; - } - - var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); - - var methodDeclText = methodDecl - .WithAttributeLists(new SyntaxList()) - .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) - .WithBody(null) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) - .WithoutTrivia().ToString(); - var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; var source = $@"using System; using System.Collections.Generic; @@ -49,7 +35,7 @@ namespace {method.ContainingNamespace} {{ {typeDecl} {{ - {methodDeclText} + {methodDecl} {{ var hasMatch = false; (hasMatch, {method.Parameters[1].Name}) = {method.Parameters[0].Name} switch @@ -109,7 +95,15 @@ namespace {method.ContainingNamespace} continue; } - return new MethodTarget(methodSymbol, baseType, nsValue); + var parameters = new SeparatedSyntaxList().AddRange(method.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); + var methodDecl = method + .WithAttributeLists(new SyntaxList()) + .WithParameterList(method.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); + + return new MethodTarget(methodSymbol, methodDecl, baseType, nsValue); } } From d79854ec54df61a7377cd475e012771aa49a55f2 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 21:38:51 +0800 Subject: [PATCH 450/820] Remove rd.xml --- .../ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 5 ++--- samples/ControlCatalog.NetCore/rd.xml | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 samples/ControlCatalog.NetCore/rd.xml diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index d1b657722c..2b45ac1508 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -6,7 +6,7 @@ true - + true https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json 7.0.0-* @@ -22,12 +22,11 @@ - + - diff --git a/samples/ControlCatalog.NetCore/rd.xml b/samples/ControlCatalog.NetCore/rd.xml deleted file mode 100644 index 27db7f34ca..0000000000 --- a/samples/ControlCatalog.NetCore/rd.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From d5e049e70a6f9d8a3c6c0fdbda8686dfa82456ee Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 16 Apr 2022 17:41:38 +0300 Subject: [PATCH 451/820] Fixed path to DevAnalyzers.csproj Linux uses case-sensetive file systems, so MSBuild was unable to find the file --- build/DevAnalyzers.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props index 28959dbd47..14e4f6a563 100644 --- a/build/DevAnalyzers.props +++ b/build/DevAnalyzers.props @@ -1,6 +1,6 @@ - Date: Sat, 16 Apr 2022 22:17:12 -0400 Subject: [PATCH 452/820] Move ColorPicker source into Avalonia.Controls.ColorPicker --- .../ColorChangedEventArgs.cs | 0 .../ColorSpectrum/ColorHelpers.cs | 0 .../ColorSpectrum/ColorSpectrum.Properties.cs | 0 .../ColorSpectrum/ColorSpectrum.cs | 0 .../ColorSpectrum/Hsv.cs | 0 .../ColorSpectrum/IncrementAmount.cs | 0 .../ColorSpectrum/IncrementDirection.cs | 0 .../ColorSpectrum/Rgb.cs | 0 .../ColorSpectrumChannels.cs | 0 .../ColorSpectrumShape.cs | 0 .../HsvChannel.cs | 0 .../Themes/Default.xaml} | 10 ++-------- .../Themes/Fluent.xaml} | 10 ++-------- src/Avalonia.Themes.Default/DefaultTheme.xaml | 1 - .../Controls/FluentControls.xaml | 1 - 15 files changed, 4 insertions(+), 18 deletions(-) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorChangedEventArgs.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/ColorHelpers.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/ColorSpectrum.Properties.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/ColorSpectrum.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/Hsv.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/IncrementAmount.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/IncrementDirection.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/Rgb.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrumChannels.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrumShape.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/HsvChannel.cs (100%) rename src/{Avalonia.Themes.Default/Controls/ColorSpectrum.xaml => Avalonia.Controls.ColorPicker/Themes/Default.xaml} (97%) rename src/{Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml => Avalonia.Controls.ColorPicker/Themes/Fluent.xaml} (97%) diff --git a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs rename to src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs diff --git a/src/Avalonia.Controls/ColorPicker/HsvChannel.cs b/src/Avalonia.Controls.ColorPicker/HsvChannel.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/HsvChannel.cs rename to src/Avalonia.Controls.ColorPicker/HsvChannel.cs diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml similarity index 97% rename from src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 338ea69dac..832daf8853 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,14 +1,8 @@ - - - - - - - @@ -136,5 +130,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml similarity index 97% rename from src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 5de6daea11..545702ea84 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,14 +1,8 @@ - - - - - - - @@ -136,5 +130,5 @@ - + diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 682f395e5b..468b723f5b 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -11,7 +11,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index b7d35ad5cc..5b217e4764 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -11,7 +11,6 @@ - From cf654da43f1d4a8dae5f1e37347c724221eb4ecc Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 22:38:49 -0400 Subject: [PATCH 453/820] Add separate Avalonia.Controls.ColorPicker project --- Avalonia.sln | 26 +++++++++++++++++++ samples/ControlCatalog/App.xaml.cs | 13 +++++++++- samples/ControlCatalog/ControlCatalog.csproj | 1 + samples/ControlCatalog/MainView.xaml.cs | 4 +++ samples/Sandbox/Sandbox.csproj | 1 + src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 + .../Avalonia.Controls.ColorPicker.csproj | 21 +++++++++++++++ .../Avalonia.Diagnostics.csproj | 1 + .../Avalonia.LeakTests.csproj | 1 + 9 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj diff --git a/Avalonia.sln b/Avalonia.sln index 1e2a3c6027..ea30514c3e 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -169,6 +169,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "sam EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ColorPicker", "src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj", "{1ECC012A-8837-4AE2-9BDA-3E2857898727}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}" @@ -1963,6 +1965,30 @@ Global {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 866fb8632a..6539cdaee6 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -18,6 +18,16 @@ namespace ControlCatalog DataContext = new ApplicationViewModel(); } + public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + { + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent.xaml") + }; + + public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + { + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default.xaml") + }; + public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml") @@ -69,7 +79,8 @@ namespace ControlCatalog public override void Initialize() { Styles.Insert(0, Fluent); - Styles.Insert(1, DataGridFluent); + Styles.Insert(1, ColorPickerFluent); + Styles.Insert(2, DataGridFluent); AvaloniaXamlLoader.Load(this); } diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index c0e24357ca..7cbd8a3f9c 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -23,6 +23,7 @@ + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index e8ea39abbb..f2d89a1325 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -50,6 +50,7 @@ namespace ControlCatalog } Application.Current.Styles[0] = App.Fluent; Application.Current.Styles[1] = App.DataGridFluent; + Application.Current.Styles.Add(App.ColorPickerFluent); } else if (theme == CatalogTheme.FluentDark) { @@ -60,18 +61,21 @@ namespace ControlCatalog } Application.Current.Styles[0] = App.Fluent; Application.Current.Styles[1] = App.DataGridFluent; + Application.Current.Styles.Add(App.ColorPickerFluent); } else if (theme == CatalogTheme.DefaultLight) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light; Application.Current.Styles[0] = App.DefaultLight; Application.Current.Styles[1] = App.DataGridDefault; + Application.Current.Styles.Add(App.ColorPickerDefault); } else if (theme == CatalogTheme.DefaultDark) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark; Application.Current.Styles[0] = App.DefaultDark; Application.Current.Styles[1] = App.DataGridDefault; + Application.Current.Styles.Add(App.ColorPickerDefault); } } }; diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj index 8f2812e048..f3c38cd96e 100644 --- a/samples/Sandbox/Sandbox.csproj +++ b/samples/Sandbox/Sandbox.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index a0560924e7..2c40c768f5 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -19,6 +19,7 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Controls.ColorPicker, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj new file mode 100644 index 0000000000..21218fc771 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -0,0 +1,21 @@ + + + net6.0;netstandard2.0 + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index 2fb7c07b6f..a83e8be475 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -13,6 +13,7 @@ + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 0c663e1a8f..a308e1c3ed 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -9,6 +9,7 @@ + From 83402cb285712d5d9a5d3563f9aa21fc87bae2cc Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 22:50:28 -0400 Subject: [PATCH 454/820] Make Color.ToHsv() internal again --- src/Avalonia.Base/Media/Color.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index db18ec7c6e..cb90404f6d 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -653,9 +653,6 @@ namespace Avalonia.Media (byteToDouble * alpha)); } - // TODO: Mark the below .ToHsv(double...) method internal and make internals visible to Avalonia.Controls - // It is needed for the ColorPicker which works in normalized values directly - /// /// Converts the given RGBA color component values to their HSV color equivalent. /// @@ -668,7 +665,7 @@ namespace Avalonia.Media /// The Blue component in the RGB color model within the range 0..1. /// The Alpha component in the RGB color model within the range 0..1. /// A new equivalent to the given RGBA values. - public static HsvColor ToHsv( + internal static HsvColor ToHsv( double r, double g, double b, From 9f1833030dec741137058631dcfc1e67f63a5e23 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 23:29:46 -0400 Subject: [PATCH 455/820] Use namespaces in XAML --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 36 +++++++++---------- .../Themes/Default.xaml | 27 +++++++------- .../Themes/Fluent.xaml | 27 +++++++------- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index ec34193f8c..08f56be8e3 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -2,28 +2,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" + xmlns:cpp="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls.ColorPicker" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ControlCatalog.Pages.ColorPickerPage"> - - - + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 832daf8853..8d9b49b9b1 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,7 +1,8 @@ + xmlns:converters="using:Avalonia.Controls.Converters" + xmlns:primitive="using:Avalonia.Controls.Primitives"> @@ -9,7 +10,7 @@ - - - - - - - - - - diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 545702ea84..9c3a74b1fa 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,7 +1,8 @@ + xmlns:converters="using:Avalonia.Controls.Converters" + xmlns:primitive="using:Avalonia.Controls.Primitives"> @@ -9,7 +10,7 @@ - - - - - - - - - - From f13398506d96b964fb96f834063053bdaef11f23 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 23:30:22 -0400 Subject: [PATCH 456/820] Fix Avalonia.Controls.ColorPicker.csproj - Disable ApiDiff tool - Enable nullable reference types --- .../Avalonia.Controls.ColorPicker.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 21218fc771..03950fb168 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -3,6 +3,9 @@ net6.0;netstandard2.0 + + + @@ -16,6 +19,7 @@ - + + From 8da297b30073c05142ad3e31fb7710553db6e824 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 23:36:09 -0400 Subject: [PATCH 457/820] Correct ColorPicker style loading in ControlCatalog --- samples/ControlCatalog/MainView.xaml.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index f2d89a1325..0326946c08 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -49,8 +49,8 @@ namespace ControlCatalog App.Fluent.Mode = FluentThemeMode.Light; } Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.DataGridFluent; - Application.Current.Styles.Add(App.ColorPickerFluent); + Application.Current.Styles[1] = App.ColorPickerFluent; + Application.Current.Styles[2] = App.DataGridFluent; } else if (theme == CatalogTheme.FluentDark) { @@ -60,22 +60,22 @@ namespace ControlCatalog App.Fluent.Mode = FluentThemeMode.Dark; } Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.DataGridFluent; - Application.Current.Styles.Add(App.ColorPickerFluent); + Application.Current.Styles[1] = App.ColorPickerFluent; + Application.Current.Styles[2] = App.DataGridFluent; } else if (theme == CatalogTheme.DefaultLight) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light; Application.Current.Styles[0] = App.DefaultLight; - Application.Current.Styles[1] = App.DataGridDefault; - Application.Current.Styles.Add(App.ColorPickerDefault); + Application.Current.Styles[1] = App.ColorPickerDefault; + Application.Current.Styles[2] = App.DataGridDefault; } else if (theme == CatalogTheme.DefaultDark) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark; Application.Current.Styles[0] = App.DefaultDark; - Application.Current.Styles[1] = App.DataGridDefault; - Application.Current.Styles.Add(App.ColorPickerDefault); + Application.Current.Styles[1] = App.ColorPickerDefault; + Application.Current.Styles[2] = App.DataGridDefault; } } }; From a389f7b0d8aa82db0d04401887f72f0bca3c2e3a Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:02:18 -0400 Subject: [PATCH 458/820] Fix ColorPicker project references --- src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj | 2 +- tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index a83e8be475..adddf3f57b 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -13,7 +13,7 @@ - + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index a308e1c3ed..a00b24bdd7 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -9,7 +9,7 @@ - + From 60e0d12543aac9b8948f8657f0f64292cc219b50 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:04:56 -0400 Subject: [PATCH 459/820] Revert "Use namespaces in XAML" This reverts commit 9f1833030dec741137058631dcfc1e67f63a5e23. --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 36 +++++++++---------- .../Themes/Default.xaml | 27 +++++++------- .../Themes/Fluent.xaml | 27 +++++++------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index 08f56be8e3..ec34193f8c 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -2,28 +2,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:cpp="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls.ColorPicker" + xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ControlCatalog.Pages.ColorPickerPage"> - - - + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 8d9b49b9b1..832daf8853 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,8 +1,7 @@ + xmlns:converters="using:Avalonia.Controls.Converters"> @@ -10,7 +9,7 @@ - - - - - - - - - - diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 9c3a74b1fa..545702ea84 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,8 +1,7 @@ + xmlns:converters="using:Avalonia.Controls.Converters"> @@ -10,7 +9,7 @@ - - - - - - - - - - From 8453b4a79ac85fd338f23f1941005ac0ad788ae1 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:11:23 -0400 Subject: [PATCH 460/820] Add AssemblyInfo for ColorPicker.csproj --- .../Properties/AssemblyInfo.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs diff --git a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0135541349 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using Avalonia.Metadata; + +[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] + +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] From a66e15e8a2a3f2b8d2f9d0ae14eaaf8509436260 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:11:41 -0400 Subject: [PATCH 461/820] Add back PackageId for ColorPicker.csproj --- .../Avalonia.Controls.ColorPicker.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 03950fb168..0952c899d4 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -1,7 +1,7 @@  net6.0;netstandard2.0 - + Avalonia.Controls.ColorPicker From 397fd5068fc26e7093e0316d1cfb0beb28aa1543 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:31:23 -0400 Subject: [PATCH 462/820] Rename 'channel' to 'component' --- .../ColorSpectrum/ColorHelpers.cs | 44 ++-- .../ColorSpectrum/ColorSpectrum.Properties.cs | 30 +-- .../ColorSpectrum/ColorSpectrum.cs | 231 +++++++++--------- .../ColorSpectrum/Hsv.cs | 18 +- .../ColorSpectrum/IncrementAmount.cs | 2 +- .../ColorSpectrum/IncrementDirection.cs | 2 +- .../ColorSpectrum/Rgb.cs | 20 +- ...Channels.cs => ColorSpectrumComponents.cs} | 18 +- .../HsvChannel.cs | 33 --- .../HsvComponent.cs | 47 ++++ 10 files changed, 229 insertions(+), 216 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrumChannels.cs => ColorSpectrumComponents.cs} (81%) delete mode 100644 src/Avalonia.Controls.ColorPicker/HsvChannel.cs create mode 100644 src/Avalonia.Controls.ColorPicker/HsvComponent.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs index cd2decf0a7..b912d39aba 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs @@ -26,9 +26,9 @@ namespace Avalonia.Controls.Primitives return string.Empty; } - public static Hsv IncrementColorChannel( + public static Hsv IncrementColorComponent( Hsv originalHsv, - HsvChannel channel, + HsvComponent component, IncrementDirection direction, IncrementAmount amount, bool shouldWrap, @@ -52,25 +52,25 @@ namespace Avalonia.Controls.Primitives // If we're adding a large increment, then we want to snap to the next // or previous major value - for hue, this is every increment of 30; // for saturation and value, this is every increment of 10. - switch (channel) + switch (component) { - case HsvChannel.Hue: + case HsvComponent.Hue: valueToIncrement = ref newHsv.H; incrementAmount = amount == IncrementAmount.Small ? 1 : 30; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: valueToIncrement = ref newHsv.S; incrementAmount = amount == IncrementAmount.Small ? 1 : 10; break; - case HsvChannel.Value: + case HsvComponent.Value: valueToIncrement = ref newHsv.V; incrementAmount = amount == IncrementAmount.Small ? 1 : 10; break; default: - throw new InvalidOperationException("Invalid HsvChannel."); + throw new InvalidOperationException("Invalid HsvComponent."); } double previousValue = valueToIncrement; @@ -99,14 +99,14 @@ namespace Avalonia.Controls.Primitives // While working with named colors, we're going to need to be working in actual HSV units, // so we'll divide the min bound and max bound by 100 in the case of saturation or value, // since we'll have received units between 0-100 and we need them within 0-1. - if (channel == HsvChannel.Saturation || - channel == HsvChannel.Value) + if (component == HsvComponent.Saturation || + component == HsvComponent.Value) { minBound /= 100; maxBound /= 100; } - newHsv = FindNextNamedColor(originalHsv, channel, direction, shouldWrap, minBound, maxBound); + newHsv = FindNextNamedColor(originalHsv, component, direction, shouldWrap, minBound, maxBound); } return newHsv; @@ -114,7 +114,7 @@ namespace Avalonia.Controls.Primitives public static Hsv FindNextNamedColor( Hsv originalHsv, - HsvChannel channel, + HsvComponent component, IncrementDirection direction, bool shouldWrap, double minBound, @@ -136,28 +136,28 @@ namespace Avalonia.Controls.Primitives ref double newValue = ref newHsv.H; double incrementAmount = 0.0; - switch (channel) + switch (component) { - case HsvChannel.Hue: + case HsvComponent.Hue: originalValue = originalHsv.H; newValue = ref newHsv.H; incrementAmount = 1; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: originalValue = originalHsv.S; newValue = ref newHsv.S; incrementAmount = 0.01; break; - case HsvChannel.Value: + case HsvComponent.Value: originalValue = originalHsv.V; newValue = ref newHsv.V; incrementAmount = 0.01; break; default: - throw new InvalidOperationException("Invalid HsvChannel."); + throw new InvalidOperationException("Invalid HsvComponent."); } bool shouldFindMidPoint = true; @@ -228,28 +228,28 @@ namespace Avalonia.Controls.Primitives ref double currentValue = ref currentHsv.H; double wrapIncrement = 0; - switch (channel) + switch (component) { - case HsvChannel.Hue: + case HsvComponent.Hue: startValue = ref startHsv.H; currentValue = ref currentHsv.H; wrapIncrement = 360.0; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: startValue = ref startHsv.S; currentValue = ref currentHsv.S; wrapIncrement = 1.0; break; - case HsvChannel.Value: + case HsvComponent.Value: startValue = ref startHsv.V; currentValue = ref currentHsv.V; wrapIncrement = 1.0; break; default: - throw new InvalidOperationException("Invalid HsvChannel."); + throw new InvalidOperationException("Invalid HsvComponent."); } while (newColorName == currentColorName) @@ -316,7 +316,7 @@ namespace Avalonia.Controls.Primitives return newHsv; } - public static double IncrementAlphaChannel( + public static double IncrementAlphaComponent( double originalAlpha, IncrementDirection direction, IncrementAmount amount, diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index 71fc887bee..824bf9ab05 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -32,24 +32,24 @@ namespace Avalonia.Controls.Primitives Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF)); /// - /// Gets or sets the two HSV color channels displayed by the spectrum. + /// Gets or sets the two HSV color components displayed by the spectrum. /// /// /// Internally, the uses the HSV color model. /// - public ColorSpectrumChannels Channels + public ColorSpectrumComponents Components { - get => GetValue(ChannelsProperty); - set => SetValue(ChannelsProperty, value); + get => GetValue(ComponentsProperty); + set => SetValue(ComponentsProperty, value); } /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty ChannelsProperty = - AvaloniaProperty.Register( - nameof(Channels), - ColorSpectrumChannels.HueSaturation); + public static readonly StyledProperty ComponentsProperty = + AvaloniaProperty.Register( + nameof(Components), + ColorSpectrumComponents.HueSaturation); /// /// Gets or sets the currently selected color in the HSV color model. @@ -74,7 +74,7 @@ namespace Avalonia.Controls.Primitives new HsvColor(1, 0, 0, 1)); /// - /// Gets or sets the maximum value of the Hue channel in the range from 0..359. + /// Gets or sets the maximum value of the Hue component in the range from 0..359. /// This property must be greater than . /// /// @@ -93,7 +93,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MaxHue), 359); /// - /// Gets or sets the maximum value of the Saturation channel in the range from 0..100. + /// Gets or sets the maximum value of the Saturation component in the range from 0..100. /// This property must be greater than . /// /// @@ -112,7 +112,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MaxSaturation), 100); /// - /// Gets or sets the maximum value of the Value channel in the range from 0..100. + /// Gets or sets the maximum value of the Value component in the range from 0..100. /// This property must be greater than . /// /// @@ -131,7 +131,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MaxValue), 100); /// - /// Gets or sets the minimum value of the Hue channel in the range from 0..359. + /// Gets or sets the minimum value of the Hue component in the range from 0..359. /// This property must be less than . /// /// @@ -150,7 +150,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MinHue), 0); /// - /// Gets or sets the minimum value of the Saturation channel in the range from 0..100. + /// Gets or sets the minimum value of the Saturation component in the range from 0..100. /// This property must be less than . /// /// @@ -169,7 +169,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MinSaturation), 0); /// - /// Gets or sets the minimum value of the Value channel in the range from 0..100. + /// Gets or sets the minimum value of the Value component in the range from 0..100. /// This property must be less than . /// /// diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 6779c54a1e..aecaa88f36 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -80,7 +80,7 @@ namespace Avalonia.Controls.Primitives // in order to function properly while the asynchronous bitmap creation // is in progress. private ColorSpectrumShape _shapeFromLastBitmapCreation = ColorSpectrumShape.Box; - private ColorSpectrumChannels _componentsFromLastBitmapCreation = ColorSpectrumChannels.HueSaturation; + private ColorSpectrumComponents _componentsFromLastBitmapCreation = ColorSpectrumComponents.HueSaturation; private double _imageWidthFromLastBitmapCreation = 0.0; private double _imageHeightFromLastBitmapCreation = 0.0; private int _minHueFromLastBitmapCreation = 0; @@ -99,7 +99,7 @@ namespace Avalonia.Controls.Primitives public ColorSpectrum() { _shapeFromLastBitmapCreation = Shape; - _componentsFromLastBitmapCreation = Channels; + _componentsFromLastBitmapCreation = Components; _imageWidthFromLastBitmapCreation = 0; _imageHeightFromLastBitmapCreation = 0; _minHueFromLastBitmapCreation = MinHue; @@ -219,53 +219,53 @@ namespace Avalonia.Controls.Primitives bool isControlDown = e.KeyModifiers.HasFlag(KeyModifiers.Control); - HsvChannel incrementChannel = HsvChannel.Hue; + HsvComponent incrementComponent = HsvComponent.Hue; bool isSaturationValue = false; if (key == Key.Left || key == Key.Right) { - switch (Channels) + switch (Components) { - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.HueValue: - incrementChannel = HsvChannel.Hue; + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.HueValue: + incrementComponent = HsvComponent.Hue; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: isSaturationValue = true; - goto case ColorSpectrumChannels.SaturationHue; - case ColorSpectrumChannels.SaturationHue: - incrementChannel = HsvChannel.Saturation; + goto case ColorSpectrumComponents.SaturationHue; + case ColorSpectrumComponents.SaturationHue: + incrementComponent = HsvComponent.Saturation; break; - case ColorSpectrumChannels.ValueHue: - case ColorSpectrumChannels.ValueSaturation: - incrementChannel = HsvChannel.Value; + case ColorSpectrumComponents.ValueHue: + case ColorSpectrumComponents.ValueSaturation: + incrementComponent = HsvComponent.Value; break; } } else if (key == Key.Up || key == Key.Down) { - switch (Channels) + switch (Components) { - case ColorSpectrumChannels.SaturationHue: - case ColorSpectrumChannels.ValueHue: - incrementChannel = HsvChannel.Hue; + case ColorSpectrumComponents.SaturationHue: + case ColorSpectrumComponents.ValueHue: + incrementComponent = HsvComponent.Hue; break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.ValueSaturation: - incrementChannel = HsvChannel.Saturation; + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.ValueSaturation: + incrementComponent = HsvComponent.Saturation; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: isSaturationValue = true; - goto case ColorSpectrumChannels.HueValue; - case ColorSpectrumChannels.HueValue: - incrementChannel = HsvChannel.Value; + goto case ColorSpectrumComponents.HueValue; + case ColorSpectrumComponents.HueValue: + incrementComponent = HsvComponent.Value; break; } } @@ -273,19 +273,19 @@ namespace Avalonia.Controls.Primitives double minBound = 0.0; double maxBound = 0.0; - switch (incrementChannel) + switch (incrementComponent) { - case HsvChannel.Hue: + case HsvComponent.Hue: minBound = MinHue; maxBound = MaxHue; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: minBound = MinSaturation; maxBound = MaxSaturation; break; - case HsvChannel.Value: + case HsvComponent.Value: minBound = MinValue; maxBound = MaxValue; break; @@ -295,8 +295,8 @@ namespace Avalonia.Controls.Primitives // so we want left and up to be lower for hue, but higher for saturation and value. // This will ensure that the icon always moves in the direction of the key press. IncrementDirection direction = - (incrementChannel == HsvChannel.Hue && (key == Key.Left || key == Key.Up)) || - (incrementChannel != HsvChannel.Hue && (key == Key.Right || key == Key.Down)) ? + (incrementComponent == HsvComponent.Hue && (key == Key.Left || key == Key.Up)) || + (incrementComponent != HsvComponent.Hue && (key == Key.Right || key == Key.Down)) ? IncrementDirection.Lower : IncrementDirection.Higher; @@ -320,9 +320,9 @@ namespace Avalonia.Controls.Primitives IncrementAmount amount = isControlDown ? IncrementAmount.Large : IncrementAmount.Small; HsvColor hsvColor = HsvColor; - UpdateColor(ColorHelpers.IncrementColorChannel( + UpdateColor(ColorHelpers.IncrementColorComponent( new Hsv(hsvColor), - incrementChannel, + incrementComponent, direction, amount, shouldWrap: true, @@ -408,12 +408,12 @@ namespace Avalonia.Controls.Primitives throw new ArgumentException("MaxHue must be between 0 and 359."); } - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If hue is one of the axes in the spectrum bitmap, then we'll need to regenerate it // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.SaturationValue && - channels != ColorSpectrumChannels.ValueSaturation) + if (components != ColorSpectrumComponents.SaturationValue && + components != ColorSpectrumComponents.ValueSaturation) { CreateBitmapsAndColorMap(); } @@ -433,12 +433,12 @@ namespace Avalonia.Controls.Primitives throw new ArgumentException("MaxSaturation must be between 0 and 100."); } - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.HueValue && - channels != ColorSpectrumChannels.ValueHue) + if (components != ColorSpectrumComponents.HueValue && + components != ColorSpectrumComponents.ValueHue) { CreateBitmapsAndColorMap(); } @@ -458,12 +458,12 @@ namespace Avalonia.Controls.Primitives throw new ArgumentException("MaxValue must be between 0 and 100."); } - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.HueSaturation && - channels != ColorSpectrumChannels.SaturationHue) + if (components != ColorSpectrumComponents.HueSaturation && + components != ColorSpectrumComponents.SaturationHue) { CreateBitmapsAndColorMap(); } @@ -472,7 +472,7 @@ namespace Avalonia.Controls.Primitives { CreateBitmapsAndColorMap(); } - else if (change.Property == ChannelsProperty) + else if (change.Property == ComponentsProperty) { CreateBitmapsAndColorMap(); } @@ -617,23 +617,22 @@ namespace Avalonia.Controls.Primitives // Note: This can sometimes cause a crash -- possibly due to differences in c# rounding. Therefore, index is now clamped. Hsv hsvAtPoint = _hsvValues[MathUtilities.Clamp((y * width + x), 0, _hsvValues.Count - 1)]; - var channels = Channels; var hsvColor = HsvColor; - switch (channels) + switch (Components) { - case ColorSpectrumChannels.HueValue: - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.HueValue: + case ColorSpectrumComponents.ValueHue: hsvAtPoint.S = hsvColor.S; break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.SaturationHue: hsvAtPoint.V = hsvColor.V; break; - case ColorSpectrumChannels.ValueSaturation: - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.ValueSaturation: + case ColorSpectrumComponents.SaturationValue: hsvAtPoint.H = hsvColor.H; break; } @@ -681,8 +680,8 @@ namespace Avalonia.Controls.Primitives // In the case where saturation was an axis in the spectrum with hue, or value is an axis, full stop, // we inverted the direction of that axis in order to put more hue on the outside of the ring, // so we need to do similarly here when positioning the ellipse. - if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || - _componentsFromLastBitmapCreation == ColorSpectrumChannels.SaturationHue) + if (_componentsFromLastBitmapCreation == ColorSpectrumComponents.HueSaturation || + _componentsFromLastBitmapCreation == ColorSpectrumComponents.SaturationHue) { sPercent = 1 - sPercent; } @@ -693,32 +692,32 @@ namespace Avalonia.Controls.Primitives switch (_componentsFromLastBitmapCreation) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: xPercent = hPercent; yPercent = vPercent; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: xPercent = hPercent; yPercent = sPercent; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: xPercent = vPercent; yPercent = hPercent; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: xPercent = vPercent; yPercent = sPercent; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: xPercent = sPercent; yPercent = hPercent; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: xPercent = sPercent; yPercent = vPercent; break; @@ -757,8 +756,8 @@ namespace Avalonia.Controls.Primitives // In the case where saturation was an axis in the spectrum with hue, or value is an axis, full stop, // we inverted the direction of that axis in order to put more hue on the outside of the ring, // so we need to do similarly here when positioning the ellipse. - if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || - _componentsFromLastBitmapCreation == ColorSpectrumChannels.SaturationHue) + if (_componentsFromLastBitmapCreation == ColorSpectrumComponents.HueSaturation || + _componentsFromLastBitmapCreation == ColorSpectrumComponents.SaturationHue) { sThetaValue = 360 - sThetaValue; sRValue = -sRValue - 1; @@ -771,32 +770,32 @@ namespace Avalonia.Controls.Primitives switch (_componentsFromLastBitmapCreation) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: thetaValue = hThetaValue; rValue = vRValue; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: thetaValue = hThetaValue; rValue = sRValue; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: thetaValue = vThetaValue; rValue = hRValue; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: thetaValue = vThetaValue; rValue = sRValue; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: thetaValue = sThetaValue; rValue = hRValue; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: thetaValue = sThetaValue; rValue = vRValue; break; @@ -932,7 +931,7 @@ namespace Avalonia.Controls.Primitives int minValue = MinValue; int maxValue = MaxValue; ColorSpectrumShape shape = Shape; - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If min >= max, then by convention, min is the only number that a property can have. if (minHue >= maxHue) @@ -967,8 +966,8 @@ namespace Avalonia.Controls.Primitives bgraMinPixelData.Capacity = pixelDataSize; // We'll only save pixel data for the middle bitmaps if our third dimension is hue. - if (channels == ColorSpectrumChannels.ValueSaturation || - channels == ColorSpectrumChannels.SaturationValue) + if (components == ColorSpectrumComponents.ValueSaturation || + components == ColorSpectrumComponents.SaturationValue) { bgraMiddle1PixelData.Capacity = pixelDataSize; bgraMiddle2PixelData.Capacity = pixelDataSize; @@ -1004,7 +1003,7 @@ namespace Avalonia.Controls.Primitives for (int y = minDimensionInt - 1; y >= 0; --y) { FillPixelForBox( - x, y, hsv, minDimensionInt, channels, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, hsv, minDimensionInt, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1017,7 +1016,7 @@ namespace Avalonia.Controls.Primitives for (int x = 0; x < minDimensionInt; ++x) { FillPixelForRing( - x, y, minDimensionInt / 2.0, hsv, channels, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, minDimensionInt / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1030,24 +1029,24 @@ namespace Avalonia.Controls.Primitives int pixelWidth = (int)Math.Round(minDimension); int pixelHeight = (int)Math.Round(minDimension); - ColorSpectrumChannels channels2 = Channels; + ColorSpectrumComponents components2 = Components; WriteableBitmap minBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMinPixelData); WriteableBitmap maxBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMaxPixelData); - switch (channels2) + switch (components2) { - case ColorSpectrumChannels.HueValue: - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.HueValue: + case ColorSpectrumComponents.ValueHue: _saturationMinimumBitmap = minBitmap; _saturationMaximumBitmap = maxBitmap; break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.SaturationHue: _valueBitmap = maxBitmap; break; - case ColorSpectrumChannels.ValueSaturation: - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.ValueSaturation: + case ColorSpectrumComponents.SaturationValue: _hueRedBitmap = minBitmap; _hueYellowBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle1PixelData); _hueGreenBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle2PixelData); @@ -1058,7 +1057,7 @@ namespace Avalonia.Controls.Primitives } _shapeFromLastBitmapCreation = Shape; - _componentsFromLastBitmapCreation = Channels; + _componentsFromLastBitmapCreation = Components; _imageWidthFromLastBitmapCreation = minDimension; _imageHeightFromLastBitmapCreation = minDimension; _minHueFromLastBitmapCreation = MinHue; @@ -1080,7 +1079,7 @@ namespace Avalonia.Controls.Primitives double y, Hsv baseHsv, double minDimension, - ColorSpectrumChannels channels, + ColorSpectrumComponents components, double minHue, double maxHue, double minSaturation, @@ -1112,30 +1111,30 @@ namespace Avalonia.Controls.Primitives double xPercent = (minDimension - 1 - x) / (minDimension - 1); double yPercent = (minDimension - 1 - y) / (minDimension - 1); - switch (channels) + switch (components) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + yPercent * (hMax - hMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + xPercent * (vMax - vMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + yPercent * (hMax - hMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + xPercent * (sMax - sMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + yPercent * (vMax - vMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + xPercent * (hMax - hMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + yPercent * (vMax - vMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + xPercent * (sMax - sMin); hsvMin.H = 0; @@ -1146,14 +1145,14 @@ namespace Avalonia.Controls.Primitives hsvMax.H = 300; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + yPercent * (sMax - sMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + xPercent * (hMax - hMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + yPercent * (sMax - sMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + xPercent * (vMax - vMin); hsvMin.H = 0; @@ -1171,8 +1170,8 @@ namespace Avalonia.Controls.Primitives // so we'll invert the number before assigning the HSL value to the array. // Otherwise, we'll have a very narrow section in the middle that actually has meaningful hue // in the case of the ring configuration. - if (channels == ColorSpectrumChannels.HueSaturation || - channels == ColorSpectrumChannels.SaturationHue) + if (components == ColorSpectrumComponents.HueSaturation || + components == ColorSpectrumComponents.SaturationHue) { hsvMin.S = sMax - hsvMin.S + sMin; hsvMiddle1.S = sMax - hsvMiddle1.S + sMin; @@ -1200,8 +1199,8 @@ namespace Avalonia.Controls.Primitives bgraMinPixelData.Add(255); // a - ignored // We'll only save pixel data for the middle bitmaps if our third dimension is hue. - if (channels == ColorSpectrumChannels.ValueSaturation || - channels == ColorSpectrumChannels.SaturationValue) + if (components == ColorSpectrumComponents.ValueSaturation || + components == ColorSpectrumComponents.SaturationValue) { Rgb rgbMiddle1 = hsvMiddle1.ToRgb(); bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.B * 255.0)); // b @@ -1240,7 +1239,7 @@ namespace Avalonia.Controls.Primitives double y, double radius, Hsv baseHsv, - ColorSpectrumChannels channels, + ColorSpectrumComponents components, double minHue, double maxHue, double minSaturation, @@ -1298,30 +1297,30 @@ namespace Avalonia.Controls.Primitives double thetaPercent = theta / 360; - switch (channels) + switch (components) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + thetaPercent * (hMax - hMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + r * (vMax - vMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + thetaPercent * (hMax - hMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + r * (sMax - sMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + thetaPercent * (vMax - vMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + r * (hMax - hMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + thetaPercent * (vMax - vMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + r * (sMax - sMin); hsvMin.H = 0; @@ -1332,14 +1331,14 @@ namespace Avalonia.Controls.Primitives hsvMax.H = 300; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + thetaPercent * (sMax - sMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + r * (hMax - hMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + thetaPercent * (sMax - sMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + r * (vMax - vMin); hsvMin.H = 0; @@ -1357,8 +1356,8 @@ namespace Avalonia.Controls.Primitives // so we'll invert the number before assigning the HSL value to the array. // Otherwise, we'll have a very narrow section in the middle that actually has meaningful hue // in the case of the ring configuration. - if (channels == ColorSpectrumChannels.HueSaturation || - channels == ColorSpectrumChannels.SaturationHue) + if (components == ColorSpectrumComponents.HueSaturation || + components == ColorSpectrumComponents.SaturationHue) { hsvMin.S = sMax - hsvMin.S + sMin; hsvMiddle1.S = sMax - hsvMiddle1.S + sMin; @@ -1386,8 +1385,8 @@ namespace Avalonia.Controls.Primitives bgraMinPixelData.Add(255); // a // We'll only save pixel data for the middle bitmaps if our third dimension is hue. - if (channels == ColorSpectrumChannels.ValueSaturation || - channels == ColorSpectrumChannels.SaturationValue) + if (components == ColorSpectrumComponents.ValueSaturation || + components == ColorSpectrumComponents.SaturationValue) { Rgb rgbMiddle1 = hsvMiddle1.ToRgb(); bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.B * 255)); // b @@ -1432,7 +1431,7 @@ namespace Avalonia.Controls.Primitives } HsvColor hsvColor = HsvColor; - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // We'll set the base image and the overlay image based on which component is our third dimension. // If it's saturation or luminosity, then the base image is that dimension at its minimum value, @@ -1440,10 +1439,10 @@ namespace Avalonia.Controls.Primitives // If it's hue, then we'll figure out where in the color wheel we are, and then use the two // colors on either side of our position as our base image and overlay image. // For example, if our hue is orange, then the base image would be red and the overlay image yellow. - switch (channels) + switch (components) { - case ColorSpectrumChannels.HueValue: - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.HueValue: + case ColorSpectrumComponents.ValueHue: { if (_saturationMinimumBitmap == null || _saturationMaximumBitmap == null) @@ -1463,8 +1462,8 @@ namespace Avalonia.Controls.Primitives } break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.SaturationHue: { if (_valueBitmap == null) { @@ -1483,8 +1482,8 @@ namespace Avalonia.Controls.Primitives } break; - case ColorSpectrumChannels.ValueSaturation: - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.ValueSaturation: + case ColorSpectrumComponents.SaturationValue: { if (_hueRedBitmap == null || _hueYellowBitmap == null || @@ -1554,13 +1553,13 @@ namespace Avalonia.Controls.Primitives // To find how much something contrasts with white, we use the equation // for relative luminance. // - // If the third channel is value, then we won't be updating the spectrum's displayed colors, + // If the third component is value, then we won't be updating the spectrum's displayed colors, // so in that case we should use a value of 1 when considering the backdrop // for the selection ellipse. Color displayedColor; - if (Channels == ColorSpectrumChannels.HueSaturation || - Channels == ColorSpectrumChannels.SaturationHue) + if (Components == ColorSpectrumComponents.HueSaturation || + Components == ColorSpectrumComponents.SaturationHue) { HsvColor hsvColor = HsvColor; Rgb color = (new Hsv(hsvColor.H, hsvColor.S, 1.0)).ToRgb(); diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs index d44e6b3a9f..8a425b9581 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs @@ -8,39 +8,39 @@ using Avalonia.Media; namespace Avalonia.Controls.Primitives { /// - /// Contains and allows modification of Hue, Saturation and Value channel values. + /// Contains and allows modification of Hue, Saturation and Value components. /// /// /// The is a specialized struct optimized for permanence and memory: /// /// This is not a read-only struct like and allows editing the fields /// Removes the alpha component unnecessary in core calculations - /// No channel value bounds checks or clamping is done. + /// No component bounds checks or clamping is done. /// /// internal struct Hsv { /// - /// The Hue channel value in the range from 0..359. + /// The Hue component in the range from 0..359. /// public double H; /// - /// The Saturation channel value in the range from 0..1. + /// The Saturation component in the range from 0..1. /// public double S; /// - /// The Value channel value in the range from 0..1. + /// The Value component in the range from 0..1. /// public double V; /// /// 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 Hue component in the range from 0..360. + /// The Saturation component in the range from 0..1. + /// The Value component in the range from 0..1. public Hsv(double h, double s, double v) { H = h; @@ -62,7 +62,7 @@ namespace Avalonia.Controls.Primitives /// /// Converts this struct into a standard . /// - /// The Alpha channel value in the range from 0..1. + /// The Alpha component in the range from 0..1. /// A new representing this struct. public HsvColor ToHsvColor(double alpha = 1.0) { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs index fd749cd7dc..12aca593d5 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs @@ -6,7 +6,7 @@ namespace Avalonia.Controls.Primitives { /// - /// Defines a relative amount that a color channel should be incremented. + /// Defines a relative amount that a color component should be incremented. /// internal enum IncrementAmount { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs index 8002a44fc9..df9c1e3350 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs @@ -6,7 +6,7 @@ namespace Avalonia.Controls.Primitives { /// - /// Defines the direction a color channel should be incremented. + /// Defines the direction a color component should be incremented. /// internal enum IncrementDirection { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs index 616ab9e9ac..72e3821c2b 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs @@ -9,40 +9,40 @@ using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { /// - /// Contains and allows modification of Red, Green and Blue channel values. + /// Contains and allows modification of Red, Green and Blue components. /// /// /// The is a specialized struct optimized for permanence and memory: /// /// This is not a read-only struct like and allows editing the fields /// Removes the alpha component unnecessary in core calculations - /// Normalizes RGB channel values in the range of 0..1 to simplify calculations. - /// No channel value bounds checks or clamping is done. + /// Normalizes RGB components in the range of 0..1 to simplify calculations. + /// No component bounds checks or clamping is done. /// /// internal struct Rgb { /// - /// The Red channel value in the range from 0..1. + /// The Red component in the range from 0..1. /// public double R; /// - /// The Green channel value in the range from 0..1. + /// The Green component in the range from 0..1. /// public double G; /// - /// The Blue channel value in the range from 0..1. + /// The Blue component in the range from 0..1. /// public double B; /// /// Initializes a new instance of the struct. /// - /// The Red channel value in the range from 0..1. - /// The Green channel value in the range from 0..1. - /// The Blue channel value in the range from 0..1. + /// The Red component in the range from 0..1. + /// The Green component in the range from 0..1. + /// The Blue component in the range from 0..1. public Rgb(double r, double g, double b) { R = r; @@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives /// /// Converts this struct into a standard . /// - /// The Alpha channel value in the range from 0..1. + /// The Alpha component in the range from 0..1. /// A new representing this struct. public Color ToColor(double alpha = 1.0) { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs similarity index 81% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs index a31586d175..164089096e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs @@ -8,16 +8,16 @@ using Avalonia.Controls.Primitives; namespace Avalonia.Controls { /// - /// Defines the two HSV color channels displayed by a . + /// Defines the two HSV color components displayed by a . /// /// - /// Order of the color channels is important and correspond with an X/Y axis in Box + /// Order of the color components is important and correspond with an X/Y axis in Box /// shape or a degree/radius in Ring shape. /// - public enum ColorSpectrumChannels + public enum ColorSpectrumComponents { /// - /// The Hue and Value channels. + /// The Hue and Value components. /// /// /// In Box shape, Hue is mapped to the X-axis and Value is mapped to the Y-axis. @@ -26,7 +26,7 @@ namespace Avalonia.Controls HueValue, /// - /// The Value and Hue channels. + /// The Value and Hue components. /// /// /// In Box shape, Value is mapped to the X-axis and Hue is mapped to the Y-axis. @@ -35,7 +35,7 @@ namespace Avalonia.Controls ValueHue, /// - /// The Hue and Saturation channels. + /// The Hue and Saturation components. /// /// /// In Box shape, Hue is mapped to the X-axis and Saturation is mapped to the Y-axis. @@ -44,7 +44,7 @@ namespace Avalonia.Controls HueSaturation, /// - /// The Saturation and Hue channels. + /// The Saturation and Hue components. /// /// /// In Box shape, Saturation is mapped to the X-axis and Hue is mapped to the Y-axis. @@ -53,7 +53,7 @@ namespace Avalonia.Controls SaturationHue, /// - /// The Saturation and Value channels. + /// The Saturation and Value components. /// /// /// In Box shape, Saturation is mapped to the X-axis and Value is mapped to the Y-axis. @@ -62,7 +62,7 @@ namespace Avalonia.Controls SaturationValue, /// - /// The Value and Saturation channels. + /// The Value and Saturation components. /// /// /// In Box shape, Value is mapped to the X-axis and Saturation is mapped to the Y-axis. diff --git a/src/Avalonia.Controls.ColorPicker/HsvChannel.cs b/src/Avalonia.Controls.ColorPicker/HsvChannel.cs deleted file mode 100644 index 18f96fe70b..0000000000 --- a/src/Avalonia.Controls.ColorPicker/HsvChannel.cs +++ /dev/null @@ -1,33 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under the MIT License. - -namespace Avalonia.Controls -{ - /// - /// Defines a specific HSV color model channel. - /// - public enum HsvChannel - { - /// - /// The Hue channel. - /// - Hue, - - /// - /// The Saturation channel. - /// - Saturation, - - /// - /// The Value channel. - /// - Value, - - /// - /// The Alpha channel. - /// - Alpha - }; -} diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs new file mode 100644 index 0000000000..1132bd7bbb --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -0,0 +1,47 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using Avalonia.Media; + +namespace Avalonia.Controls +{ + /// + /// Defines a specific component in the HSV color model. + /// + public enum HsvComponent + { + /// + /// The Hue component. + /// + /// + /// Also see: + /// + Hue, + + /// + /// The Saturation component. + /// + /// + /// Also see: + /// + Saturation, + + /// + /// The Value component. + /// + /// + /// Also see: + /// + Value, + + /// + /// The Alpha component. + /// + /// + /// Also see: + /// + Alpha + }; +} From a39de875aa00ab18fdd2db74121b7071c9ee2a8b Mon Sep 17 00:00:00 2001 From: AndrejBunjac Date: Mon, 18 Apr 2022 17:34:31 +0200 Subject: [PATCH 463/820] Added invalidation to LayoutThickness property in Border and ContentPresenter and implemented minor review fixes. --- src/Avalonia.Controls/Border.cs | 38 +++++++++++++++---- .../Presenters/ContentPresenter.cs | 29 ++++++++++---- src/Avalonia.Layout/Layoutable.cs | 2 +- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index ce64570dc8..53de95ac41 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -1,8 +1,10 @@ +using System; using Avalonia.Collections; using Avalonia.Controls.Shapes; using Avalonia.Controls.Utils; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Utilities; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -88,6 +90,18 @@ namespace Avalonia.Controls AffectsMeasure(BorderThicknessProperty); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + switch (change.Property.Name) + { + case nameof(UseLayoutRounding): + case nameof(BorderThickness): + _layoutThickness = null; + break; + } + } + /// /// Gets or sets a brush with which to paint the background. /// @@ -169,29 +183,39 @@ namespace Avalonia.Controls set => SetValue(BoxShadowProperty, value); } - private Thickness _layoutThickness = default; + private Thickness? _layoutThickness; + private double _scale; private Thickness LayoutThickness { get { - if (_layoutThickness == default) + VerifyScale(); + + if (_layoutThickness == null) { var borderThickness = BorderThickness; if (UseLayoutRounding) - { - var scale = LayoutHelper.GetLayoutScale(this); - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); - } + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); _layoutThickness = borderThickness; } - return _layoutThickness; + return _layoutThickness.Value; } } + private void VerifyScale() + { + var currentScale = LayoutHelper.GetLayoutScale(this); + if (MathUtilities.AreClose(currentScale, _scale)) + return; + + _scale = currentScale; + _layoutThickness = null; + } + /// /// Renders the control. /// diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index bbb772a4ce..ae08b4a452 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -8,6 +8,7 @@ using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Metadata; +using Avalonia.Utilities; namespace Avalonia.Controls.Presenters { @@ -249,6 +250,10 @@ namespace Avalonia.Controls.Presenters case nameof(TemplatedParent): TemplatedParentChanged(change); break; + case nameof(UseLayoutRounding): + case nameof(BorderThickness): + _layoutThickness = null; + break; } } @@ -329,29 +334,39 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } - private Thickness _layoutThickness = default; + private Thickness? _layoutThickness; + private double _scale; private Thickness LayoutThickness { get { - if (_layoutThickness == default) + VerifyScale(); + + if (_layoutThickness == null) { var borderThickness = BorderThickness; if (UseLayoutRounding) - { - var scale = LayoutHelper.GetLayoutScale(this); - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); - } + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); _layoutThickness = borderThickness; } - return _layoutThickness; + return _layoutThickness.Value; } } + private void VerifyScale() + { + var currentScale = LayoutHelper.GetLayoutScale(this); + if (MathUtilities.AreClose(currentScale, _scale)) + return; + + _scale = currentScale; + _layoutThickness = null; + } + /// public override void Render(DrawingContext context) { diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 23a76f6ee2..df7aa937a0 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -653,7 +653,7 @@ namespace Avalonia.Layout // Margin has to be treated separately because the layout rounding function is not linear // f(a + b) != f(a) + f(b) // If the margin isn't pre-rounded some sizes will be offset by 1 pixel in certain scales. - if (UseLayoutRounding) + if (useLayoutRounding) { margin = LayoutHelper.RoundLayoutThickness(margin, scale, scale); } From 2902e3d24a9736484da225ec7ec15fa24b523437 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 19 Apr 2022 17:02:20 +0200 Subject: [PATCH 464/820] Rework Inlines invalidation --- .../TextFormatting/Unicode/BiDiAlgorithm.cs | 5 ++ .../Documents/IInlineHost.cs | 11 +++++ src/Avalonia.Controls/Documents/Inline.cs | 12 ++--- .../Documents/InlineCollection.cs | 48 ++++++++++++------- .../Documents/InlineUIContainer.cs | 12 +++-- src/Avalonia.Controls/Documents/LineBreak.cs | 2 +- src/Avalonia.Controls/Documents/Run.cs | 5 +- src/Avalonia.Controls/Documents/Span.cs | 9 ++-- .../Documents/TextElement.cs | 15 ++---- src/Avalonia.Controls/TextBlock.cs | 19 ++++---- 10 files changed, 77 insertions(+), 61 deletions(-) create mode 100644 src/Avalonia.Controls/Documents/IInlineHost.cs diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs index d18a4b2a87..2511807d9c 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs @@ -238,6 +238,11 @@ namespace Avalonia.Media.TextFormatting.Unicode _levelRuns.Clear(); _resolvedLevelsBuffer.Clear(); + if (types.IsEmpty) + { + return; + } + // Setup original types and working types _originalClasses = types; _workingClasses = _workingClassesBuffer.Add(types); diff --git a/src/Avalonia.Controls/Documents/IInlineHost.cs b/src/Avalonia.Controls/Documents/IInlineHost.cs new file mode 100644 index 0000000000..da72c207be --- /dev/null +++ b/src/Avalonia.Controls/Documents/IInlineHost.cs @@ -0,0 +1,11 @@ +using Avalonia.LogicalTree; + +namespace Avalonia.Controls.Documents +{ + internal interface IInlineHost : ILogical + { + void AddVisualChild(IControl child); + + void Invalidate(); + } +} diff --git a/src/Avalonia.Controls/Documents/Inline.cs b/src/Avalonia.Controls/Documents/Inline.cs index 445a48ecf4..a657d754b3 100644 --- a/src/Avalonia.Controls/Documents/Inline.cs +++ b/src/Avalonia.Controls/Documents/Inline.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Text; -using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; -namespace Avalonia.Controls.Documents +namespace Avalonia.Controls.Documents { /// /// Inline element. @@ -45,7 +44,7 @@ namespace Avalonia.Controls.Documents set { SetValue(BaselineAlignmentProperty, value); } } - internal abstract void BuildTextRun(IList textRuns, IInlinesHost parent); + internal abstract void BuildTextRun(IList textRuns); internal abstract void AppendText(StringBuilder stringBuilder); @@ -63,14 +62,9 @@ namespace Avalonia.Controls.Documents { case nameof(TextDecorations): case nameof(BaselineAlignment): - Invalidate(); + InlineHost?.Invalidate(); break; } } } - - public interface IInlinesHost : ILogical - { - void AddVisualChild(IControl child); - } } diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs index abe8f2cd4d..a76222385e 100644 --- a/src/Avalonia.Controls/Documents/InlineCollection.cs +++ b/src/Avalonia.Controls/Documents/InlineCollection.cs @@ -12,29 +12,37 @@ namespace Avalonia.Controls.Documents [WhitespaceSignificantCollection] public class InlineCollection : AvaloniaList { + private readonly IInlineHost? _host; private string? _text = string.Empty; /// /// Initializes a new instance of the class. /// - public InlineCollection(ILogical parent) : base(0) + public InlineCollection(ILogical parent) : this(parent, null) { } + + /// + /// Initializes a new instance of the class. + /// + internal InlineCollection(ILogical parent, IInlineHost? host = null) : base(0) { + _host = host; + ResetBehavior = ResetBehavior.Remove; this.ForEachItem( x => { ((ISetLogicalParent)x).SetParent(parent); - x.Invalidated += Invalidate; - Invalidate(); + x.InlineHost = host; + host?.Invalidate(); }, x => { ((ISetLogicalParent)x).SetParent(null); - x.Invalidated -= Invalidate; - Invalidate(); + x.InlineHost = host; + host?.Invalidate(); }, - () => throw new NotSupportedException()); + () => throw new NotSupportedException()); } public bool HasComplexContent => Count > 0; @@ -98,22 +106,20 @@ namespace Avalonia.Controls.Documents public void Add(IControl child) { - if (!HasComplexContent && !string.IsNullOrEmpty(_text)) - { - base.Add(new Run(_text)); - - _text = string.Empty; - } + var implicitRun = new InlineUIContainer(child); - base.Add(new InlineUIContainer(child)); + Add(implicitRun); } public override void Add(Inline item) { if (!HasComplexContent) { - base.Add(new Run(_text)); - + if (!string.IsNullOrEmpty(_text)) + { + base.Add(new Run(_text)); + } + _text = string.Empty; } @@ -124,11 +130,19 @@ namespace Avalonia.Controls.Documents /// Raised when an inline in the collection changes. /// public event EventHandler? Invalidated; - + /// /// Raises the event. /// - protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty); + protected void Invalidate() + { + if(_host != null) + { + _host.Invalidate(); + } + + Invalidated?.Invoke(this, EventArgs.Empty); + } private void Invalidate(object? sender, EventArgs e) => Invalidate(); } diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index 47851903dd..eb12092bb8 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Text; -using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; @@ -58,11 +57,16 @@ namespace Avalonia.Controls.Documents set => SetValue(ChildProperty, value); } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { - ((ISetLogicalParent)Child).SetParent(parent); + if(InlineHost == null) + { + return; + } + + ((ISetLogicalParent)Child).SetParent(InlineHost); - parent.AddVisualChild(Child); + InlineHost.AddVisualChild(Child); textRuns.Add(new InlineRun(Child, CreateTextRunProperties())); } diff --git a/src/Avalonia.Controls/Documents/LineBreak.cs b/src/Avalonia.Controls/Documents/LineBreak.cs index 00fad491d3..aeb81f7313 100644 --- a/src/Avalonia.Controls/Documents/LineBreak.cs +++ b/src/Avalonia.Controls/Documents/LineBreak.cs @@ -20,7 +20,7 @@ namespace Avalonia.Controls.Documents { } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { textRuns.Add(new TextEndOfLine()); } diff --git a/src/Avalonia.Controls/Documents/Run.cs b/src/Avalonia.Controls/Documents/Run.cs index 884718c28b..2c6482b586 100644 --- a/src/Avalonia.Controls/Documents/Run.cs +++ b/src/Avalonia.Controls/Documents/Run.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Text; using Avalonia.Data; -using Avalonia.LogicalTree; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; @@ -51,7 +50,7 @@ namespace Avalonia.Controls.Documents set { SetValue (TextProperty, value); } } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { var text = (Text ?? "").AsMemory(); @@ -76,7 +75,7 @@ namespace Avalonia.Controls.Documents switch (change.Property.Name) { case nameof(Text): - Invalidate(); + InlineHost?.Invalidate(); break; } } diff --git a/src/Avalonia.Controls/Documents/Span.cs b/src/Avalonia.Controls/Documents/Span.cs index 32e19d4153..bd1b4fc5e1 100644 --- a/src/Avalonia.Controls/Documents/Span.cs +++ b/src/Avalonia.Controls/Documents/Span.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Text; -using Avalonia.LogicalTree; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; -using Avalonia.Utilities; namespace Avalonia.Controls.Documents { @@ -27,8 +25,7 @@ namespace Avalonia.Controls.Documents public Span() { Inlines = new InlineCollection(this); - - Inlines.Invalidated += (s, e) => Invalidate(); + Inlines.Invalidated += (s, e) => InlineHost?.Invalidate(); } /// @@ -37,13 +34,13 @@ namespace Avalonia.Controls.Documents [Content] public InlineCollection Inlines { get; } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { if (Inlines.HasComplexContent) { foreach (var inline in Inlines) { - inline.BuildTextRun(textRuns, parent); + inline.BuildTextRun(textRuns); } } else diff --git a/src/Avalonia.Controls/Documents/TextElement.cs b/src/Avalonia.Controls/Documents/TextElement.cs index d8e13554b5..faf869cce6 100644 --- a/src/Avalonia.Controls/Documents/TextElement.cs +++ b/src/Avalonia.Controls/Documents/TextElement.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; namespace Avalonia.Controls.Documents { @@ -251,10 +250,7 @@ namespace Avalonia.Controls.Documents control.SetValue(ForegroundProperty, value); } - /// - /// Raised when the visual representation of the text element changes. - /// - public event EventHandler? Invalidated; + internal IInlineHost? InlineHost { get; set; } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { @@ -269,14 +265,9 @@ namespace Avalonia.Controls.Documents case nameof(FontWeight): case nameof(FontStretch): case nameof(Foreground): - Invalidate(); + InlineHost?.Invalidate(); break; } } - - /// - /// Raises the event. - /// - protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty); } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c698f0ff3b..3bcb74eee6 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// /// A control that displays a block of text. /// - public class TextBlock : Control, IInlinesHost + public class TextBlock : Control, IInlineHost { /// /// Defines the property. @@ -155,9 +155,7 @@ namespace Avalonia.Controls /// public TextBlock() { - Inlines = new InlineCollection(this); - - Inlines.Invalidated += InlinesChanged; + Inlines = new InlineCollection(this, this); } /// @@ -211,7 +209,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets the inlines. + /// Gets the inlines. /// [Content] public InlineCollection Inlines { get; } @@ -569,7 +567,7 @@ namespace Avalonia.Controls foreach (var inline in Inlines) { - inline.BuildTextRun(textRuns, this); + inline.BuildTextRun(textRuns); } textSource = new InlinesTextSource(textRuns); @@ -667,8 +665,6 @@ namespace Avalonia.Controls case nameof (Padding): case nameof (LineHeight): case nameof (MaxLines): - - case nameof (InlinesProperty): case nameof (Text): case nameof (TextDecorations): @@ -685,7 +681,7 @@ namespace Avalonia.Controls InvalidateTextLayout(); } - void IInlinesHost.AddVisualChild(IControl child) + void IInlineHost.AddVisualChild(IControl child) { if (child.VisualParent == null) { @@ -693,6 +689,11 @@ namespace Avalonia.Controls } } + void IInlineHost.Invalidate() + { + InvalidateTextLayout(); + } + private readonly struct InlinesTextSource : ITextSource { private readonly IReadOnlyList _textRuns; From 628ae788e4ce422e4576b0c6a2d49bed0ef9977d Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 19 Apr 2022 19:22:58 +0200 Subject: [PATCH 465/820] Fix PointToClient not working on macOS. --- native/Avalonia.Native/src/OSX/window.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index d16c466fe6..4426e7fdff 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -457,7 +457,7 @@ public: } point = ConvertPointY(point); - NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; From 5bacd9144383ac6c43226b974f9bbeea16c6b51d Mon Sep 17 00:00:00 2001 From: Kevin Ivarsen Date: Tue, 19 Apr 2022 22:16:38 -0700 Subject: [PATCH 466/820] Fix #6603: PInvokeStackImbalance error caused by incorrect signature for WindowsDeleteString function. When PreserveSig=false, a function that natively returns an HRESULT and has no final [out] parameter should instead be marked as void. --- src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs index 5026fbaaba..89cde01ff4 100644 --- a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs +++ b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs @@ -23,7 +23,7 @@ namespace Avalonia.Win32.WinRT [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall, PreserveSig = false)] - internal static extern unsafe IntPtr WindowsDeleteString(IntPtr hString); + internal static extern unsafe void WindowsDeleteString(IntPtr hString); [DllImport("Windows.UI.Composition", EntryPoint = "DllGetActivationFactory", CallingConvention = CallingConvention.StdCall, PreserveSig = false)] From 050ac5fbba104fa7606cc5c3d3618f837f158731 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 20 Apr 2022 13:53:38 +0200 Subject: [PATCH 467/820] Fix line metrics for empty lines that are processed by TextWrapping --- .../Media/TextFormatting/TextFormatterImpl.cs | 26 +++++++++++++++-- .../Media/TextFormatting/TextLayout.cs | 29 ++----------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 7241c62472..be07745d89 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -8,6 +8,8 @@ namespace Avalonia.Media.TextFormatting { internal class TextFormatterImpl : TextFormatter { + private static readonly char[] s_empty = { ' ' }; + /// public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null) @@ -524,6 +526,27 @@ namespace Avalonia.Media.TextFormatting return measuredLength != 0; } + /// + /// Creates an empty text line. + /// + /// The empty text line. + public static TextLineImpl CreateEmptyTextLine(int firstTextSourceIndex, TextParagraphProperties paragraphProperties) + { + var flowDirection = paragraphProperties.FlowDirection; + var properties = paragraphProperties.DefaultTextRunProperties; + var glyphTypeface = properties.Typeface.GlyphTypeface; + var text = new ReadOnlySlice(s_empty, firstTextSourceIndex, 1); + var glyph = glyphTypeface.GetGlyph(s_empty[0]); + var glyphInfos = new[] { new GlyphInfo(glyph, firstTextSourceIndex) }; + + var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize, + (sbyte)flowDirection); + + var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; + + return new TextLineImpl(textRuns, firstTextSourceIndex, 1, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); + } + /// /// Performs text wrapping returns a list of text lines. /// @@ -540,8 +563,7 @@ namespace Avalonia.Media.TextFormatting { if(textRuns.Count == 0) { - return new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, flowDirection); - + return CreateEmptyTextLine(firstTextSourceIndex, paragraphProperties); } if (!TryMeasureLength(textRuns, paragraphWidth, out var measuredLength)) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index c6692b6203..0df608cb34 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs @@ -10,8 +10,6 @@ namespace Avalonia.Media.TextFormatting /// public class TextLayout { - private static readonly char[] s_empty = { ' ' }; - private readonly ITextSource _textSource; private readonly TextParagraphProperties _paragraphProperties; private readonly TextTrimming _textTrimming; @@ -408,32 +406,11 @@ namespace Avalonia.Media.TextFormatting height += textLine.Height; } - /// - /// Creates an empty text line. - /// - /// The empty text line. - private TextLine CreateEmptyTextLine(int firstTextSourceIndex) - { - var flowDirection = _paragraphProperties.FlowDirection; - var properties = _paragraphProperties.DefaultTextRunProperties; - var glyphTypeface = properties.Typeface.GlyphTypeface; - var text = new ReadOnlySlice(s_empty, firstTextSourceIndex, 1); - var glyph = glyphTypeface.GetGlyph(s_empty[0]); - var glyphInfos = new[] { new GlyphInfo(glyph, firstTextSourceIndex) }; - - var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize, - (sbyte)flowDirection); - - var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; - - return new TextLineImpl(textRuns, firstTextSourceIndex, 1, MaxWidth, _paragraphProperties, flowDirection).FinalizeLine(); - } - private IReadOnlyList CreateTextLines() { if (MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight)) { - var textLine = CreateEmptyTextLine(0); + var textLine = TextFormatterImpl.CreateEmptyTextLine(0, _paragraphProperties); Bounds = new Rect(0,0,0, textLine.Height); @@ -457,7 +434,7 @@ namespace Avalonia.Media.TextFormatting { if(previousLine != null && previousLine.NewLineLength > 0) { - var emptyTextLine = CreateEmptyTextLine(_textSourceLength); + var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, _paragraphProperties); textLines.Add(emptyTextLine); @@ -506,7 +483,7 @@ namespace Avalonia.Media.TextFormatting //Make sure the TextLayout always contains at least on empty line if(textLines.Count == 0) { - var textLine = CreateEmptyTextLine(0); + var textLine = TextFormatterImpl.CreateEmptyTextLine(0, _paragraphProperties); textLines.Add(textLine); From f9dbbb3da1d42b6450ac10b6485d118fcc5fa0aa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 16:35:53 +0200 Subject: [PATCH 468/820] Make UpdateDataValidation non-generic. --- src/Avalonia.Base/AvaloniaObject.cs | 12 ++++---- src/Avalonia.Controls/AutoCompleteBox.cs | 10 +++++-- src/Avalonia.Controls/Button.cs | 9 ++++-- .../Calendar/CalendarDatePicker.cs | 4 +-- src/Avalonia.Controls/MenuItem.cs | 9 ++++-- .../NumericUpDown/NumericUpDown.cs | 10 +++++-- .../Primitives/SelectingItemsControl.cs | 10 +++++-- src/Avalonia.Controls/Slider.cs | 7 +++-- src/Avalonia.Controls/TextBox.cs | 7 +++-- .../AvaloniaObjectTests_DataValidation.cs | 28 +++++++++---------- 10 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index bc1e95805f..1f14ddede4 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -646,10 +646,12 @@ namespace Avalonia /// enabled. /// /// The property. - /// The new binding value for the property. - protected virtual void UpdateDataValidation( - AvaloniaProperty property, - BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected virtual void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { } @@ -860,7 +862,7 @@ namespace Avalonia if (metadata.EnableDataValidation == true) { - UpdateDataValidation(property, value); + UpdateDataValidation(property, value.Type, value.Error); } } diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 3316c06bf5..5c95932c1f 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -1346,12 +1346,16 @@ namespace Avalonia.Controls /// enabled. /// /// The property. - /// The new binding value for the property. - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == TextProperty || property == SelectedItemProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index a4a147e0f3..0ef1ba4c8c 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -498,12 +498,15 @@ namespace Avalonia.Controls protected override AutomationPeer OnCreateAutomationPeer() => new ButtonAutomationPeer(this); /// - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { - base.UpdateDataValidation(property, value); + base.UpdateDataValidation(property, state, error); if (property == CommandProperty) { - if (value.Type == BindingValueType.BindingError) + if (state == BindingValueType.BindingError) { if (_commandCanExecute) { diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index 0ac2056ed1..0409eb30aa 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -540,11 +540,11 @@ namespace Avalonia.Controls } } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error) { if (property == SelectedDateProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 955af8888b..619eafb71b 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -501,12 +501,15 @@ namespace Avalonia.Controls return new MenuItemAutomationPeer(this); } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { - base.UpdateDataValidation(property, value); + base.UpdateDataValidation(property, state, error); if (property == CommandProperty) { - _commandBindingError = value.Type == BindingValueType.BindingError; + _commandBindingError = state == BindingValueType.BindingError; if (_commandBindingError && _commandCanExecute) { _commandCanExecute = false; diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index fbbaab6182..4d86a0f17c 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -403,12 +403,16 @@ namespace Avalonia.Controls /// enabled. /// /// The property. - /// The new binding value for the property. - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == TextProperty || property == ValueProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 6f2554bef3..bff6799792 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -501,12 +501,16 @@ namespace Avalonia.Controls.Primitives /// enabled. /// /// The property. - /// The new binding value for the property. - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == SelectedItemProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index f0a0fba1af..64dfce22d4 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -361,11 +361,14 @@ namespace Avalonia.Controls Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue; } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == ValueProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 1f3dbc87db..0be58e7fcc 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1262,11 +1262,14 @@ namespace Avalonia.Controls return new TextBoxAutomationPeer(this); } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == TextProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs index 65f03b3eca..d48e58136a 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs @@ -52,14 +52,14 @@ namespace Avalonia.Base.UnitTests source.OnNext(BindingValue.DataValidationError(new Exception())); source.OnNext(7); - var result = target.Notifications.Cast>().ToList(); + var result = target.Notifications; Assert.Equal(4, result.Count); - Assert.Equal(BindingValueType.Value, result[0].Type); - Assert.Equal(6, result[0].Value); - Assert.Equal(BindingValueType.BindingError, result[1].Type); - Assert.Equal(BindingValueType.DataValidationError, result[2].Type); - Assert.Equal(BindingValueType.Value, result[3].Type); - Assert.Equal(7, result[3].Value); + Assert.Equal(BindingValueType.Value, result[0].type); + Assert.Equal(6, result[0].value); + Assert.Equal(BindingValueType.BindingError, result[1].type); + Assert.Equal(BindingValueType.DataValidationError, result[2].type); + Assert.Equal(BindingValueType.Value, result[3].type); + Assert.Equal(7, result[3].value); } [Fact] @@ -72,8 +72,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.NonValidatedDirectProperty, source); source.OnNext(1); - var result = target.Notifications.Cast>().ToList(); - Assert.Equal(1, result.Count); + Assert.Equal(1, target.Notifications.Count); } [Fact] @@ -154,13 +153,14 @@ namespace Avalonia.Base.UnitTests set { SetAndRaise(ValidatedDirectStringProperty, ref _directString, value); } } - public IList Notifications { get; } = new List(); + public List<(BindingValueType type, object value)> Notifications { get; } = new(); - protected override void UpdateDataValidation( - AvaloniaProperty property, - BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception error) { - Notifications.Add(value); + Notifications.Add((state, GetValue(property))); } } From 7f469752d55097d4dcf014e23fd2f0798f199e68 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 16:43:20 +0200 Subject: [PATCH 469/820] Remove generic methods from IInteractive. --- .../Interactivity/IInteractive.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/Avalonia.Base/Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs index afda29e329..6d7dcd64f4 100644 --- a/src/Avalonia.Base/Interactivity/IInteractive.cs +++ b/src/Avalonia.Base/Interactivity/IInteractive.cs @@ -28,21 +28,6 @@ namespace Avalonia.Interactivity RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, bool handledEventsToo = false); - /// - /// Adds a handler for the specified routed event. - /// - /// The type of the event's args. - /// The routed event. - /// The handler. - /// The routing strategies to listen to. - /// Whether handled events should also be listened for. - /// A disposable that terminates the event subscription. - void AddHandler( - RoutedEvent routedEvent, - EventHandler handler, - RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, - bool handledEventsToo = false) where TEventArgs : RoutedEventArgs; - /// /// Removes a handler for the specified routed event. /// @@ -50,15 +35,6 @@ namespace Avalonia.Interactivity /// The handler. void RemoveHandler(RoutedEvent routedEvent, Delegate handler); - /// - /// Removes a handler for the specified routed event. - /// - /// The type of the event's args. - /// The routed event. - /// The handler. - void RemoveHandler(RoutedEvent routedEvent, EventHandler handler) - where TEventArgs : RoutedEventArgs; - /// /// Adds the object's handlers for a routed event to an event route. /// From 4ec36c97e2810c8e1bb48f85339d76e96e502b78 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 16:47:54 +0200 Subject: [PATCH 470/820] Remove generic methods from IDispatcher. --- src/Avalonia.Base/Threading/IDispatcher.cs | 26 ---------------------- 1 file changed, 26 deletions(-) diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index eccd42bd4e..713a7ac4d7 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -26,15 +26,6 @@ namespace Avalonia.Threading /// The priority with which to invoke the method. void Post(Action action, DispatcherPriority priority = default); - /// - /// Posts an action that will be invoked on the dispatcher thread. - /// - /// type of argument - /// The method to call. - /// The argument of method to call. - /// The priority with which to invoke the method. - void Post(Action action, T arg, DispatcherPriority priority = default); - /// /// Invokes a action on the dispatcher thread. /// @@ -43,14 +34,6 @@ namespace Avalonia.Threading /// A task that can be used to track the method's execution. Task InvokeAsync(Action action, DispatcherPriority priority = default); - /// - /// Invokes a method on the dispatcher thread. - /// - /// The method. - /// The priority with which to invoke the method. - /// A task that can be used to track the method's execution. - Task InvokeAsync(Func function, DispatcherPriority priority = default); - /// /// Queues the specified work to run on the dispatcher thread and returns a proxy for the /// task returned by . @@ -59,14 +42,5 @@ namespace Avalonia.Threading /// The priority with which to invoke the method. /// A task that represents a proxy for the task returned by . Task InvokeAsync(Func function, DispatcherPriority priority = default); - - /// - /// Queues the specified work to run on the dispatcher thread and returns a proxy for the - /// task returned by . - /// - /// The work to execute asynchronously. - /// The priority with which to invoke the method. - /// A task that represents a proxy for the task returned by . - Task InvokeAsync(Func> function, DispatcherPriority priority = default); } } From fa44075d26873d80b6fba5df8492d774fadec589 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 17:19:02 +0200 Subject: [PATCH 471/820] Make utility methods non-virtual. Not sure why these method were virtual anyway: any customization should be done by overriding the non-generic `CreateItemContainerGenerator` method. --- src/Avalonia.Controls/TreeView.cs | 2 +- src/Avalonia.Controls/TreeViewItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 1d806913dd..b2a188a2ea 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -401,7 +401,7 @@ namespace Avalonia.Controls protected virtual ITreeItemContainerGenerator CreateTreeItemContainerGenerator() => CreateTreeItemContainerGenerator(); - protected virtual ITreeItemContainerGenerator CreateTreeItemContainerGenerator() where TVItem: TreeViewItem, new() + protected ITreeItemContainerGenerator CreateTreeItemContainerGenerator() where TVItem: TreeViewItem, new() { return new TreeItemContainerGenerator( this, diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index a0a3c09942..490b0b3ce3 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -96,7 +96,7 @@ namespace Avalonia.Controls protected override IItemContainerGenerator CreateItemContainerGenerator() => CreateTreeItemContainerGenerator(); /// - protected virtual ITreeItemContainerGenerator CreateTreeItemContainerGenerator() + protected ITreeItemContainerGenerator CreateTreeItemContainerGenerator() where TVItem: TreeViewItem, new() { return new TreeItemContainerGenerator( From 6df672e4c078bae8dd0259825032af68824f3f08 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 22:30:45 +0200 Subject: [PATCH 472/820] Remove IAvaloniaPropertyVisitor. It was only used internally in creating `ISetterInstance`s so add a virtual method to `AvaloniaProperty` to do this explicitly without generic virtual methods. --- src/Avalonia.Base/AvaloniaProperty.cs | 11 +--- src/Avalonia.Base/DirectPropertyBase.cs | 32 +++++++-- src/Avalonia.Base/StyledPropertyBase.cs | 32 +++++++-- src/Avalonia.Base/Styling/Setter.cs | 65 +------------------ .../Utilities/IAvaloniaPropertyVisitor.cs | 32 --------- .../AvaloniaPropertyTests.cs | 11 ++-- 6 files changed, 62 insertions(+), 121 deletions(-) delete mode 100644 src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 62ca971412..fd43ced196 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Avalonia.Data; using Avalonia.Data.Core; +using Avalonia.Styling; using Avalonia.Utilities; namespace Avalonia @@ -454,15 +455,6 @@ namespace Avalonia return Name; } - /// - /// Uses the visitor pattern to resolve an untyped property to a typed property. - /// - /// The type of user data passed. - /// The visitor which will accept the typed property. - /// The user data to pass. - public abstract void Accept(IAvaloniaPropertyVisitor visitor, ref TData data) - where TData : struct; - /// /// Routes an untyped ClearValue call to a typed call. /// @@ -508,6 +500,7 @@ namespace Avalonia BindingPriority priority); internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent); + internal abstract ISetterInstance CreateSetterInstance(IStyleable target, object? value); /// /// Overrides the metadata for the property on the specified type. diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 6e3baea99b..9c1ffce24c 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Data; using Avalonia.Reactive; +using Avalonia.Styling; using Avalonia.Utilities; namespace Avalonia @@ -120,12 +121,6 @@ namespace Avalonia base.OverrideMetadata(type, metadata); } - /// - public override void Accept(IAvaloniaPropertyVisitor visitor, ref TData data) - { - visitor.Visit(this, ref data); - } - /// internal override void RouteClearValue(AvaloniaObject o) { @@ -181,5 +176,30 @@ namespace Avalonia { throw new NotSupportedException("Direct properties do not support inheritance."); } + + internal override ISetterInstance CreateSetterInstance(IStyleable target, object? value) + { + if (value is IBinding binding) + { + return new PropertySetterBindingInstance( + target, + this, + binding); + } + else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) + { + return new PropertySetterLazyInstance( + target, + this, + () => (TValue)template.Build()); + } + else + { + return new PropertySetterInstance( + target, + this, + (TValue)value!); + } + } } } diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index f94723866a..dd5eb703ea 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; +using Avalonia.Styling; using Avalonia.Utilities; namespace Avalonia @@ -158,12 +159,6 @@ namespace Avalonia base.OverrideMetadata(type, metadata); } - /// - public override void Accept(IAvaloniaPropertyVisitor visitor, ref TData data) - { - visitor.Visit(this, ref data); - } - /// /// Gets the string representation of the property. /// @@ -237,6 +232,31 @@ namespace Avalonia o.InheritanceParentChanged(this, oldParent); } + internal override ISetterInstance CreateSetterInstance(IStyleable target, object? value) + { + if (value is IBinding binding) + { + return new PropertySetterBindingInstance( + target, + this, + binding); + } + else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) + { + return new PropertySetterLazyInstance( + target, + this, + () => (TValue)template.Build()); + } + else + { + return new PropertySetterInstance( + target, + this, + (TValue)value!); + } + } + private object? GetDefaultBoxedValue(Type type) { _ = type ?? throw new ArgumentNullException(nameof(type)); diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index 168a882499..b4b3399022 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -16,7 +16,7 @@ namespace Avalonia.Styling /// A is used to set a value on a /// depending on a condition. /// - public class Setter : ISetter, IAnimationSetter, IAvaloniaPropertyVisitor + public class Setter : ISetter, IAnimationSetter { private object? _value; @@ -68,68 +68,7 @@ namespace Avalonia.Styling throw new InvalidOperationException("Setter.Property must be set."); } - var data = new SetterVisitorData - { - target = target, - value = Value, - }; - - Property.Accept(this, ref data); - return data.result!; - } - - void IAvaloniaPropertyVisitor.Visit( - StyledPropertyBase property, - ref SetterVisitorData data) - { - if (data.value is IBinding binding) - { - data.result = new PropertySetterBindingInstance( - data.target, - property, - binding); - } - else if (data.value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(property.PropertyType)) - { - data.result = new PropertySetterLazyInstance( - data.target, - property, - () => (T)template.Build()); - } - else - { - data.result = new PropertySetterInstance( - data.target, - property, - (T)data.value!); - } - } - - void IAvaloniaPropertyVisitor.Visit( - DirectPropertyBase property, - ref SetterVisitorData data) - { - if (data.value is IBinding binding) - { - data.result = new PropertySetterBindingInstance( - data.target, - property, - binding); - } - else if (data.value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(property.PropertyType)) - { - data.result = new PropertySetterLazyInstance( - data.target, - property, - () => (T)template.Build()); - } - else - { - data.result = new PropertySetterInstance( - data.target, - property, - (T)data.value!); - } + return Property.CreateSetterInstance(target, Value); } private struct SetterVisitorData diff --git a/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs b/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs deleted file mode 100644 index 6a8df91b81..0000000000 --- a/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Avalonia.Utilities -{ - /// - /// A visitor to resolve an untyped to a typed property. - /// - /// The type of user data passed. - /// - /// Pass an instance that implements this interface to - /// - /// in order to resolve un untyped to a typed - /// or . - /// - public interface IAvaloniaPropertyVisitor - where TData : struct - { - /// - /// Called when the property is a styled property. - /// - /// The property value type. - /// The property. - /// The user data. - void Visit(StyledPropertyBase property, ref TData data); - - /// - /// Called when the property is a direct property. - /// - /// The property value type. - /// The property. - /// The user data. - void Visit(DirectPropertyBase property, ref TData data); - } -} diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs index ee29c82345..f3f39b465b 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Data; +using Avalonia.Styling; using Avalonia.Utilities; using Xunit; @@ -146,11 +147,6 @@ namespace Avalonia.Base.UnitTests OverrideMetadata(typeof(T), metadata); } - public override void Accept(IAvaloniaPropertyVisitor vistor, ref TData data) - { - throw new NotImplementedException(); - } - internal override IDisposable RouteBind( AvaloniaObject o, IObservable> source, @@ -186,6 +182,11 @@ namespace Avalonia.Base.UnitTests { throw new NotImplementedException(); } + + internal override ISetterInstance CreateSetterInstance(IStyleable target, object value) + { + throw new NotImplementedException(); + } } private class Class1 : AvaloniaObject From c53e5307a02e37a5781429514ef5f0fa935ed5f8 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 20 Apr 2022 23:30:23 +0200 Subject: [PATCH 473/820] Fix WeakHashList losing one item when upgrading storage. --- src/Avalonia.Base/Utilities/WeakHashList.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs index 32668872da..51aa24dd6c 100644 --- a/src/Avalonia.Base/Utilities/WeakHashList.cs +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -104,8 +104,10 @@ internal class WeakHashList where T : class if (existing!.TryGetTarget(out var target)) Add(target); } - _arr = null; + Add(item); + + _arr = null; } public void Remove(T item) From 82835ef95708edaf80a4ac2406179192d2db3d59 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 21 Apr 2022 09:53:28 +0200 Subject: [PATCH 474/820] Use nested styles in Button template. And related fixes to make this work. --- src/Avalonia.Base/Styling/NestingSelector.cs | 2 +- src/Avalonia.Base/Styling/Style.cs | 8 +- .../Controls/Button.xaml | 80 ++++++++++--------- .../Styling/SelectorTests_Nesting.cs | 68 +++++++++++++++- 4 files changed, 116 insertions(+), 42 deletions(-) diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index 1be54dea3c..741eb7e9ca 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -17,7 +17,7 @@ namespace Avalonia.Styling { if (parent is Style s && s.Selector is Selector selector) { - return selector.Match(control, null, subscribe); + return selector.Match(control, (parent as Style)?.Parent, subscribe); } throw new InvalidOperationException( diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 6020dfe25f..7a83322355 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -120,15 +120,19 @@ namespace Avalonia.Styling instance.Start(); } + var result = match.Result; + if (_children is not null) { foreach (var child in _children) { - child.TryAttach(target, host); + var childResult = child.TryAttach(target, host); + if (childResult > result) + result = childResult; } } - return match.Result; + return result; } public bool TryGetResource(object key, out object? result) diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index f545206a2f..282f575605 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -7,9 +7,11 @@ + 8,5,8,6 + - - + + - + - + - + - + - + + + + + - diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index eeb2fad996..f7e8793ede 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -9,7 +9,7 @@ namespace Avalonia.Base.UnitTests.Styling public class SelectorTests_Nesting { [Fact] - public void Nesting_Class_Doesnt_Match_Parent_Selector() + public void Nesting_Class_Doesnt_Match_Parent_OfType_Selector() { var control = new Control2(); Style nested; @@ -26,7 +26,7 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Or_Nesting_Class_Doesnt_Match_Parent_Selector() + public void Or_Nesting_Class_Doesnt_Match_Parent_OfType_Selector() { var control = new Control2(); Style nested; @@ -45,7 +45,7 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Or_Nesting_Child_OfType_Does_Not_Match_Parent_Selector() + public void Or_Nesting_Child_OfType_Doesnt_Match_Parent_OfType_Selector() { var control = new Control1(); var panel = new DockPanel { Children = { control } }; @@ -64,6 +64,34 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Equal(SelectorMatchResult.NeverThisInstance, match.Result); } + [Fact] + public void Double_Nesting_Class_Doesnt_Match_Grandparent_OfType_Selector() + { + var control = new Control2 + { + Classes = { "foo", "bar" }, + }; + + Style parent; + Style nested; + var grandparent = new Style(x => x.OfType()) + { + Children = + { + (parent = new Style(x => x.Nesting().Class("foo")) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("bar"))) + } + }) + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); + } + [Fact] public void Nesting_Class_Matches() { @@ -87,6 +115,40 @@ namespace Avalonia.Base.UnitTests.Styling Assert.False(sink.Active); } + [Fact] + public void Double_Nesting_Class_Matches() + { + var control = new Control1 + { + Classes = { "foo", "bar" }, + }; + + Style parent; + Style nested; + var grandparent = new Style(x => x.OfType()) + { + Children = + { + (parent = new Style(x => x.Nesting().Class("foo")) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("bar"))) + } + }) + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Remove("foo"); + Assert.False(sink.Active); + } + [Fact] public void Or_Nesting_Class_Matches() { From 30531df3273f576a11ed2ca03c07a07d740a5eea Mon Sep 17 00:00:00 2001 From: Kevin Ivarsen Date: Thu, 21 Apr 2022 01:43:29 -0700 Subject: [PATCH 475/820] Disable clipping in DevTools content/padding/margin adorner so that the margin part is visible (fixes #8025) --- src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 2553ae90ce..58807b489e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -36,6 +36,7 @@ namespace Avalonia.Diagnostics.Views new Border { BorderBrush = new SolidColorBrush(Colors.Yellow, 0.5) } }, }; + AdornerLayer.SetIsClipEnabled(_adorner, false); } protected void AddAdorner(object? sender, PointerEventArgs e) From 066c81b1ac3784b36a151bcf3b90d8cba7f5254c Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 21 Apr 2022 11:35:30 +0200 Subject: [PATCH 476/820] Another fix for WeakHashList. Added basic unit tests. --- src/Avalonia.Base/Utilities/WeakHashList.cs | 13 ++-- .../Utilities/WeakHashListTests.cs | 66 +++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs index 51aa24dd6c..df480aa062 100644 --- a/src/Avalonia.Base/Utilities/WeakHashList.cs +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -6,6 +6,8 @@ namespace Avalonia.Utilities; internal class WeakHashList where T : class { + public const int DefaultArraySize = 8; + private struct Key { public WeakReference? Weak; @@ -63,7 +65,8 @@ internal class WeakHashList where T : class WeakReference?[]? _arr; int _arrCount; - public bool IsEmpty => _dic == null || _dic.Count == 0; + public bool IsEmpty => _dic is not null ? _dic.Count == 0 : _arrCount == 0; + public bool NeedCompact { get; private set; } public void Add(T item) @@ -79,7 +82,7 @@ internal class WeakHashList where T : class } if (_arr == null) - _arr = new WeakReference[8]; + _arr = new WeakReference[DefaultArraySize]; if (_arrCount < _arr.Length) { @@ -108,6 +111,7 @@ internal class WeakHashList where T : class Add(item); _arr = null; + _arrCount = 0; } public void Remove(T item) @@ -119,7 +123,7 @@ internal class WeakHashList where T : class if (_arr[c]?.TryGetTarget(out var target) == true && target == item) { _arr[c] = null; - Compact(); + ArrCompact(); return; } } @@ -219,7 +223,6 @@ internal class WeakHashList where T : class } if (_dic != null) { - foreach (var kvp in _dic) { if (kvp.Key.Weak?.TryGetTarget(out var target) == true) @@ -235,4 +238,4 @@ internal class WeakHashList where T : class return pooled; } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs new file mode 100644 index 0000000000..9ae82e2be0 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs @@ -0,0 +1,66 @@ +using Avalonia.Utilities; +using Xunit; + +namespace Avalonia.Base.UnitTests.Utilities; + +public class WeakHashListTests +{ + [Fact] + public void Is_Empty_Works() + { + var target = new WeakHashList(); + + Assert.True(target.IsEmpty); + + target.Add("1"); + + Assert.False(target.IsEmpty); + + target.Remove("1"); + + Assert.True(target.IsEmpty); + + // Fill array storage. + var arrMaxSize = WeakHashList.DefaultArraySize; + + for (int i = 0; i < arrMaxSize; i++) + { + target.Add(i.ToString()); + } + + Assert.False(target.IsEmpty); + + // This goes above array storage and upgrades to a dictionary. + target.Add(arrMaxSize.ToString()); + + Assert.False(target.IsEmpty); + + // Remove everything, this should still keep an empty dictionary. + for (int i = 0; i < arrMaxSize + 1; i++) + { + target.Remove(i.ToString()); + } + + Assert.True(target.IsEmpty); + } + + [Fact] + public void Array_Compact_After_Remove_Works() + { + var target = new WeakHashList(); + + // Use all slots in array storage. + var arrMaxSize = WeakHashList.DefaultArraySize; + + for (int i = 0; i < arrMaxSize; i++) + { + target.Add(i.ToString()); + } + + // This should compact the array. + target.Remove("3"); + + // And new value should fill empty space. + target.Add("42"); + } +} From 3e6bc0b48d4ea1e32a7b552b5c3c54c093a6592a Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 21 Apr 2022 14:17:26 +0200 Subject: [PATCH 477/820] Some more hit testing fixes --- src/Avalonia.Base/Media/GlyphRun.cs | 44 +++++---- .../Media/TextFormatting/TextFormatterImpl.cs | 2 +- .../Media/TextFormatting/TextLineImpl.cs | 74 +++++++------- .../Media/TextFormatting/TextLineTests.cs | 97 +++++++++++++------ 4 files changed, 133 insertions(+), 84 deletions(-) diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index ec270d796a..9a2645f03d 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -49,7 +49,7 @@ namespace Avalonia.Media IReadOnlyList? glyphClusters = null, int biDiLevel = 0) { - _glyphTypeface = glyphTypeface; + _glyphTypeface = glyphTypeface; FontRenderingEmSize = fontRenderingEmSize; @@ -204,7 +204,7 @@ namespace Avalonia.Media public double GetDistanceFromCharacterHit(CharacterHit characterHit) { var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - + var distance = 0.0; if (IsLeftToRight) @@ -223,7 +223,7 @@ namespace Avalonia.Media } var glyphIndex = FindGlyphIndex(characterIndex); - + if (GlyphClusters != null) { var currentCluster = GlyphClusters[glyphIndex]; @@ -249,7 +249,7 @@ namespace Avalonia.Media { //RightToLeft var glyphIndex = FindGlyphIndex(characterIndex); - + if (GlyphClusters != null) { if (characterIndex > GlyphClusters[0]) @@ -284,13 +284,13 @@ namespace Avalonia.Media public CharacterHit GetCharacterHitFromDistance(double distance, out bool isInside) { var characterIndex = 0; - + // Before if (distance <= 0) { isInside = false; - if(GlyphClusters != null) + if (GlyphClusters != null) { characterIndex = GlyphClusters[characterIndex]; } @@ -307,11 +307,11 @@ namespace Avalonia.Media characterIndex = GlyphIndices.Count - 1; - if(GlyphClusters != null) + if (GlyphClusters != null) { characterIndex = GlyphClusters[characterIndex]; } - + var lastCharacterHit = FindNearestCharacterHit(characterIndex, out _); return IsLeftToRight ? lastCharacterHit : new CharacterHit(lastCharacterHit.FirstCharacterIndex); @@ -327,7 +327,7 @@ namespace Avalonia.Media var advance = GetGlyphAdvance(index, out var cluster); characterIndex = cluster; - + if (distance > currentX && distance <= currentX + advance) { break; @@ -345,7 +345,7 @@ namespace Avalonia.Media var advance = GetGlyphAdvance(index, out var cluster); characterIndex = cluster; - + if (currentX - advance < distance) { break; @@ -554,6 +554,16 @@ namespace Avalonia.Media nextCluster = GlyphClusters[currentIndex]; } + if (nextCluster < Characters.Start) + { + nextCluster = Characters.Start; + } + + if (cluster < Characters.Start) + { + cluster = Characters.Start; + } + int trailingLength; if (nextCluster == cluster) @@ -577,7 +587,7 @@ namespace Avalonia.Media private double GetGlyphAdvance(int index, out int cluster) { cluster = GlyphClusters != null ? GlyphClusters[index] : index; - + if (GlyphAdvances != null) { return GlyphAdvances[index]; @@ -603,7 +613,7 @@ namespace Avalonia.Media var widthIncludingTrailingWhitespace = 0d; var trailingWhitespaceLength = GetTrailingWhitespaceLength(out var newLineLength, out var glyphCount); - + for (var index = 0; index < GlyphIndices.Count; index++) { var advance = GetGlyphAdvance(index, out _); @@ -615,7 +625,7 @@ namespace Avalonia.Media if (IsLeftToRight) { - for (var index = GlyphIndices.Count - glyphCount; index { new ShapedTextCharacters(shapedBuffer, properties) }; - return new TextLineImpl(textRuns, firstTextSourceIndex, 1, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); + return new TextLineImpl(textRuns, firstTextSourceIndex, 0, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); } /// diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 30e3728d1f..b480774d1d 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -183,6 +183,7 @@ namespace Avalonia.Media.TextFormatting case ShapedTextCharacters shapedRun: { characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + break; } default: @@ -426,31 +427,42 @@ namespace Avalonia.Media.TextFormatting if (nextRun != null) { - if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) + switch (nextRun) { - goto skip; - } + case ShapedTextCharacters when currentRun is ShapedTextCharacters: + { + if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) + { + goto skip; + } - if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) - { - goto skip; - } + if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) + { + goto skip; + } - if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) - { - goto skip; - } + if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) + { + goto skip; + } - if (currentRun.Text.End < firstTextSourceCharacterIndex) - { - goto skip; - } + if (currentRun.Text.End < firstTextSourceCharacterIndex) + { + goto skip; + } - goto noop; + goto noop; + } + default: + { + goto noop; + } + } skip: { startX += currentRun.Size.Width; + currentPosition += currentRun.TextSourceLength; } continue; @@ -460,7 +472,6 @@ namespace Avalonia.Media.TextFormatting } } - var endX = startX; var endOffset = 0d; @@ -520,11 +531,13 @@ namespace Avalonia.Media.TextFormatting } default: { - if (firstTextSourceCharacterIndex + textLength >= currentRun.Text.Start + currentRun.Text.Length) + if (currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex + textLength) { endX += currentRun.Size.Width; } + currentPosition += currentRun.TextSourceLength; + break; } } @@ -538,7 +551,9 @@ namespace Avalonia.Media.TextFormatting if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) { - var textBounds = new TextBounds(currentRect.WithWidth(currentRect.Width + width), currentDirection); + currentRect = currentRect.WithWidth(currentRect.Width + width); + + var textBounds = new TextBounds(currentRect, currentDirection); result[result.Count - 1] = textBounds; } @@ -551,21 +566,9 @@ namespace Avalonia.Media.TextFormatting if (currentDirection == FlowDirection.LeftToRight) { - if (nextRun != null) - { - if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= firstTextSourceCharacterIndex + textLength) - { - break; - } - - currentPosition = nextRun.Text.End; - } - else + if (currentPosition >= firstTextSourceCharacterIndex + textLength) { - if (currentPosition >= firstTextSourceCharacterIndex + textLength) - { - break; - } + break; } } else @@ -575,10 +578,7 @@ namespace Avalonia.Media.TextFormatting break; } - if (currentPosition != currentRun.Text.Start) - { - endX += currentRun.Size.Width - endOffset; - } + endX += currentRun.Size.Width - endOffset; } lastDirection = currentDirection; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index e9bc792be3..b58d9051f3 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -70,12 +70,12 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } } - + [Fact] public void Should_Get_Next_Caret_CharacterHit_Bidi() { const string text = "אבג 1 ABC"; - + using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); @@ -90,7 +90,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var clusters = new List(); - foreach (var textRun in textLine.TextRuns.OrderBy(x=> x.Text.Start)) + foreach (var textRun in textLine.TextRuns.OrderBy(x => x.Text.Start)) { var shapedRun = (ShapedTextCharacters)textRun; @@ -98,7 +98,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting shapedRun.ShapedBuffer.GlyphClusters.Reverse() : shapedRun.ShapedBuffer.GlyphClusters); } - + var nextCharacterHit = new CharacterHit(0, clusters[1] - clusters[0]); foreach (var cluster in clusters) @@ -122,7 +122,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting public void Should_Get_Previous_Caret_CharacterHit_Bidi() { const string text = "אבג 1 ABC"; - + using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); @@ -137,7 +137,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var clusters = new List(); - foreach (var textRun in textLine.TextRuns.OrderBy(x=> x.Text.Start)) + foreach (var textRun in textLine.TextRuns.OrderBy(x => x.Text.Start)) { var shapedRun = (ShapedTextCharacters)textRun; @@ -147,13 +147,13 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } clusters.Reverse(); - + var nextCharacterHit = new CharacterHit(text.Length - 1); foreach (var cluster in clusters) { var currentCaretIndex = nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength; - + Assert.Equal(cluster, currentCaretIndex); nextCharacterHit = textLine.GetPreviousCaretCharacterHit(nextCharacterHit); @@ -168,7 +168,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(lastCharacterHit.TrailingLength, nextCharacterHit.TrailingLength); } } - + [InlineData("𐐷𐐷𐐷𐐷𐐷")] [InlineData("01234567🎉\n")] [InlineData("𐐷1234")] @@ -324,7 +324,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } - Assert.Equal(currentDistance,textLine.GetDistanceFromCharacterHit(new CharacterHit(s_multiLineText.Length))); + Assert.Equal(currentDistance, textLine.GetDistanceFromCharacterHit(new CharacterHit(s_multiLineText.Length))); } } @@ -371,7 +371,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting yield return CreateData("01234 01234", 58, TextTrimming.WordEllipsis, "01234\u2026"); yield return CreateData("01234", 9, TextTrimming.CharacterEllipsis, "\u2026"); yield return CreateData("01234", 2, TextTrimming.CharacterEllipsis, ""); - + object[] CreateData(string text, double width, TextTrimming mode, string expected) { return new object[] @@ -424,7 +424,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { var defaultProperties = new GenericTextRunProperties(Typeface.Default); var textSource = new DrawableRunTextSource(); - + var formatter = new TextFormatterImpl(); var textLine = @@ -471,7 +471,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(4, textLine.TextRuns.Count); - var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3,1)); + var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3, 1)); Assert.Equal(3, currentHit.FirstCharacterIndex); Assert.Equal(0, currentHit.TrailingLength); @@ -552,11 +552,11 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting switch (textSourceIndex) { case 0: - return new CustomDrawableRun(); + return new CustomDrawableRun(); case 1: return new TextCharacters(new ReadOnlySlice(Text.AsMemory(), 1, 1, 1), new GenericTextRunProperties(Typeface.Default)); case 2: - return new CustomDrawableRun(); + return new CustomDrawableRun(); case 3: return new TextCharacters(new ReadOnlySlice(Text.AsMemory(), 3, 1, 3), new GenericTextRunProperties(Typeface.Default)); default: @@ -564,14 +564,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } } - + private class CustomDrawableRun : DrawableTextRun { public override Size Size => new(14, 14); public override double Baseline => 14; public override void Draw(DrawingContext drawingContext, Point origin) { - + } } @@ -587,29 +587,29 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var shapedTextRuns = textLine.TextRuns.Cast().ToList(); var lastCluster = -1; - + foreach (var textRun in shapedTextRuns) { var shapedBuffer = textRun.ShapedBuffer; var currentClusters = shapedBuffer.GlyphClusters.ToList(); - foreach (var currentCluster in currentClusters) + foreach (var currentCluster in currentClusters) { if (lastCluster == currentCluster) { continue; } - + glyphClusters.Add(currentCluster); lastCluster = currentCluster; } } - + return glyphClusters; } - + private static List BuildRects(TextLine textLine) { var rects = new List(); @@ -624,11 +624,11 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting foreach (var textRun in shapedTextRuns) { var shapedBuffer = textRun.ShapedBuffer; - + for (var index = 0; index < shapedBuffer.GlyphAdvances.Count; index++) { var currentCluster = shapedBuffer.GlyphClusters[index]; - + var advance = shapedBuffer.GlyphAdvances[index]; if (lastCluster != currentCluster) @@ -642,10 +642,10 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting rects.Remove(rect); rect = rect.WithWidth(rect.Width + advance); - + rects.Add(rect); } - + currentX += advance; lastCluster = currentCluster; @@ -655,8 +655,43 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting return rects; } + [Fact] - public void Should_Get_TextBounds() + public void Should_Get_TextBounds_Mixed() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var text = "0123".AsMemory(); + var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); + + var textRuns = new List + { + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption), defaultProperties), + new CustomDrawableRun(), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length + 1, text.Length), shaperOption), defaultProperties), + new CustomDrawableRun(), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2 + 2, text.Length), shaperOption), defaultProperties), + new CustomDrawableRun(), + }; + + var textSource = new FixedRunsTextSource(textRuns); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var textBounds = textLine.GetTextBounds(0, text.Length * 3 + 3); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + } + } + + [Fact] + public void Should_Get_TextBounds_BiDi() { using (Start()) { @@ -673,7 +708,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 3, text.Length), ltrOptions), defaultProperties) }; - + var textSource = new FixedRunsTextSource(textRuns); var formatter = new TextFormatterImpl(); @@ -700,12 +735,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting public TextRun? GetTextRun(int textSourceIndex) { + var currentPosition = 0; + foreach (var textRun in _textRuns) { - if(textRun.Text.Start == textSourceIndex) + if (currentPosition == textSourceIndex) { return textRun; } + + currentPosition += textRun.TextSourceLength; } return null; From 32d72930972ce5586caaf5259909c4b932945b2f Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 21 Apr 2022 15:33:01 +0200 Subject: [PATCH 478/820] Fix GetNextCharacterHit for trailing whitespace --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 7f2dde7c1e..9ac4b71a12 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -662,7 +662,7 @@ namespace Avalonia.Controls.Presenters caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - if (textLine.NewLineLength > 0 && caretIndex == textLine.FirstTextSourceIndex + textLine.Length) + if (textLine.TrailingWhitespaceLength > 0 && caretIndex == textLine.FirstTextSourceIndex + textLine.Length) { characterHit = new CharacterHit(caretIndex); } From aaf04a38dab6bb8d1dfc263e6fc922a3ce111b10 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 21 Apr 2022 15:39:09 +0200 Subject: [PATCH 479/820] Fix property GetValue --- src/Avalonia.Controls/Documents/InlineUIContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index eb12092bb8..5f08c23099 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Documents get { double baseline = Size.Height; - double baselineOffsetValue = (double)Control.GetValue(TextBlock.BaselineOffsetProperty); + double baselineOffsetValue = Control.GetValue(TextBlock.BaselineOffsetProperty); if (!MathUtilities.IsZero(baselineOffsetValue)) { From 982e2d5db090305b3f57fc46e226da26839061fa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 21 Apr 2022 16:15:36 +0200 Subject: [PATCH 480/820] Fix merge error due to changed API. And moved fields to live with other field. --- src/Avalonia.Controls/Border.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 53de95ac41..06368eb5c6 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -71,6 +71,8 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(BorderLineJoin), PenLineJoin.Miter); private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper(); + private Thickness? _layoutThickness; + private double _scale; /// /// Initializes static members of the class. @@ -90,7 +92,7 @@ namespace Avalonia.Controls AffectsMeasure(BorderThicknessProperty); } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); switch (change.Property.Name) @@ -183,9 +185,6 @@ namespace Avalonia.Controls set => SetValue(BoxShadowProperty, value); } - private Thickness? _layoutThickness; - private double _scale; - private Thickness LayoutThickness { get From f33fe3b708f1d6a5966b4ba71447d35165a4aeaf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 21 Apr 2022 20:25:16 +0200 Subject: [PATCH 481/820] Avoid checking all array values. --- src/Avalonia.Base/Utilities/WeakHashList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs index df480aa062..fe582e8a78 100644 --- a/src/Avalonia.Base/Utilities/WeakHashList.cs +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -118,7 +118,7 @@ internal class WeakHashList where T : class { if (_arr != null) { - for (var c = 0; c < _arr.Length; c++) + for (var c = 0; c < _arrCount; c++) { if (_arr[c]?.TryGetTarget(out var target) == true && target == item) { From a81a2d908ab71bfca837902290affe9628e4fccb Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 21 Apr 2022 20:57:46 +0200 Subject: [PATCH 482/820] Fix double property reads. --- src/Avalonia.Controls/Border.cs | 2 +- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 06368eb5c6..bc740c133a 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -196,7 +196,7 @@ namespace Avalonia.Controls var borderThickness = BorderThickness; if (UseLayoutRounding) - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); + borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, _scale, _scale); _layoutThickness = borderThickness; } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index e70a3bc1c9..996cb29534 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -514,7 +514,7 @@ namespace Avalonia.Controls.Presenters var borderThickness = BorderThickness; if (UseLayoutRounding) - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); + borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, _scale, _scale); _layoutThickness = borderThickness; } From 2a5b2cf90f16418806170a18ed5692600784c7a5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 21 Apr 2022 22:24:32 -0400 Subject: [PATCH 483/820] Use newer GetOldValue method --- .../ColorSpectrum/ColorSpectrum.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index aecaa88f36..fe9a2fac43 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -361,7 +361,7 @@ namespace Avalonia.Controls.Primitives } /// - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { if (change.Property == ColorProperty) { @@ -380,7 +380,7 @@ namespace Avalonia.Controls.Primitives UpdateBitmapSources(); } - _oldColor = change.OldValue.GetValueOrDefault(); + _oldColor = change.GetOldValue(); } else if (change.Property == HsvColorProperty) { @@ -391,7 +391,7 @@ namespace Avalonia.Controls.Primitives SetColor(); } - _oldHsvColor = change.OldValue.GetValueOrDefault(); + _oldHsvColor = change.GetOldValue(); } else if (change.Property == MinHueProperty || change.Property == MaxHueProperty) From fd96e6483d1ec66cc3afefd5e5250f79c4f8e474 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 22 Apr 2022 21:59:08 +0200 Subject: [PATCH 484/820] Add scaled ComboBox to ControlCatalog. To test #7147. --- samples/ControlCatalog/Pages/ComboBoxPage.xaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/samples/ControlCatalog/Pages/ComboBoxPage.xaml b/samples/ControlCatalog/Pages/ComboBoxPage.xaml index 64e80a8e11..9f2fbb88f3 100644 --- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml @@ -86,6 +86,16 @@ + + + + + + Inline Items + Inline Item 2 + Inline Item 3 + Inline Item 4 + WrapSelection From d1956d18b4ee8f3112855e59f89cdb5d82960430 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 22 Apr 2022 21:59:57 +0200 Subject: [PATCH 485/820] Scale ComboBox popup according to RenderTransform. --- .../Primitives/IPopupHost.cs | 56 +++++-- .../Primitives/OverlayPopupHost.cs | 42 +++-- src/Avalonia.Controls/Primitives/Popup.cs | 150 ++++++++++++++++-- src/Avalonia.Controls/Primitives/PopupRoot.cs | 39 ++--- .../Controls/OverlayPopupHost.xaml | 18 ++- .../Controls/PopupRoot.xaml | 22 +-- .../Controls/ComboBox.xaml | 3 +- .../Controls/OverlayPopupHost.xaml | 16 +- .../Controls/PopupRoot.xaml | 14 +- .../Primitives/PopupTests.cs | 4 + 10 files changed, 257 insertions(+), 107 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 36d2ae9230..8652924f90 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -2,6 +2,7 @@ using System; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; +using Avalonia.Media; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -17,16 +18,50 @@ namespace Avalonia.Controls.Primitives public interface IPopupHost : IDisposable, IFocusScope { /// - /// Sets the control to display in the popup. + /// Gets or sets the fixed width of the popup. /// - /// - void SetChild(IControl? control); + double Width { get; set; } + + /// + /// Gets or sets the minimum width of the popup. + /// + double MinWidth { get; set; } + + /// + /// Gets or sets the maximum width of the popup. + /// + double MaxWidth { get; set; } + + /// + /// Gets or sets the fixed height of the popup. + /// + double Height { get; set; } + + /// + /// Gets or sets the minimum height of the popup. + /// + double MinHeight { get; set; } + + /// + /// Gets or sets the maximum height of the popup. + /// + double MaxHeight { get; set; } /// /// Gets the presenter from the control's template. /// IContentPresenter? Presenter { get; } + /// + /// Gets or sets whether the popup appears on top of all other windows. + /// + bool Topmost { get; set; } + + /// + /// Gets or sets a transform that will be applied to the popup. + /// + Transform? Transform { get; set; } + /// /// Gets the root of the visual tree in the case where the popup is presented using a /// separate visual tree. @@ -57,6 +92,12 @@ namespace Avalonia.Controls.Primitives PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All, Rect? rect = null); + /// + /// Sets the control to display in the popup. + /// + /// + void SetChild(IControl? control); + /// /// Shows the popup. /// @@ -66,14 +107,5 @@ namespace Avalonia.Controls.Primitives /// Hides the popup. /// void Hide(); - - /// - /// Binds the constraints of the popup host to a set of properties, usally those present on - /// . - /// - IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, - StyledProperty minWidthProperty, StyledProperty maxWidthProperty, - StyledProperty heightProperty, StyledProperty minHeightProperty, - StyledProperty maxHeightProperty, StyledProperty topmostProperty); } } diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 6ac544e0fe..4765718c3b 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -11,6 +11,12 @@ namespace Avalonia.Controls.Primitives { public class OverlayPopupHost : ContentControl, IPopupHost, IInteractive, IManagedPopupPositionerPopup { + /// + /// Defines the property. + /// + public static readonly StyledProperty TransformProperty = + PopupRoot.TransformProperty.AddOwner(); + private readonly OverlayLayer _overlayLayer; private PopupPositionerParameters _positionerParameters = new PopupPositionerParameters(); private ManagedPopupPositioner _positioner; @@ -29,10 +35,22 @@ namespace Avalonia.Controls.Primitives } public IVisual? HostedVisualTreeRoot => null; - + + public Transform? Transform + { + get => GetValue(TransformProperty); + set => SetValue(TransformProperty, value); + } + /// IInteractive? IInteractive.InteractiveParent => Parent; + bool IPopupHost.Topmost + { + get => false; + set { /* Not currently supported in overlay popups */ } + } + public void Dispose() => Hide(); @@ -48,28 +66,6 @@ namespace Avalonia.Controls.Primitives _shown = false; } - public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, StyledProperty minWidthProperty, - StyledProperty maxWidthProperty, StyledProperty heightProperty, StyledProperty minHeightProperty, - StyledProperty maxHeightProperty, StyledProperty topmostProperty) - { - // Topmost property is not supported - var bindings = new List(); - - void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to])); - Bind(WidthProperty, widthProperty); - Bind(MinWidthProperty, minWidthProperty); - Bind(MaxWidthProperty, maxWidthProperty); - Bind(HeightProperty, heightProperty); - Bind(MinHeightProperty, minHeightProperty); - Bind(MaxHeightProperty, maxHeightProperty); - - return Disposable.Create(() => - { - foreach (var x in bindings) - x.Dispose(); - }); - } - public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, PopupAnchor anchor = PopupAnchor.None, PopupGravity gravity = PopupGravity.None, PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All, diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index bb546107e0..7a7e41b029 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -14,6 +14,8 @@ using Avalonia.LogicalTree; using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.VisualTree; +using Avalonia.Media; +using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -33,6 +35,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty ChildProperty = AvaloniaProperty.Register(nameof(Child)); + /// + /// Defines the property. + /// + public static readonly StyledProperty InheritsTransformProperty = + AvaloniaProperty.Register(nameof(InheritsTransform)); + /// /// Defines the property. /// @@ -196,6 +204,16 @@ namespace Avalonia.Controls.Primitives set; } + /// + /// Gets or sets a value that determines whether the popup inherits the render transform + /// from its . Defaults to false. + /// + public bool InheritsTransform + { + get => GetValue(InheritsTransformProperty); + set => SetValue(InheritsTransformProperty, value); + } + /// /// Gets or sets a value that determines how the can be dismissed. /// @@ -395,24 +413,29 @@ namespace Avalonia.Controls.Primitives } _isOpenRequested = false; - var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); + var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); var handlerCleanup = new CompositeDisposable(7); - popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty, - HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty).DisposeWith(handlerCleanup); - + UpdateHostSizing(popupHost, topLevel, placementTarget); + popupHost.Topmost = Topmost; popupHost.SetChild(Child); ((ISetLogicalParent)popupHost).SetParent(this); - popupHost.ConfigurePosition( - placementTarget, - PlacementMode, - new Point(HorizontalOffset, VerticalOffset), - PlacementAnchor, - PlacementGravity, - PlacementConstraintAdjustment, - PlacementRect); + if (InheritsTransform && placementTarget is Control c) + { + SubscribeToEventHandler>( + c, + PlacementTargetPropertyChanged, + (x, handler) => x.PropertyChanged += handler, + (x, handler) => x.PropertyChanged -= handler).DisposeWith(handlerCleanup); + } + else + { + popupHost.Transform = null; + } + + UpdateHostPosition(popupHost, placementTarget); SubscribeToEventHandler>(popupHost, RootTemplateApplied, (x, handler) => x.TemplateApplied += handler, @@ -494,7 +517,7 @@ namespace Avalonia.Controls.Primitives } } - _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup); + _openState = new PopupOpenState(placementTarget, topLevel, popupHost, cleanupPopup); WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); @@ -542,7 +565,93 @@ namespace Avalonia.Controls.Primitives base.OnDetachedFromLogicalTree(e); Close(); } - + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (_openState is not null) + { + if (change.Property == WidthProperty || + change.Property == MinWidthProperty || + change.Property == MaxWidthProperty || + change.Property == HeightProperty || + change.Property == MinHeightProperty || + change.Property == MaxHeightProperty) + { + UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget); + } + else if (change.Property == PlacementTargetProperty || + change.Property == PlacementModeProperty || + change.Property == HorizontalOffsetProperty || + change.Property == VerticalOffsetProperty || + change.Property == PlacementAnchorProperty || + change.Property == PlacementConstraintAdjustmentProperty || + change.Property == PlacementRectProperty) + { + if (change.Property == PlacementTargetProperty) + { + var newTarget = change.GetNewValue() ?? this.FindLogicalAncestorOfType(); + + if (newTarget is null || newTarget.GetVisualRoot() != _openState.TopLevel) + { + Close(); + return; + } + + _openState.PlacementTarget = newTarget; + } + + UpdateHostPosition(_openState.PopupHost, _openState.PlacementTarget); + } + else if (change.Property == TopmostProperty) + { + _openState.PopupHost.Topmost = change.GetNewValue(); + } + } + } + + private void UpdateHostPosition(IPopupHost popupHost, IControl placementTarget) + { + popupHost.ConfigurePosition( + placementTarget, + PlacementMode, + new Point(HorizontalOffset, VerticalOffset), + PlacementAnchor, + PlacementGravity, + PlacementConstraintAdjustment, + PlacementRect ?? new Rect(default, placementTarget.Bounds.Size)); + } + + private void UpdateHostSizing(IPopupHost popupHost, TopLevel topLevel, IControl placementTarget) + { + var scaleX = 1.0; + var scaleY = 1.0; + + if (InheritsTransform && placementTarget.TransformToVisual(topLevel) is Matrix m) + { + scaleX = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12); + scaleY = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12); + + // Ideally we'd only assign a ScaleTransform here when the scale != 1, but there's + // an issue with LayoutTransformControl in that it sets its LayoutTransform property + // with LocalValue priority in ArrangeOverride in certain cases when LayoutTransform + // is null, which breaks TemplateBindings to this property. Offending commit/line: + // + // https://github.com/AvaloniaUI/Avalonia/commit/6fbe1c2180ef45a940e193f1b4637e64eaab80ed#diff-5344e793df13f462126a8153ef46c44194f244b6890f25501709bae51df97f82R54 + popupHost.Transform = new ScaleTransform(scaleX, scaleY); + } + else + { + popupHost.Transform = null; + } + + popupHost.Width = Width * scaleX; + popupHost.MinWidth = MinWidth * scaleX; + popupHost.MaxWidth = MaxWidth * scaleX; + popupHost.Height = Height * scaleY; + popupHost.MinHeight = MinHeight * scaleY; + popupHost.MaxHeight = MaxHeight * scaleY; + } + private void HandlePositionChange() { if (_openState != null) @@ -824,6 +933,14 @@ namespace Avalonia.Controls.Primitives } } + private void PlacementTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (_openState is not null && e.Property == Visual.TransformedBoundsProperty) + { + UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget); + } + } + private void WindowLostFocus() { if (IsLightDismissEnabled) @@ -862,15 +979,16 @@ namespace Avalonia.Controls.Primitives private readonly IDisposable _cleanup; private IDisposable? _presenterCleanup; - public PopupOpenState(TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup) + public PopupOpenState(IControl placementTarget, TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup) { + PlacementTarget = placementTarget; TopLevel = topLevel; PopupHost = popupHost; _cleanup = cleanup; } public TopLevel TopLevel { get; } - + public IControl PlacementTarget { get; set; } public IPopupHost PopupHost { get; } public void SetPresenterSubscription(IDisposable? presenterCleanup) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index abf56e5420..f7bf7c1a27 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Reactive.Disposables; using Avalonia.Automation.Peers; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Interactivity; @@ -8,7 +6,6 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.VisualTree; -using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { @@ -17,6 +14,12 @@ namespace Avalonia.Controls.Primitives /// public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost { + /// + /// Defines the property. + /// + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); + private PopupPositionerParameters _positionerParameters; /// @@ -54,6 +57,15 @@ namespace Avalonia.Controls.Primitives /// public new IPopupImpl? PlatformImpl => (IPopupImpl?)base.PlatformImpl; + /// + /// Gets or sets a transform that will be applied to the popup. + /// + public Transform? Transform + { + get => GetValue(TransformProperty); + set => SetValue(TransformProperty, value); + } + /// /// Gets the parent control in the event route. /// @@ -103,27 +115,6 @@ namespace Avalonia.Controls.Primitives IVisual IPopupHost.HostedVisualTreeRoot => this; - public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, StyledProperty minWidthProperty, - StyledProperty maxWidthProperty, StyledProperty heightProperty, StyledProperty minHeightProperty, - StyledProperty maxHeightProperty, StyledProperty topmostProperty) - { - var bindings = new List(); - - void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to])); - Bind(WidthProperty, widthProperty); - Bind(MinWidthProperty, minWidthProperty); - Bind(MaxWidthProperty, maxWidthProperty); - Bind(HeightProperty, heightProperty); - Bind(MinHeightProperty, minHeightProperty); - Bind(MaxHeightProperty, maxHeightProperty); - Bind(TopmostProperty, topmostProperty); - return Disposable.Create(() => - { - foreach (var x in bindings) - x.Dispose(); - }); - } - protected override Size MeasureOverride(Size availableSize) { var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; diff --git a/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml b/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml index 301e19d208..07d905ea1d 100644 --- a/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml +++ b/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml @@ -1,4 +1,4 @@ - diff --git a/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml index 9468cc5535..5e8f3337ee 100644 --- a/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml @@ -10,16 +10,18 @@ - - - - - - + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml index e35093d2f1..93ecc438eb 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml @@ -119,7 +119,8 @@ MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="{TemplateBinding MaxDropDownHeight}" PlacementTarget="Background" - IsLightDismissEnabled="True"> + IsLightDismissEnabled="True" + InheritsTransform="True"> - - - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index 1e573913b9..f608cf55f5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -10,16 +10,18 @@ - - - - + + + + - - + + + diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index cac8ca885d..d9cb40d1cc 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -325,6 +325,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal( new[] { + "LayoutTransformControl", "VisualLayerManager", "ContentPresenter", "ContentPresenter", @@ -337,6 +338,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal( new[] { + "LayoutTransformControl", "Panel", "Border", "VisualLayerManager", @@ -356,6 +358,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal( new object[] { + popupRoot, popupRoot, popupRoot, target, @@ -372,6 +375,7 @@ namespace Avalonia.Controls.UnitTests.Primitives popupRoot, popupRoot, popupRoot, + popupRoot, target, null, }, From 7e2c1ad165b28a3918ec22278e2716254bcc1e45 Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Fri, 22 Apr 2022 21:33:55 -0400 Subject: [PATCH 486/820] Implement TransformOrigin for brushes --- .../Animation/Animators/GradientBrushAnimator.cs | 6 ++++++ src/Avalonia.Base/Media/Brush.cs | 15 +++++++++++++++ src/Avalonia.Base/Media/IBrush.cs | 5 +++++ .../Immutable/ImmutableConicGradientBrush.cs | 4 +++- .../Media/Immutable/ImmutableGradientBrush.cs | 11 ++++++++++- .../Media/Immutable/ImmutableImageBrush.cs | 3 +++ .../Immutable/ImmutableLinearGradientBrush.cs | 4 +++- .../Immutable/ImmutableRadialGradientBrush.cs | 4 +++- .../Media/Immutable/ImmutableSolidColorBrush.cs | 5 +++++ .../Media/Immutable/ImmutableTileBrush.cs | 9 +++++++++ .../Media/Immutable/ImmutableVisualBrush.cs | 3 +++ 11 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs index 5e97635c9a..4727ea1bfb 100644 --- a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs @@ -31,6 +31,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -41,6 +42,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -50,6 +52,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -102,18 +105,21 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, oldRadial.Transform is { } ? new ImmutableTransform(oldRadial.Transform.Value) : null, + oldRadial.TransformOrigin, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, oldConic.Transform is { } ? new ImmutableTransform(oldConic.Transform.Value) : null, + oldConic.TransformOrigin, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, oldLinear.Transform is { } ? new ImmutableTransform(oldLinear.Transform.Value) : null, + oldLinear.TransformOrigin, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs index 9d989979a7..8d531e9394 100644 --- a/src/Avalonia.Base/Media/Brush.cs +++ b/src/Avalonia.Base/Media/Brush.cs @@ -24,6 +24,12 @@ namespace Avalonia.Media public static readonly StyledProperty TransformProperty = AvaloniaProperty.Register(nameof(Transform)); + /// + /// Defines the property + /// + public static readonly StyledProperty TransformOriginProperty = + AvaloniaProperty.Register(nameof(TransformOrigin)); + /// public event EventHandler? Invalidated; @@ -51,6 +57,15 @@ namespace Avalonia.Media set { SetValue(TransformProperty, value); } } + /// + /// Gets or sets the origin of the brush + /// + public RelativePoint TransformOrigin + { + get => GetValue(TransformOriginProperty); + set => SetValue(TransformOriginProperty, value); + } + /// /// Parses a brush string. /// diff --git a/src/Avalonia.Base/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs index 830c066182..14ccf12b8f 100644 --- a/src/Avalonia.Base/Media/IBrush.cs +++ b/src/Avalonia.Base/Media/IBrush.cs @@ -17,5 +17,10 @@ namespace Avalonia.Media /// Gets the transform of the brush. /// ITransform? Transform { get; } + + /// + /// Gets the origin of the brushes + /// + RelativePoint TransformOrigin { get; } } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs index 4b97615c4c..70232f0a63 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The center point for the gradient. /// The starting angle for the gradient. @@ -20,10 +21,11 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, double angle = 0) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { Center = center ?? RelativePoint.Center; Angle = angle; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs index f1e51687d0..1e95acbf22 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs @@ -13,16 +13,19 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. protected ImmutableGradientBrush( IReadOnlyList gradientStops, double opacity, ImmutableTransform? transform, + RelativePoint? transformOrigin, GradientSpreadMethod spreadMethod) { GradientStops = gradientStops; Opacity = opacity; Transform = transform; + TransformOrigin = transformOrigin.HasValue ? transformOrigin.Value : RelativePoint.TopLeft; SpreadMethod = spreadMethod; } @@ -31,7 +34,8 @@ 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.TransformOrigin, source.SpreadMethod) { } @@ -47,6 +51,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + /// public GradientSpreadMethod SpreadMethod { get; } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index c36e82eacb..f9892bf60c 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -16,6 +16,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin 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. @@ -29,6 +30,7 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint transformOrigin = new RelativePoint(), RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -39,6 +41,7 @@ namespace Avalonia.Media.Immutable destinationRect ?? RelativeRect.Fill, opacity, transform, + transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, tileMode, diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs index 64c0f9b44e..3c26b5c009 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The start point for the gradient. /// The end point for the gradient. @@ -20,10 +21,11 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? startPoint = null, RelativePoint? endPoint = null) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { StartPoint = startPoint ?? RelativePoint.TopLeft; EndPoint = endPoint ?? RelativePoint.BottomRight; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs index 3da4bdd8e9..e08d2810f8 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The start point for the gradient. /// @@ -25,11 +26,12 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, RelativePoint? gradientOrigin = null, double radius = 0.5) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { Center = center ?? RelativePoint.Center; GradientOrigin = gradientOrigin ?? RelativePoint.Center; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs index 9b1b2500ef..6755dfd236 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs @@ -53,6 +53,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + public bool Equals(ImmutableSolidColorBrush? other) { if (ReferenceEquals(null, other)) return false; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 2c1844a2c2..6df27872fc 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs @@ -15,6 +15,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin 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. @@ -27,6 +28,7 @@ namespace Avalonia.Media.Immutable RelativeRect destinationRect, double opacity, ImmutableTransform? transform, + RelativePoint transformOrigin, RelativeRect sourceRect, Stretch stretch, TileMode tileMode, @@ -37,6 +39,7 @@ namespace Avalonia.Media.Immutable DestinationRect = destinationRect; Opacity = opacity; Transform = transform; + TransformOrigin = transformOrigin; SourceRect = sourceRect; Stretch = stretch; TileMode = tileMode; @@ -54,6 +57,7 @@ namespace Avalonia.Media.Immutable source.DestinationRect, source.Opacity, source.Transform?.ToImmutable(), + source.TransformOrigin, source.SourceRect, source.Stretch, source.TileMode, @@ -78,6 +82,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + /// public RelativeRect SourceRect { get; } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 8ecef63237..b436dcdb5e 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -17,6 +17,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin 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. @@ -30,6 +31,7 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint transformOrigin = new RelativePoint(), RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -40,6 +42,7 @@ namespace Avalonia.Media.Immutable destinationRect ?? RelativeRect.Fill, opacity, transform, + transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, tileMode, From 488108aab24c59547e4cac443e28bd41d2f7f0bc Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Fri, 22 Apr 2022 21:34:27 -0400 Subject: [PATCH 487/820] Support for TransformOrigin in Skia DC --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 39 ++++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 2548b9f5aa..7e0360d691 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -180,7 +180,8 @@ namespace Avalonia.Skia var size = geometry.Bounds.Size; using (var fill = brush != null ? CreatePaint(_fillPaint, brush, size) : default(PaintWrapper)) - using (var stroke = pen?.Brush != null ? CreatePaint(_strokePaint, pen, size) : default(PaintWrapper)) + using (var stroke = pen?.Brush != null ? CreatePaint(_strokePaint, pen, + size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0))) : default(PaintWrapper)) { if (fill.Paint != null) { @@ -397,7 +398,7 @@ namespace Avalonia.Skia if (pen?.Brush != null) { - using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size)) + using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0)))) { if (paint.Paint is object) { @@ -432,7 +433,7 @@ namespace Avalonia.Skia if (pen?.Brush != null) { - using (var paint = CreatePaint(_strokePaint, pen, rect.Size)) + using (var paint = CreatePaint(_strokePaint, pen, rect.Size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0)))) { if (paint.Paint is object) { @@ -624,8 +625,12 @@ namespace Avalonia.Skia } else { + var transformOrigin = linearGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * linearGradient.Transform.Value * (offset); + using (var shader = - SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, linearGradient.Transform.Value.ToSKMatrix())) + SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, transform.ToSKMatrix())) { paintWrapper.Paint.Shader = shader; } @@ -653,8 +658,12 @@ namespace Avalonia.Skia } else { + var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * radialGradient.Transform.Value * (offset); + using (var shader = - SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode, radialGradient.Transform.Value.ToSKMatrix())) + SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode, transform.ToSKMatrix())) { paintWrapper.Paint.Shader = shader; } @@ -693,9 +702,14 @@ namespace Avalonia.Skia } else { + + var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * radialGradient.Transform.Value * (offset); + using (var shader = SKShader.CreateCompose( SKShader.CreateColor(reversedColors[0]), - SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode, radialGradient.Transform.Value.ToSKMatrix()) + SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode, transform.ToSKMatrix()) )) { paintWrapper.Paint.Shader = shader; @@ -716,7 +730,12 @@ namespace Avalonia.Skia if (conicGradient.Transform is { }) { - rotation = rotation.PreConcat(conicGradient.Transform.Value.ToSKMatrix()); + + var transformOrigin = conicGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * conicGradient.Transform.Value * (offset); + + rotation = rotation.PreConcat(transform.ToSKMatrix()); } using (var shader = @@ -793,7 +812,11 @@ namespace Avalonia.Skia if (tileBrush.Transform is { }) { - paintTransform = paintTransform.PreConcat(tileBrush.Transform.Value.ToSKMatrix()); + var origin = tileBrush.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(origin); + var transform = (-offset) * tileBrush.Transform.Value * (offset); + + paintTransform = paintTransform.PreConcat(transform.ToSKMatrix()); } using (var shader = image.ToShader(tileX, tileY, paintTransform)) From 6b2cedfcd1b763691373ba0b6ffb5f0cb6c9f445 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 23 Apr 2022 10:15:46 +0200 Subject: [PATCH 488/820] Fix failing test. It was probably not a good idea to test the contents of the template explicitly in unit tests, but here we are... --- .../Primitives/PopupRootTests.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index f27ff3928c..6d3351d2b2 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -78,9 +78,13 @@ namespace Avalonia.Controls.UnitTests.Primitives var templatedChild = ((Visual)target.Host).GetVisualChildren().Single(); - Assert.IsType(templatedChild); + Assert.IsType(templatedChild); - var visualLayerManager = templatedChild.GetVisualChildren().Skip(1).Single(); + var panel = templatedChild.GetVisualChildren().Single(); + + Assert.IsType(panel); + + var visualLayerManager = panel.GetVisualChildren().Skip(1).Single(); Assert.IsType(visualLayerManager); From 0cdbd53bc312d2711461552c88a5250c1c1d3c1a Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 25 Apr 2022 15:21:26 +0200 Subject: [PATCH 489/820] Rewrite TextBounds test --- .../Media/TextFormatting/TextLineTests.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index b58d9051f3..e3b9e5a8b1 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -665,14 +665,17 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var text = "0123".AsMemory(); var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); + var shapedBuffer = TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption); + var firstRun = new ShapedTextCharacters(shapedBuffer, defaultProperties); + var textRuns = new List { - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption), defaultProperties), new CustomDrawableRun(), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length + 1, text.Length), shaperOption), defaultProperties), + firstRun, new CustomDrawableRun(), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2 + 2, text.Length), shaperOption), defaultProperties), + new ShapedTextCharacters(shapedBuffer, defaultProperties), new CustomDrawableRun(), + new ShapedTextCharacters(shapedBuffer, defaultProperties) }; var textSource = new FixedRunsTextSource(textRuns); @@ -687,6 +690,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, textBounds.Count); Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + + textBounds = textLine.GetTextBounds(0, firstRun.Text.Length); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(0, firstRun.Text.Length + 1); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width + 14, textBounds[0].Rectangle.Width); } } From 7c63e1a60b9b20ee5bec85c26a21a15f801c1600 Mon Sep 17 00:00:00 2001 From: Tako Date: Mon, 25 Apr 2022 17:42:52 +0300 Subject: [PATCH 490/820] Fix ContextMenu freeze. --- src/Avalonia.Controls/ItemsControl.cs | 2 +- .../Platform/DefaultMenuInteractionHandler.cs | 2 +- .../Primitives/SelectingItemsControlTests.cs | 45 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 256160a116..56b0014c05 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -518,7 +518,7 @@ namespace Avalonia.Controls } c = result; - } while (c != null && c != from); + } while (c != null && c != from && direction != NavigationDirection.First && direction != NavigationDirection.Last); return null; } diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 6e9ac537f1..2f9bf0ac06 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -417,7 +417,7 @@ namespace Avalonia.Controls.Platform protected internal virtual void MenuOpened(object? sender, RoutedEventArgs e) { - if (e.Source == Menu) + if (e.Source is Menu) { Menu?.MoveSelection(NavigationDirection.First, true); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 0e0ca7cd25..3d36395c3a 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -6,6 +6,7 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Reactive.Disposables; +using System.Threading.Tasks; using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; @@ -1614,6 +1615,50 @@ namespace Avalonia.Controls.UnitTests.Primitives target.MoveSelection(NavigationDirection.Next, true); } + [Fact(Timeout = 2000)] + public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_First_Item() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] + { + new ListBoxItem { Focusable = false }, + new ListBoxItem(), + } + }; + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + // Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below. + // https://github.com/xunit/xunit/issues/2222 + await Task.Run(() => target.MoveSelection(NavigationDirection.First, true)); + Assert.Equal(-1, target.SelectedIndex); + } + + [Fact(Timeout = 2000)] + public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_Last_Item() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] + { + new ListBoxItem(), + new ListBoxItem { Focusable = false }, + } + }; + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + // Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below. + // https://github.com/xunit/xunit/issues/2222 + await Task.Run(() => target.MoveSelection(NavigationDirection.Last, true)); + Assert.Equal(-1, target.SelectedIndex); + } + [Fact] public void MoveSelection_Does_Select_Disabled_Controls() { From d75732699cb4ad2ed53ae441fe83ebbe0a27a758 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 26 Apr 2022 16:59:58 +0200 Subject: [PATCH 491/820] Added attributes describing API stability. Adds two attributes which describe the stability of an API: - `UnstableAttribute` indicates that we provide no API stability guarantees for the class between minor/patch versions. This is mostly applied to `*Impl` interfaces which describe the interface between core Avalonia and platform implementations - `NotClientImplementableAttribute` indicates that an interface is stable for consumption by a client, but should not be implemented outside of Avalonia itself (either because custom implementations are not supported, or because as members may be added to its API) --- build/ApiCompatAttributeExcludeList.txt | 2 ++ src/Avalonia.Base/Animation/IAnimation.cs | 2 ++ src/Avalonia.Base/Animation/IAnimationSetter.cs | 3 +++ src/Avalonia.Base/Animation/IAnimator.cs | 2 ++ src/Avalonia.Base/Animation/IClock.cs | 4 ++-- src/Avalonia.Base/Animation/IGlobalClock.cs | 5 ++--- src/Avalonia.Base/Animation/ITransition.cs | 2 ++ src/Avalonia.Base/Controls/INameScope.cs | 4 ++-- src/Avalonia.Base/Controls/IPseudoClasses.cs | 2 ++ src/Avalonia.Base/Controls/IResourceHost.cs | 4 ++-- src/Avalonia.Base/Controls/IResourceNode.cs | 4 ++-- .../Controls/ISetInheritanceParent.cs | 3 ++- src/Avalonia.Base/Controls/ISetLogicalParent.cs | 4 ++-- src/Avalonia.Base/Data/Core/IPropertyInfo.cs | 2 ++ src/Avalonia.Base/Data/IBinding.cs | 3 +++ src/Avalonia.Base/IAvaloniaObject.cs | 2 ++ src/Avalonia.Base/IDataContextProvider.cs | 3 ++- src/Avalonia.Base/IDirectPropertyAccessor.cs | 1 + src/Avalonia.Base/IDirectPropertyMetadata.cs | 3 +++ src/Avalonia.Base/IStyledElement.cs | 4 ++-- src/Avalonia.Base/IStyledPropertyMetadata.cs | 3 +++ src/Avalonia.Base/Input/IAccessKeyHandler.cs | 3 +++ src/Avalonia.Base/Input/IFocusManager.cs | 3 +++ src/Avalonia.Base/Input/IInputDevice.cs | 2 ++ src/Avalonia.Base/Input/IInputElement.cs | 2 ++ src/Avalonia.Base/Input/IInputManager.cs | 2 ++ src/Avalonia.Base/Input/IInputRoot.cs | 3 +++ src/Avalonia.Base/Input/IKeyboardDevice.cs | 2 ++ .../Input/IKeyboardNavigationHandler.cs | 3 +++ src/Avalonia.Base/Input/IMainMenu.cs | 2 ++ src/Avalonia.Base/Input/IMouseDevice.cs | 2 ++ src/Avalonia.Base/Input/IPointer.cs | 3 +++ src/Avalonia.Base/Input/IPointerDevice.cs | 2 ++ src/Avalonia.Base/Input/Platform/IClipboard.cs | 2 ++ .../Input/Platform/IPlatformDragSource.cs | 2 ++ src/Avalonia.Base/Input/Raw/IDragDropDevice.cs | 5 ++++- .../Input/TextInput/ITextInputMethodImpl.cs | 4 ++++ src/Avalonia.Base/Interactivity/IInteractive.cs | 2 ++ src/Avalonia.Base/Layout/ILayoutManager.cs | 4 ++-- src/Avalonia.Base/Layout/ILayoutRoot.cs | 3 +++ src/Avalonia.Base/Layout/ILayoutable.cs | 4 ++-- src/Avalonia.Base/LogicalTree/ILogical.cs | 2 ++ src/Avalonia.Base/LogicalTree/ILogicalRoot.cs | 5 ++++- src/Avalonia.Base/Media/IBrush.cs | 2 ++ src/Avalonia.Base/Media/IConicGradientBrush.cs | 5 ++++- src/Avalonia.Base/Media/IDashStyle.cs | 2 ++ .../Media/IExperimentalAcrylicMaterial.cs | 5 ++++- src/Avalonia.Base/Media/IGradientBrush.cs | 2 ++ src/Avalonia.Base/Media/IGradientStop.cs | 5 ++++- src/Avalonia.Base/Media/IImageBrush.cs | 4 +++- src/Avalonia.Base/Media/ILinearGradientBrush.cs | 7 +++++-- src/Avalonia.Base/Media/IMutableBrush.cs | 2 ++ .../IMutableExperimentalAcrylicMaterial.cs | 5 ++++- src/Avalonia.Base/Media/IMutableTransform.cs | 1 + src/Avalonia.Base/Media/IPen.cs | 5 ++++- src/Avalonia.Base/Media/IRadialGradientBrush.cs | 7 +++++-- src/Avalonia.Base/Media/ISolidColorBrush.cs | 5 ++++- src/Avalonia.Base/Media/ITileBrush.cs | 4 +++- src/Avalonia.Base/Media/IVisualBrush.cs | 6 ++++-- src/Avalonia.Base/Media/Imaging/IBitmap.cs | 2 ++ .../Media/TextFormatting/ITextSource.cs | 5 ++++- .../Metadata/NotClientImplementableAttribute.cs | 17 +++++++++++++++++ src/Avalonia.Base/Metadata/UnstableAttribute.cs | 12 ++++++++++++ src/Avalonia.Base/Platform/IAssetLoader.cs | 2 ++ src/Avalonia.Base/Platform/IBitmapImpl.cs | 2 ++ src/Avalonia.Base/Platform/ICursorImpl.cs | 2 ++ .../Platform/IDrawingContextImpl.cs | 2 ++ src/Avalonia.Base/Platform/IFontManagerImpl.cs | 2 ++ src/Avalonia.Base/Platform/IGeometryImpl.cs | 2 ++ src/Avalonia.Base/Platform/IGlyphRunImpl.cs | 2 ++ .../Platform/IGlyphTypefaceImpl.cs | 2 ++ .../Platform/IMacOSTopLevelPlatformHandle.cs | 2 ++ .../Platform/IPlatformRenderInterface.cs | 2 ++ src/Avalonia.Base/Platform/IPlatformSettings.cs | 2 ++ .../Platform/IPlatformThreadingInterface.cs | 2 ++ .../Platform/IRenderTargetBitmapImpl.cs | 2 ++ src/Avalonia.Base/Platform/IRuntimePlatform.cs | 5 +++++ .../Platform/IStreamGeometryContextImpl.cs | 3 +++ .../Platform/IStreamGeometryImpl.cs | 3 +++ src/Avalonia.Base/Platform/ITextShaperImpl.cs | 2 ++ .../Platform/ITransformedGeometryImpl.cs | 5 ++++- .../Platform/IWriteableBitmapImpl.cs | 5 ++++- .../Platform/Interop/IDynamicLibraryLoader.cs | 2 ++ .../Rendering/IDeferredRendererLock.cs | 2 ++ src/Avalonia.Base/Rendering/IRenderLoop.cs | 5 ++++- src/Avalonia.Base/Rendering/IRenderRoot.cs | 2 ++ src/Avalonia.Base/Rendering/IRenderTimer.cs | 2 ++ .../Rendering/IVisualBrushInitialize.cs | 2 ++ .../Rendering/IVisualBrushRenderer.cs | 2 ++ .../Styling/Activators/IStyleActivator.cs | 6 +++--- .../Styling/Activators/IStyleActivatorSink.cs | 3 ++- src/Avalonia.Base/Styling/IGlobalStyles.cs | 4 ++-- src/Avalonia.Base/Styling/ISetter.cs | 4 ++-- src/Avalonia.Base/Styling/ISetterInstance.cs | 6 +++--- src/Avalonia.Base/Styling/IStyle.cs | 5 ++--- src/Avalonia.Base/Styling/IStyleHost.cs | 2 ++ src/Avalonia.Base/Styling/IStyleInstance.cs | 4 ++-- src/Avalonia.Base/Styling/IStyleable.cs | 2 ++ src/Avalonia.Base/Styling/ITemplatedControl.cs | 2 ++ src/Avalonia.Base/VisualTree/IVisual.cs | 4 ++-- .../IApplicationLifetime.cs | 3 +++ .../IClassicDesktopStyleApplicationLifetime.cs | 2 ++ .../IControlledApplicationLifetime.cs | 2 ++ .../ISingleViewApplicationLifetime.cs | 3 +++ .../Diagnostics/IPopupHostProvider.cs | 2 ++ .../Offscreen/OffscreenTopLevelImpl.cs | 2 ++ src/Avalonia.Controls/IContentControl.cs | 2 ++ src/Avalonia.Controls/IControl.cs | 2 ++ src/Avalonia.Controls/IGlobalDataTemplates.cs | 2 ++ src/Avalonia.Controls/IMenu.cs | 2 ++ src/Avalonia.Controls/IMenuElement.cs | 2 ++ src/Avalonia.Controls/IMenuItem.cs | 5 ++++- .../INativeMenuExporterEventsImplBridge.cs | 3 +++ .../INativeMenuItemExporterEventsImplBridge.cs | 3 +++ src/Avalonia.Controls/IPanel.cs | 5 ++++- src/Avalonia.Controls/IScrollable.cs | 1 - .../IManagedNotificationManager.cs | 5 ++++- .../Notifications/INotification.cs | 2 ++ .../Notifications/INotificationManager.cs | 5 ++++- .../Platform/DefaultMenuInteractionHandler.cs | 2 ++ .../Platform/IApplicationPlatformEvents.cs | 3 +++ .../Platform/IMenuInteractionHandler.cs | 5 ++++- .../Platform/IMountedVolumeInfoProvider.cs | 4 ++-- .../Platform/INativeControlHostImpl.cs | 6 +++++- .../Platform/IPlatformIconLoader.cs | 2 ++ .../Platform/IPlatformLifetimeEventsImpl.cs | 3 ++- .../Platform/IPlatformNativeSurfaceHandle.cs | 2 ++ src/Avalonia.Controls/Platform/IPopupImpl.cs | 2 ++ src/Avalonia.Controls/Platform/IScreenImpl.cs | 4 ++-- .../Platform/ISystemDialogImpl.cs | 2 ++ src/Avalonia.Controls/Platform/ITopLevelImpl.cs | 2 ++ .../ITopLevelImplWithTextInputMethod.cs | 2 ++ .../Platform/ITopLevelNativeMenuExporter.cs | 7 ++++++- src/Avalonia.Controls/Platform/ITrayIconImpl.cs | 2 ++ .../Platform/IWindowBaseImpl.cs | 2 ++ .../Platform/IWindowIconImpl.cs | 2 ++ src/Avalonia.Controls/Platform/IWindowImpl.cs | 2 ++ .../Platform/IWindowingPlatform.cs | 3 +++ .../InternalPlatformThreadingInterface.cs | 5 ++--- .../Platform/MountedDriveInfo.cs | 2 ++ .../Platform/PlatformManager.cs | 2 ++ .../Surfaces/IFramebufferPlatformSurface.cs | 4 +++- .../Presenters/IContentPresenter.cs | 2 ++ .../Presenters/IContentPresenterHost.cs | 2 ++ .../Presenters/IItemsPresenter.cs | 2 ++ .../Presenters/IItemsPresenterHost.cs | 2 ++ src/Avalonia.Controls/Presenters/IPresenter.cs | 2 ++ src/Avalonia.Controls/Primitives/IPopupHost.cs | 2 ++ .../PopupPositioning/IPopupPositioner.cs | 4 ++++ src/Avalonia.Controls/Remote/RemoteServer.cs | 3 ++- .../Remote/Server/RemoteServerTopLevelImpl.cs | 2 ++ .../Templates/IDataTemplateHost.cs | 4 +++- .../Imaging/IOpenGlBitmapImpl.cs | 4 +++- src/Skia/Avalonia.Skia/GlyphRunImpl.cs | 2 ++ src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs | 2 ++ .../Avalonia.Skia/ISkiaDrawingContextImpl.cs | 2 ++ .../Avalonia.Direct2D1/Media/BrushImpl.cs | 2 ++ .../Media/DrawingContextImpl.cs | 2 ++ .../Avalonia.Direct2D1/Media/GeometryImpl.cs | 2 ++ .../Media/GlyphTypefaceImpl.cs | 2 ++ .../Avalonia.Direct2D1/Media/ImageBrushImpl.cs | 2 ++ .../Media/Imaging/BitmapImpl.cs | 2 ++ .../Media/Imaging/D2DBitmapImpl.cs | 2 ++ .../Media/Imaging/D2DRenderTargetBitmapImpl.cs | 2 ++ .../Media/Imaging/WicBitmapImpl.cs | 2 ++ .../Media/Imaging/WicRenderTargetBitmapImpl.cs | 2 ++ .../Media/LinearGradientBrushImpl.cs | 2 ++ .../Media/RadialGradientBrushImpl.cs | 2 ++ .../Media/SolidColorBrushImpl.cs | 2 ++ .../Media/StreamGeometryContextImpl.cs | 2 ++ .../Media/StreamGeometryImpl.cs | 2 ++ .../Media/TransformedGeometryImpl.cs | 2 ++ src/Windows/Avalonia.Win32/ScreenImpl.cs | 2 ++ src/Windows/Avalonia.Win32/TrayIconImpl.cs | 2 ++ src/Windows/Avalonia.Win32/WindowImpl.cs | 2 ++ 175 files changed, 452 insertions(+), 79 deletions(-) create mode 100644 build/ApiCompatAttributeExcludeList.txt create mode 100644 src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs create mode 100644 src/Avalonia.Base/Metadata/UnstableAttribute.cs diff --git a/build/ApiCompatAttributeExcludeList.txt b/build/ApiCompatAttributeExcludeList.txt new file mode 100644 index 0000000000..1df5a30ec3 --- /dev/null +++ b/build/ApiCompatAttributeExcludeList.txt @@ -0,0 +1,2 @@ +T:Avalonia.Metadata.NotClientImplementableAttribute +T:Avalonia.Metadata.UnstableAttribute diff --git a/src/Avalonia.Base/Animation/IAnimation.cs b/src/Avalonia.Base/Animation/IAnimation.cs index 436a765d27..eda86ed106 100644 --- a/src/Avalonia.Base/Animation/IAnimation.cs +++ b/src/Avalonia.Base/Animation/IAnimation.cs @@ -1,12 +1,14 @@ using System; using System.Threading; using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Animation objects /// + [NotClientImplementable] public interface IAnimation { /// diff --git a/src/Avalonia.Base/Animation/IAnimationSetter.cs b/src/Avalonia.Base/Animation/IAnimationSetter.cs index 6a1d3539e2..8c4ba95517 100644 --- a/src/Avalonia.Base/Animation/IAnimationSetter.cs +++ b/src/Avalonia.Base/Animation/IAnimationSetter.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Animation { + [NotClientImplementable] public interface IAnimationSetter { AvaloniaProperty? Property { get; set; } diff --git a/src/Avalonia.Base/Animation/IAnimator.cs b/src/Avalonia.Base/Animation/IAnimator.cs index f64ac9f913..06ba8dc329 100644 --- a/src/Avalonia.Base/Animation/IAnimator.cs +++ b/src/Avalonia.Base/Animation/IAnimator.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Animator objects /// + [NotClientImplementable] public interface IAnimator : IList { /// diff --git a/src/Avalonia.Base/Animation/IClock.cs b/src/Avalonia.Base/Animation/IClock.cs index ae44102077..7b3b7b3924 100644 --- a/src/Avalonia.Base/Animation/IClock.cs +++ b/src/Avalonia.Base/Animation/IClock.cs @@ -1,9 +1,9 @@ using System; -using System.Collections.Generic; -using System.Text; +using Avalonia.Metadata; namespace Avalonia.Animation { + [NotClientImplementable] public interface IClock : IObservable { PlayState PlayState { get; set; } diff --git a/src/Avalonia.Base/Animation/IGlobalClock.cs b/src/Avalonia.Base/Animation/IGlobalClock.cs index b0455e2c80..138dc07539 100644 --- a/src/Avalonia.Base/Animation/IGlobalClock.cs +++ b/src/Avalonia.Base/Animation/IGlobalClock.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Avalonia.Metadata; namespace Avalonia.Animation { + [NotClientImplementable] public interface IGlobalClock : IClock { } diff --git a/src/Avalonia.Base/Animation/ITransition.cs b/src/Avalonia.Base/Animation/ITransition.cs index 241ca208d1..639b92ce35 100644 --- a/src/Avalonia.Base/Animation/ITransition.cs +++ b/src/Avalonia.Base/Animation/ITransition.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Transition objects. /// + [NotClientImplementable] public interface ITransition { /// diff --git a/src/Avalonia.Base/Controls/INameScope.cs b/src/Avalonia.Base/Controls/INameScope.cs index 1ca7db2f37..a12e88c055 100644 --- a/src/Avalonia.Base/Controls/INameScope.cs +++ b/src/Avalonia.Base/Controls/INameScope.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Controls @@ -7,6 +6,7 @@ namespace Avalonia.Controls /// /// Defines a name scope. /// + [NotClientImplementable] public interface INameScope { /// diff --git a/src/Avalonia.Base/Controls/IPseudoClasses.cs b/src/Avalonia.Base/Controls/IPseudoClasses.cs index 27290716d0..eda521727f 100644 --- a/src/Avalonia.Base/Controls/IPseudoClasses.cs +++ b/src/Avalonia.Base/Controls/IPseudoClasses.cs @@ -1,9 +1,11 @@ +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Exposes an interface for setting pseudoclasses on a collection. /// + [NotClientImplementable] public interface IPseudoClasses { /// diff --git a/src/Avalonia.Base/Controls/IResourceHost.cs b/src/Avalonia.Base/Controls/IResourceHost.cs index ea34a8b39a..286f0e36ef 100644 --- a/src/Avalonia.Base/Controls/IResourceHost.cs +++ b/src/Avalonia.Base/Controls/IResourceHost.cs @@ -1,6 +1,5 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +9,7 @@ namespace Avalonia.Controls /// /// This interface is implemented by and `Application`. /// + [NotClientImplementable] public interface IResourceHost : IResourceNode { /// diff --git a/src/Avalonia.Base/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs index 73bfeaf161..d6c900f97f 100644 --- a/src/Avalonia.Base/Controls/IResourceNode.cs +++ b/src/Avalonia.Base/Controls/IResourceNode.cs @@ -1,6 +1,5 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -12,6 +11,7 @@ namespace Avalonia.Controls /// () and resource providers such as /// (see ). /// + [NotClientImplementable] public interface IResourceNode { /// diff --git a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs index dbf8c68892..e85e025005 100644 --- a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs +++ b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs @@ -1,4 +1,4 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +10,7 @@ namespace Avalonia.Controls /// Additionally, also sets the inheritance parent; this /// interface is only needed where the logical and inheritance parents differ. /// + [NotClientImplementable] public interface ISetInheritanceParent { /// diff --git a/src/Avalonia.Base/Controls/ISetLogicalParent.cs b/src/Avalonia.Base/Controls/ISetLogicalParent.cs index 85bda05961..7f1b4b5d87 100644 --- a/src/Avalonia.Base/Controls/ISetLogicalParent.cs +++ b/src/Avalonia.Base/Controls/ISetLogicalParent.cs @@ -1,6 +1,5 @@ using Avalonia.LogicalTree; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +9,7 @@ namespace Avalonia.Controls /// /// You should not usually need to use this interface - it is for advanced scenarios only. /// + [NotClientImplementable] public interface ISetLogicalParent { /// diff --git a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs index 4d80feb4ba..0cb8a937cc 100644 --- a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs +++ b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Data.Core { + [NotClientImplementable] public interface IPropertyInfo { string Name { get; } diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs index 9535cf608e..7d44bf09b5 100644 --- a/src/Avalonia.Base/Data/IBinding.cs +++ b/src/Avalonia.Base/Data/IBinding.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Data { /// /// Holds a binding that can be applied to a property on an object. /// + [NotClientImplementable] public interface IBinding { /// diff --git a/src/Avalonia.Base/IAvaloniaObject.cs b/src/Avalonia.Base/IAvaloniaObject.cs index 00f5062f9e..3b0016903b 100644 --- a/src/Avalonia.Base/IAvaloniaObject.cs +++ b/src/Avalonia.Base/IAvaloniaObject.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Data; +using Avalonia.Metadata; namespace Avalonia { /// /// Interface for getting/setting values on an object. /// + [NotClientImplementable] public interface IAvaloniaObject { /// diff --git a/src/Avalonia.Base/IDataContextProvider.cs b/src/Avalonia.Base/IDataContextProvider.cs index 1172adcaa4..1d381cf4a5 100644 --- a/src/Avalonia.Base/IDataContextProvider.cs +++ b/src/Avalonia.Base/IDataContextProvider.cs @@ -1,10 +1,11 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia { /// /// Defines an element with a data context that can be used for binding. /// + [NotClientImplementable] public interface IDataContextProvider : IAvaloniaObject { /// diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs index e34483fa7b..476a36f372 100644 --- a/src/Avalonia.Base/IDirectPropertyAccessor.cs +++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia { diff --git a/src/Avalonia.Base/IDirectPropertyMetadata.cs b/src/Avalonia.Base/IDirectPropertyMetadata.cs index 4fd4a3a6b7..7d74470a13 100644 --- a/src/Avalonia.Base/IDirectPropertyMetadata.cs +++ b/src/Avalonia.Base/IDirectPropertyMetadata.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia { /// /// Untyped interface to /// + [NotClientImplementable] public interface IDirectPropertyMetadata { /// diff --git a/src/Avalonia.Base/IStyledElement.cs b/src/Avalonia.Base/IStyledElement.cs index a068d4a5bf..4eed54de45 100644 --- a/src/Avalonia.Base/IStyledElement.cs +++ b/src/Avalonia.Base/IStyledElement.cs @@ -2,12 +2,12 @@ using System.ComponentModel; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Styling; -#nullable enable - namespace Avalonia { + [NotClientImplementable] public interface IStyledElement : IStyleable, IStyleHost, diff --git a/src/Avalonia.Base/IStyledPropertyMetadata.cs b/src/Avalonia.Base/IStyledPropertyMetadata.cs index 6b29b5f977..89990338e0 100644 --- a/src/Avalonia.Base/IStyledPropertyMetadata.cs +++ b/src/Avalonia.Base/IStyledPropertyMetadata.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia { /// /// Untyped interface to /// + [NotClientImplementable] public interface IStyledPropertyMetadata { /// diff --git a/src/Avalonia.Base/Input/IAccessKeyHandler.cs b/src/Avalonia.Base/Input/IAccessKeyHandler.cs index e484d003c7..93a50968e2 100644 --- a/src/Avalonia.Base/Input/IAccessKeyHandler.cs +++ b/src/Avalonia.Base/Input/IAccessKeyHandler.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for classes that handle access keys for a window. /// + [Unstable] public interface IAccessKeyHandler { /// diff --git a/src/Avalonia.Base/Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs index 2510479a8e..0c85cad2f7 100644 --- a/src/Avalonia.Base/Input/IFocusManager.cs +++ b/src/Avalonia.Base/Input/IFocusManager.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Manages focus for the application. /// + [NotClientImplementable] public interface IFocusManager { /// diff --git a/src/Avalonia.Base/Input/IInputDevice.cs b/src/Avalonia.Base/Input/IInputDevice.cs index ab0fae65df..fe4f584f18 100644 --- a/src/Avalonia.Base/Input/IInputDevice.cs +++ b/src/Avalonia.Base/Input/IInputDevice.cs @@ -1,7 +1,9 @@ using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { + [NotClientImplementable] public interface IInputDevice { /// diff --git a/src/Avalonia.Base/Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs index d1552a3a2a..78001143d7 100644 --- a/src/Avalonia.Base/Input/IInputElement.cs +++ b/src/Avalonia.Base/Input/IInputElement.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Interactivity; +using Avalonia.Metadata; using Avalonia.VisualTree; #nullable enable @@ -10,6 +11,7 @@ namespace Avalonia.Input /// /// Defines input-related functionality for a control. /// + [NotClientImplementable] public interface IInputElement : IInteractive, IVisual { /// diff --git a/src/Avalonia.Base/Input/IInputManager.cs b/src/Avalonia.Base/Input/IInputManager.cs index 80b71d3e47..658226a519 100644 --- a/src/Avalonia.Base/Input/IInputManager.cs +++ b/src/Avalonia.Base/Input/IInputManager.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { @@ -7,6 +8,7 @@ namespace Avalonia.Input /// Receives input from the windowing subsystem and dispatches it to interested parties /// for processing. /// + [NotClientImplementable] public interface IInputManager { /// diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index 98e8699573..7edc69df52 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for top-level input elements. /// + [NotClientImplementable] public interface IInputRoot : IInputElement { /// diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs index d0e84e5ad0..c8db8bf16f 100644 --- a/src/Avalonia.Base/Input/IKeyboardDevice.cs +++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Input { @@ -50,6 +51,7 @@ namespace Avalonia.Input KeyboardMask = Alt | Control | Shift | Meta } + [NotClientImplementable] public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement? FocusedElement { get; } diff --git a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs index 88d00b3b50..3bcd6d6206 100644 --- a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs +++ b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for classes that handle keyboard navigation for a window. /// + [Unstable] public interface IKeyboardNavigationHandler { /// diff --git a/src/Avalonia.Base/Input/IMainMenu.cs b/src/Avalonia.Base/Input/IMainMenu.cs index 67b58c0ffc..213a979c28 100644 --- a/src/Avalonia.Base/Input/IMainMenu.cs +++ b/src/Avalonia.Base/Input/IMainMenu.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Interactivity; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Input @@ -7,6 +8,7 @@ namespace Avalonia.Input /// /// Defines the interface for a window's main menu. /// + [NotClientImplementable] public interface IMainMenu : IVisual { /// diff --git a/src/Avalonia.Base/Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs index 6b7f0e76e5..2d66397d63 100644 --- a/src/Avalonia.Base/Input/IMouseDevice.cs +++ b/src/Avalonia.Base/Input/IMouseDevice.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Input { /// /// Represents a mouse device. /// + [NotClientImplementable] public interface IMouseDevice : IPointerDevice { /// diff --git a/src/Avalonia.Base/Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs index 7af48cef82..66aeacadc9 100644 --- a/src/Avalonia.Base/Input/IPointer.cs +++ b/src/Avalonia.Base/Input/IPointer.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { + [NotClientImplementable] public interface IPointer { int Id { get; } diff --git a/src/Avalonia.Base/Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs index 0096bb77bf..0993835feb 100644 --- a/src/Avalonia.Base/Input/IPointerDevice.cs +++ b/src/Avalonia.Base/Input/IPointerDevice.cs @@ -1,9 +1,11 @@ using System; using Avalonia.VisualTree; using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { + [NotClientImplementable] public interface IPointerDevice : IInputDevice { /// diff --git a/src/Avalonia.Base/Input/Platform/IClipboard.cs b/src/Avalonia.Base/Input/Platform/IClipboard.cs index eb880904eb..bf2a5a8602 100644 --- a/src/Avalonia.Base/Input/Platform/IClipboard.cs +++ b/src/Avalonia.Base/Input/Platform/IClipboard.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Input.Platform { + [NotClientImplementable] public interface IClipboard { Task GetTextAsync(); diff --git a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs index 30d8ee5337..8d1b93087e 100644 --- a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs +++ b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Input.Platform { + [Unstable] public interface IPlatformDragSource { Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects); diff --git a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs index f7b7914bd1..3bcc9fadd3 100644 --- a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs +++ b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs @@ -1,5 +1,8 @@ -namespace Avalonia.Input.Raw +using Avalonia.Metadata; + +namespace Avalonia.Input.Raw { + [NotClientImplementable] public interface IDragDropDevice : IInputDevice { } diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs index 4404c903b7..be7ad81f81 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Input.TextInput { + [Unstable] public interface ITextInputMethodImpl { void SetClient(ITextInputMethodClient? client); @@ -8,6 +11,7 @@ namespace Avalonia.Input.TextInput void Reset(); } + [NotClientImplementable] public interface ITextInputMethodRoot : IInputRoot { ITextInputMethodImpl? InputMethod { get; } diff --git a/src/Avalonia.Base/Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs index 6d7dcd64f4..980bf54f1f 100644 --- a/src/Avalonia.Base/Interactivity/IInteractive.cs +++ b/src/Avalonia.Base/Interactivity/IInteractive.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; #nullable enable @@ -7,6 +8,7 @@ namespace Avalonia.Interactivity /// /// Interface for objects that raise routed events. /// + [NotClientImplementable] public interface IInteractive { /// diff --git a/src/Avalonia.Base/Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs index 614670a53b..143ce13a1b 100644 --- a/src/Avalonia.Base/Layout/ILayoutManager.cs +++ b/src/Avalonia.Base/Layout/ILayoutManager.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Layout { /// /// Manages measuring and arranging of controls. /// + [NotClientImplementable] public interface ILayoutManager : IDisposable { /// diff --git a/src/Avalonia.Base/Layout/ILayoutRoot.cs b/src/Avalonia.Base/Layout/ILayoutRoot.cs index e2f16b338a..df15fc1a1d 100644 --- a/src/Avalonia.Base/Layout/ILayoutRoot.cs +++ b/src/Avalonia.Base/Layout/ILayoutRoot.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Layout { /// /// Defines the root of a layoutable tree. /// + [NotClientImplementable] public interface ILayoutRoot : ILayoutable { /// diff --git a/src/Avalonia.Base/Layout/ILayoutable.cs b/src/Avalonia.Base/Layout/ILayoutable.cs index 54d3ba6a11..d8b546b04a 100644 --- a/src/Avalonia.Base/Layout/ILayoutable.cs +++ b/src/Avalonia.Base/Layout/ILayoutable.cs @@ -1,12 +1,12 @@ +using Avalonia.Metadata; using Avalonia.VisualTree; -#nullable enable - namespace Avalonia.Layout { /// /// Defines layout-related functionality for a control. /// + [NotClientImplementable] public interface ILayoutable : IVisual { /// diff --git a/src/Avalonia.Base/LogicalTree/ILogical.cs b/src/Avalonia.Base/LogicalTree/ILogical.cs index caff3d8150..9cc2edff86 100644 --- a/src/Avalonia.Base/LogicalTree/ILogical.cs +++ b/src/Avalonia.Base/LogicalTree/ILogical.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Metadata; namespace Avalonia.LogicalTree { /// /// Represents a node in the logical tree. /// + [NotClientImplementable] public interface ILogical { /// diff --git a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs index 4a61544a6f..ea0f554e96 100644 --- a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs +++ b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs @@ -1,8 +1,11 @@ -namespace Avalonia.LogicalTree +using Avalonia.Metadata; + +namespace Avalonia.LogicalTree { /// /// Represents a root of a logical tree. /// + [NotClientImplementable] public interface ILogicalRoot : ILogical { } diff --git a/src/Avalonia.Base/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs index 830c066182..10700492d1 100644 --- a/src/Avalonia.Base/Media/IBrush.cs +++ b/src/Avalonia.Base/Media/IBrush.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Media { @@ -6,6 +7,7 @@ namespace Avalonia.Media /// Describes how an area is painted. /// [TypeConverter(typeof(BrushConverter))] + [NotClientImplementable] public interface IBrush { /// diff --git a/src/Avalonia.Base/Media/IConicGradientBrush.cs b/src/Avalonia.Base/Media/IConicGradientBrush.cs index 5368dd1851..6a397b86d4 100644 --- a/src/Avalonia.Base/Media/IConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/IConicGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Paints an area with a conic gradient. /// + [NotClientImplementable] public interface IConicGradientBrush : IGradientBrush { /// diff --git a/src/Avalonia.Base/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs index 7835c7a1e9..7208216603 100644 --- a/src/Avalonia.Base/Media/IDashStyle.cs +++ b/src/Avalonia.Base/Media/IDashStyle.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Represents the sequence of dashes and gaps that will be applied by a . /// + [NotClientImplementable] public interface IDashStyle { /// diff --git a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs index e71584258a..38048fb255 100644 --- a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Experimental Interface for producing Acrylic-like materials. /// + [NotClientImplementable] public interface IExperimentalAcrylicMaterial { /// diff --git a/src/Avalonia.Base/Media/IGradientBrush.cs b/src/Avalonia.Base/Media/IGradientBrush.cs index 18db0af660..9b78a4af78 100644 --- a/src/Avalonia.Base/Media/IGradientBrush.cs +++ b/src/Avalonia.Base/Media/IGradientBrush.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// A brush that draws with a gradient. /// + [NotClientImplementable] public interface IGradientBrush : IBrush { /// diff --git a/src/Avalonia.Base/Media/IGradientStop.cs b/src/Avalonia.Base/Media/IGradientStop.cs index 22eb9df60d..64b952f3bc 100644 --- a/src/Avalonia.Base/Media/IGradientStop.cs +++ b/src/Avalonia.Base/Media/IGradientStop.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Describes the location and color of a transition point in a gradient. /// + [NotClientImplementable] public interface IGradientStop { /// diff --git a/src/Avalonia.Base/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs index aaa481bd28..732f1957d0 100644 --- a/src/Avalonia.Base/Media/IImageBrush.cs +++ b/src/Avalonia.Base/Media/IImageBrush.cs @@ -1,10 +1,12 @@ using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Paints an area with an . /// + [NotClientImplementable] public interface IImageBrush : ITileBrush { /// @@ -12,4 +14,4 @@ namespace Avalonia.Media /// IBitmap Source { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ILinearGradientBrush.cs b/src/Avalonia.Base/Media/ILinearGradientBrush.cs index 3e2a5a0e22..4f4a55db36 100644 --- a/src/Avalonia.Base/Media/ILinearGradientBrush.cs +++ b/src/Avalonia.Base/Media/ILinearGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// A brush that draws with a linear gradient. /// + [NotClientImplementable] public interface ILinearGradientBrush : IGradientBrush { /// @@ -15,4 +18,4 @@ /// RelativePoint EndPoint { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs index 415db61d68..fef124ba36 100644 --- a/src/Avalonia.Base/Media/IMutableBrush.cs +++ b/src/Avalonia.Base/Media/IMutableBrush.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Represents a mutable brush which can return an immutable clone of itself. /// + [NotClientImplementable] public interface IMutableBrush : IBrush, IAffectsRender { /// diff --git a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs index fcfe4631a6..f954a8c52a 100644 --- a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Represents a mutable brush which can return an immutable clone of itself. /// + [NotClientImplementable] public interface IMutableExperimentalAcrylicMaterial : IExperimentalAcrylicMaterial, IAffectsRender { /// diff --git a/src/Avalonia.Base/Media/IMutableTransform.cs b/src/Avalonia.Base/Media/IMutableTransform.cs index 2033c434c0..22526eed17 100644 --- a/src/Avalonia.Base/Media/IMutableTransform.cs +++ b/src/Avalonia.Base/Media/IMutableTransform.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Media { diff --git a/src/Avalonia.Base/Media/IPen.cs b/src/Avalonia.Base/Media/IPen.cs index 1cad9948b4..eca1cb507a 100644 --- a/src/Avalonia.Base/Media/IPen.cs +++ b/src/Avalonia.Base/Media/IPen.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Describes how a stroke is drawn. /// + [NotClientImplementable] public interface IPen { /// diff --git a/src/Avalonia.Base/Media/IRadialGradientBrush.cs b/src/Avalonia.Base/Media/IRadialGradientBrush.cs index cadf53cc18..0f025e98bc 100644 --- a/src/Avalonia.Base/Media/IRadialGradientBrush.cs +++ b/src/Avalonia.Base/Media/IRadialGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Paints an area with a radial gradient. /// + [NotClientImplementable] public interface IRadialGradientBrush : IGradientBrush { /// @@ -21,4 +24,4 @@ /// double Radius { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs index d0ab00599b..29e11210f1 100644 --- a/src/Avalonia.Base/Media/ISolidColorBrush.cs +++ b/src/Avalonia.Base/Media/ISolidColorBrush.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Media { /// /// Fills an area with a solid color. /// + [NotClientImplementable] public interface ISolidColorBrush : IBrush { /// @@ -10,4 +13,4 @@ namespace Avalonia.Media /// Color Color { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs index 991857eec9..cb5a591003 100644 --- a/src/Avalonia.Base/Media/ITileBrush.cs +++ b/src/Avalonia.Base/Media/ITileBrush.cs @@ -1,10 +1,12 @@ using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Media -{ +{ /// /// A brush which displays a repeating image. /// + [NotClientImplementable] public interface ITileBrush : IBrush { /// diff --git a/src/Avalonia.Base/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs index e74892b218..b8900bbd73 100644 --- a/src/Avalonia.Base/Media/IVisualBrush.cs +++ b/src/Avalonia.Base/Media/IVisualBrush.cs @@ -1,10 +1,12 @@ -using Avalonia.VisualTree; +using Avalonia.Metadata; +using Avalonia.VisualTree; namespace Avalonia.Media { /// /// Paints an area with an . /// + [NotClientImplementable] public interface IVisualBrush : ITileBrush { /// @@ -12,4 +14,4 @@ namespace Avalonia.Media /// IVisual Visual { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/Imaging/IBitmap.cs b/src/Avalonia.Base/Media/Imaging/IBitmap.cs index 134bebc002..bd04d5ce86 100644 --- a/src/Avalonia.Base/Media/Imaging/IBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/IBitmap.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Utilities; @@ -8,6 +9,7 @@ namespace Avalonia.Media.Imaging /// /// Represents a bitmap image. /// + [NotClientImplementable] public interface IBitmap : IImage, IDisposable { /// diff --git a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs index 32012ab8e9..85641977e6 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media.TextFormatting +using Avalonia.Metadata; + +namespace Avalonia.Media.TextFormatting { /// /// Produces objects that are used by the . /// + [NotClientImplementable] public interface ITextSource { /// diff --git a/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs new file mode 100644 index 0000000000..348c983c03 --- /dev/null +++ b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Avalonia.Metadata +{ + /// + /// This interface is not intended to be implemented outside of the core Avalonia framework as + /// its API may change without warning. + /// + /// + /// This interface is stable for consumption by a client, but should not be implemented as members + /// may be added to its API. + /// + [AttributeUsage(AttributeTargets.Interface)] + public class NotClientImplementableAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Metadata/UnstableAttribute.cs b/src/Avalonia.Base/Metadata/UnstableAttribute.cs new file mode 100644 index 0000000000..3b6fa5168a --- /dev/null +++ b/src/Avalonia.Base/Metadata/UnstableAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Avalonia.Metadata +{ + /// + /// This API is unstable and is not covered by API compatibility guarantees between minor and + /// patch releases. + /// + public class UnstableAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs index e3899784ad..b65d61803f 100644 --- a/src/Avalonia.Base/Platform/IAssetLoader.cs +++ b/src/Avalonia.Base/Platform/IAssetLoader.cs @@ -2,12 +2,14 @@ using System; using System.Collections.Generic; using System.IO; using System.Reflection; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Loads assets compiled into the application binary. /// + [Unstable] public interface IAssetLoader { /// diff --git a/src/Avalonia.Base/Platform/IBitmapImpl.cs b/src/Avalonia.Base/Platform/IBitmapImpl.cs index 1e68bc477d..8f11f68e7c 100644 --- a/src/Avalonia.Base/Platform/IBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IBitmapImpl.cs @@ -1,11 +1,13 @@ using System; using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IBitmapImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/ICursorImpl.cs b/src/Avalonia.Base/Platform/ICursorImpl.cs index 14235869f7..74e0ba2e5c 100644 --- a/src/Avalonia.Base/Platform/ICursorImpl.cs +++ b/src/Avalonia.Base/Platform/ICursorImpl.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input; +using Avalonia.Metadata; #nullable enable @@ -8,6 +9,7 @@ namespace Avalonia.Platform /// /// Represents a platform implementation of a . /// + [Unstable] public interface ICursorImpl : IDisposable { } diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 4e6612e908..d84a509234 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -3,12 +3,14 @@ using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the interface through which drawing occurs. /// + [Unstable] public interface IDrawingContextImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs index 0110287afd..932249bd52 100644 --- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs +++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IFontManagerImpl { /// diff --git a/src/Avalonia.Base/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs index ed6de1b5c7..c80f8923ef 100644 --- a/src/Avalonia.Base/Platform/IGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/IGeometryImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs index 08786d9689..7801bdd50f 100644 --- a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs @@ -1,9 +1,11 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Actual implementation of a glyph run that stores platform dependent resources. /// + [Unstable] public interface IGlyphRunImpl : IDisposable { } } diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs index 6afd79d29c..415f34fb29 100644 --- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IGlyphTypefaceImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs index e399976bbe..b087724079 100644 --- a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs +++ b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IMacOSTopLevelPlatformHandle { IntPtr NSView { get; } diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index c46efd46c3..0eeefddf0b 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.IO; using Avalonia.Media; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the main platform-specific interface for the rendering subsystem. /// + [Unstable] public interface IPlatformRenderInterface { /// diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs index e4b28e6575..78d1817312 100644 --- a/src/Avalonia.Base/Platform/IPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformSettings { Size DoubleClickSize { get; } diff --git a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs index 2137f965cc..bf18a7da5b 100644 --- a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using Avalonia.Metadata; using Avalonia.Threading; namespace Avalonia.Platform @@ -7,6 +8,7 @@ namespace Avalonia.Platform /// /// Provides platform-specific services relating to threading. /// + [Unstable] public interface IPlatformThreadingInterface { void RunLoop(CancellationToken cancellationToken); diff --git a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs index 9add07afe3..d33c503650 100644 --- a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; namespace Avalonia.Platform { @@ -5,6 +6,7 @@ namespace Avalonia.Platform /// Defines the platform-specific interface for a /// . /// + [Unstable] public interface IRenderTargetBitmapImpl : IBitmapImpl, IRenderTarget { } diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 850757a1ee..8ab04f5995 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IRuntimePlatform { IDisposable StartSystemTimer(TimeSpan interval, Action tick); @@ -9,6 +11,7 @@ namespace Avalonia.Platform IUnmanagedBlob AllocBlob(int size); } + [Unstable] public interface IUnmanagedBlob : IDisposable { IntPtr Address { get; } @@ -17,6 +20,7 @@ namespace Avalonia.Platform } + [Unstable] public struct RuntimePlatformInfo { public OperatingSystemType OperatingSystem { get; set; } @@ -29,6 +33,7 @@ namespace Avalonia.Platform public bool IsUnix { get; set; } } + [Unstable] public enum OperatingSystemType { Unknown, diff --git a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs index 4587979308..3d5ee36608 100644 --- a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs +++ b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { /// /// Describes a geometry using drawing commands. /// + [Unstable] public interface IStreamGeometryContextImpl : IGeometryContext { } diff --git a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs index 5b070fde02..bd4411f3a4 100644 --- a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IStreamGeometryImpl : IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/ITextShaperImpl.cs b/src/Avalonia.Base/Platform/ITextShaperImpl.cs index 11be9e3f09..10e58b7d0b 100644 --- a/src/Avalonia.Base/Platform/ITextShaperImpl.cs +++ b/src/Avalonia.Base/Platform/ITextShaperImpl.cs @@ -1,4 +1,5 @@ using Avalonia.Media.TextFormatting; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Platform @@ -6,6 +7,7 @@ namespace Avalonia.Platform /// /// An abstraction that is used produce shaped text. /// + [Unstable] public interface ITextShaperImpl { /// diff --git a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs index 1ed025b571..2754414cd1 100644 --- a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Platform +using Avalonia.Metadata; + +namespace Avalonia.Platform { /// /// Represents a geometry with a transform applied. @@ -7,6 +9,7 @@ /// An transforms a geometry without transforming its /// stroke thickness. /// + [Unstable] public interface ITransformedGeometryImpl : IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs index c4e2e4915f..fa1e1862b7 100644 --- a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Platform +using Avalonia.Metadata; + +namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IWriteableBitmapImpl : IBitmapImpl { ILockedFramebuffer Lock(); diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs index 8124ce6bc4..9389ebc703 100644 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform.Interop { + [Unstable] public interface IDynamicLibraryLoader { IntPtr LoadLibrary(string dll); diff --git a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs index eab3dca58e..1c6bd69158 100644 --- a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs +++ b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Rendering { + [Unstable] public interface IDeferredRendererLock { IDisposable? TryLock(); diff --git a/src/Avalonia.Base/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs index dd7442e7f8..9838967261 100644 --- a/src/Avalonia.Base/Rendering/IRenderLoop.cs +++ b/src/Avalonia.Base/Rendering/IRenderLoop.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Rendering +using Avalonia.Metadata; + +namespace Avalonia.Rendering { /// /// The application render loop. @@ -7,6 +9,7 @@ /// The render loop is responsible for advancing the animation timer and updating the scene /// graph for visible windows. /// + [NotClientImplementable] public interface IRenderLoop { /// diff --git a/src/Avalonia.Base/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs index 54e58bf39c..1aa44158b2 100644 --- a/src/Avalonia.Base/Rendering/IRenderRoot.cs +++ b/src/Avalonia.Base/Rendering/IRenderRoot.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.VisualTree; @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// /// Represents the root of a renderable tree. /// + [NotClientImplementable] public interface IRenderRoot : IVisual { /// diff --git a/src/Avalonia.Base/Rendering/IRenderTimer.cs b/src/Avalonia.Base/Rendering/IRenderTimer.cs index d333e928a0..ee74c345be 100644 --- a/src/Avalonia.Base/Rendering/IRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/IRenderTimer.cs @@ -1,11 +1,13 @@ using System; using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Rendering { /// /// Defines the interface implemented by an application render timer. /// + [NotClientImplementable] public interface IRenderTimer { /// diff --git a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs index 00449c5344..b5ab8ed0bd 100644 --- a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs +++ b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Rendering { @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// Internal interface for initializing controls that are to be used as the visual in a /// . /// + [Unstable] public interface IVisualBrushInitialize { /// diff --git a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs index 1cd6515635..f5312ad39b 100644 --- a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs +++ b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Rendering @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// /// Defines a renderer used to render a visual brush to a bitmap. /// + [Unstable] public interface IVisualBrushRenderer { /// diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs index 479100ed8a..ac7b8b3ef1 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs @@ -1,6 +1,5 @@ -#nullable enable - -using System; +using System; +using Avalonia.Metadata; namespace Avalonia.Styling.Activators { @@ -16,6 +15,7 @@ namespace Avalonia.Styling.Activators /// - The subscription can have a tag associated with it, allowing a subscriber to index /// into a list of subscriptions without having to allocate additional objects. /// + [Unstable] public interface IStyleActivator : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs index a1a6ef5c28..fbb18dc304 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs @@ -1,10 +1,11 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling.Activators { /// /// Receives notifications from an . /// + [Unstable] public interface IStyleActivatorSink { /// diff --git a/src/Avalonia.Base/Styling/IGlobalStyles.cs b/src/Avalonia.Base/Styling/IGlobalStyles.cs index ab24e3138c..f9667a8895 100644 --- a/src/Avalonia.Base/Styling/IGlobalStyles.cs +++ b/src/Avalonia.Base/Styling/IGlobalStyles.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines the style host that provides styles global to the application. /// + [NotClientImplementable] public interface IGlobalStyles : IStyleHost { /// diff --git a/src/Avalonia.Base/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs index d588817be8..71ae5d84c0 100644 --- a/src/Avalonia.Base/Styling/ISetter.cs +++ b/src/Avalonia.Base/Styling/ISetter.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a setter for a . /// + [NotClientImplementable] public interface ISetter { /// diff --git a/src/Avalonia.Base/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs index a299a87b64..e0d3137619 100644 --- a/src/Avalonia.Base/Styling/ISetterInstance.cs +++ b/src/Avalonia.Base/Styling/ISetterInstance.cs @@ -1,12 +1,12 @@ -#nullable enable - -using System; +using System; +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a setter that has been instanced on a control. /// + [Unstable] public interface ISetterInstance : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs index 78fbe0f2b5..fe6e8319ad 100644 --- a/src/Avalonia.Base/Styling/IStyle.cs +++ b/src/Avalonia.Base/Styling/IStyle.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; -using Avalonia.Controls; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines the interface for styles. /// + [NotClientImplementable] public interface IStyle { /// diff --git a/src/Avalonia.Base/Styling/IStyleHost.cs b/src/Avalonia.Base/Styling/IStyleHost.cs index 360b40d9a1..2ae488825c 100644 --- a/src/Avalonia.Base/Styling/IStyleHost.cs +++ b/src/Avalonia.Base/Styling/IStyleHost.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; +using Avalonia.Metadata; #nullable enable @@ -8,6 +9,7 @@ namespace Avalonia.Styling /// /// Defines an element that has a collection. /// + [NotClientImplementable] public interface IStyleHost { /// diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 8ddb989bc0..5b5b99097c 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a style that has been instanced on a control. /// + [Unstable] public interface IStyleInstance : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs index a3df779057..5bc972e7ab 100644 --- a/src/Avalonia.Base/Styling/IStyleable.cs +++ b/src/Avalonia.Base/Styling/IStyleable.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Collections; +using Avalonia.Metadata; #nullable enable @@ -9,6 +10,7 @@ namespace Avalonia.Styling /// /// Interface for styleable elements. /// + [NotClientImplementable] public interface IStyleable : IAvaloniaObject, INamed { /// diff --git a/src/Avalonia.Base/Styling/ITemplatedControl.cs b/src/Avalonia.Base/Styling/ITemplatedControl.cs index 5485babb62..68989c8a5e 100644 --- a/src/Avalonia.Base/Styling/ITemplatedControl.cs +++ b/src/Avalonia.Base/Styling/ITemplatedControl.cs @@ -1,6 +1,8 @@ +using Avalonia.Metadata; namespace Avalonia.Styling { + [NotClientImplementable] public interface ITemplatedControl : IAvaloniaObject { } diff --git a/src/Avalonia.Base/VisualTree/IVisual.cs b/src/Avalonia.Base/VisualTree/IVisual.cs index b1251618c4..3b053fab38 100644 --- a/src/Avalonia.Base/VisualTree/IVisual.cs +++ b/src/Avalonia.Base/VisualTree/IVisual.cs @@ -1,10 +1,9 @@ using System; using Avalonia.Collections; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering; -#nullable enable - namespace Avalonia.VisualTree { /// @@ -18,6 +17,7 @@ namespace Avalonia.VisualTree /// implemented by . It should not be necessary to implement it /// anywhere else. /// + [NotClientImplementable] public interface IVisual { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs index 9860d0cb38..b38a539d4a 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface IApplicationLifetime { diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index 2bd5c1238d..4b88f6b537 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes { /// /// Controls application lifetime in classic desktop style /// + [NotClientImplementable] public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs index 3f61aeb536..d7eda790df 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface IControlledApplicationLifetime : IApplicationLifetime { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs index e25815602e..480c65e5ad 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface ISingleViewApplicationLifetime : IApplicationLifetime { Control? MainView { get; set; } diff --git a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs index 45cd1d727e..64978248e5 100644 --- a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs +++ b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Diagnostics { /// /// Diagnostics interface to retrieve an associated . /// + [NotClientImplementable] public interface IPopupHostProvider { /// diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index 08d559a5c1..1c7bdb9b37 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using Avalonia.Input; using Avalonia.Input.Raw; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; namespace Avalonia.Controls.Embedding.Offscreen { + [Unstable] public abstract class OffscreenTopLevelImplBase : ITopLevelImpl { private double _scaling = 1; diff --git a/src/Avalonia.Controls/IContentControl.cs b/src/Avalonia.Controls/IContentControl.cs index d28b0afb25..b4d8d0f574 100644 --- a/src/Avalonia.Controls/IContentControl.cs +++ b/src/Avalonia.Controls/IContentControl.cs @@ -1,5 +1,6 @@ using Avalonia.Controls.Templates; using Avalonia.Layout; +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -7,6 +8,7 @@ namespace Avalonia.Controls /// Defines a control that displays according to a /// . /// + [NotClientImplementable] public interface IContentControl : IControl { /// diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs index b501bc15a7..3395fc1059 100644 --- a/src/Avalonia.Controls/IControl.cs +++ b/src/Avalonia.Controls/IControl.cs @@ -1,6 +1,7 @@ using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -8,6 +9,7 @@ namespace Avalonia.Controls /// /// Interface for Avalonia controls. /// + [NotClientImplementable] public interface IControl : IVisual, IDataTemplateHost, ILayoutable, diff --git a/src/Avalonia.Controls/IGlobalDataTemplates.cs b/src/Avalonia.Controls/IGlobalDataTemplates.cs index 92dcd2c189..f4499ddb5e 100644 --- a/src/Avalonia.Controls/IGlobalDataTemplates.cs +++ b/src/Avalonia.Controls/IGlobalDataTemplates.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Templates; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Defines the application-global data templates. /// + [NotClientImplementable] public interface IGlobalDataTemplates : IDataTemplateHost { } diff --git a/src/Avalonia.Controls/IMenu.cs b/src/Avalonia.Controls/IMenu.cs index 0722a22f08..d90c5ea7a8 100644 --- a/src/Avalonia.Controls/IMenu.cs +++ b/src/Avalonia.Controls/IMenu.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Platform; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Represents a or . /// + [NotClientImplementable] public interface IMenu : IMenuElement { /// diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs index a3200d2b1b..c13c20b639 100644 --- a/src/Avalonia.Controls/IMenuElement.cs +++ b/src/Avalonia.Controls/IMenuElement.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using Avalonia.Input; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Represents an or . /// + [NotClientImplementable] public interface IMenuElement : IControl { /// diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs index 35e36eb0f4..9d7ef3c18d 100644 --- a/src/Avalonia.Controls/IMenuItem.cs +++ b/src/Avalonia.Controls/IMenuItem.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Controls +using Avalonia.Metadata; + +namespace Avalonia.Controls { /// /// Represents a . /// + [NotClientImplementable] public interface IMenuItem : IMenuElement { /// diff --git a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs index f492e6ca0f..29963e4821 100644 --- a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { + [Unstable] public interface INativeMenuExporterEventsImplBridge { void RaiseNeedsUpdate (); diff --git a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs index 6cb68d8ddd..a6c7489971 100644 --- a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { + [Unstable] public interface INativeMenuItemExporterEventsImplBridge { void RaiseClicked (); diff --git a/src/Avalonia.Controls/IPanel.cs b/src/Avalonia.Controls/IPanel.cs index 7b9e2c2074..8f2564ec74 100644 --- a/src/Avalonia.Controls/IPanel.cs +++ b/src/Avalonia.Controls/IPanel.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { /// /// Interface for controls that can contain multiple children. /// + [NotClientImplementable] public interface IPanel : IControl { /// @@ -10,4 +13,4 @@ namespace Avalonia.Controls /// Controls Children { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/IScrollable.cs b/src/Avalonia.Controls/IScrollable.cs index 2a98b3910a..680088290c 100644 --- a/src/Avalonia.Controls/IScrollable.cs +++ b/src/Avalonia.Controls/IScrollable.cs @@ -1,4 +1,3 @@ - namespace Avalonia.Controls.Primitives { /// diff --git a/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs b/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs index 977544674d..b2e6e9e80b 100644 --- a/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Controls.Notifications +using Avalonia.Metadata; + +namespace Avalonia.Controls.Notifications { /// /// Represents a notification manager that can show arbitrary content. @@ -9,6 +11,7 @@ /// can display arbitrary content, as opposed to notification managers which display notifications /// using the host operating system's notification mechanism. /// + [NotClientImplementable] public interface IManagedNotificationManager : INotificationManager { /// diff --git a/src/Avalonia.Controls/Notifications/INotification.cs b/src/Avalonia.Controls/Notifications/INotification.cs index fa08233097..9ccce5b2c4 100644 --- a/src/Avalonia.Controls/Notifications/INotification.cs +++ b/src/Avalonia.Controls/Notifications/INotification.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.Notifications { /// /// Represents a notification that can be shown in a window or by the host operating system. /// + [NotClientImplementable] public interface INotification { /// diff --git a/src/Avalonia.Controls/Notifications/INotificationManager.cs b/src/Avalonia.Controls/Notifications/INotificationManager.cs index 72fb8e6c08..5fa479f2c3 100644 --- a/src/Avalonia.Controls/Notifications/INotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/INotificationManager.cs @@ -1,9 +1,12 @@ -namespace Avalonia.Controls.Notifications +using Avalonia.Metadata; + +namespace Avalonia.Controls.Notifications { /// /// Represents a notification manager that can be used to show notifications in a window or using /// the host operating system. /// + [NotClientImplementable] public interface INotificationManager { /// diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 6e9ac537f1..abef29bbd9 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; @@ -14,6 +15,7 @@ namespace Avalonia.Controls.Platform /// /// Provides the default keyboard and pointer interaction for menus. /// + [Unstable] public class DefaultMenuInteractionHandler : IMenuInteractionHandler { private readonly bool _isContextMenu; diff --git a/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs b/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs index a8d3a3b3ac..99bbb8b56d 100644 --- a/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs +++ b/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { + [Unstable] public interface IApplicationPlatformEvents { void RaiseUrlsOpened(string[] urls); diff --git a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs index dd8503f768..47b5a048b0 100644 --- a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Controls.Platform +using Avalonia.Metadata; + +namespace Avalonia.Controls.Platform { /// /// Handles user interaction for menus. /// + [Unstable] public interface IMenuInteractionHandler { /// diff --git a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs index 6e10163175..daeb9076e6 100644 --- a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs +++ b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs @@ -1,13 +1,13 @@ using System; using System.Collections.ObjectModel; -using System.Threading.Tasks; -using Avalonia.Platform; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Defines a platform-specific mount volumes info provider implementation. /// + [Unstable] public interface IMountedVolumeInfoProvider { /// diff --git a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs index df13613848..ffa79aa8d6 100644 --- a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs +++ b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs @@ -1,10 +1,11 @@ using System; using System.Diagnostics.CodeAnalysis; +using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.VisualTree; namespace Avalonia.Controls.Platform { + [Unstable] public interface INativeControlHostImpl { INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent); @@ -13,11 +14,13 @@ namespace Avalonia.Controls.Platform bool IsCompatibleWith(IPlatformHandle handle); } + [Unstable] public interface INativeControlHostDestroyableControlHandle : IPlatformHandle { void Destroy(); } + [Unstable] public interface INativeControlHostControlTopLevelAttachment : IDisposable { INativeControlHostImpl? AttachedTo { get; set; } @@ -27,6 +30,7 @@ namespace Avalonia.Controls.Platform void ShowInBounds(Rect rect); } + [Unstable] public interface ITopLevelImplWithNativeControlHost { INativeControlHostImpl? NativeControlHost { get; } diff --git a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs index ecbc6d2234..4c844ce30f 100644 --- a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs +++ b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs @@ -1,7 +1,9 @@ using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformIconLoader { IWindowIconImpl LoadIcon(string fileName); diff --git a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs index 4cd6640453..0658f9211c 100644 --- a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs +++ b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs @@ -1,9 +1,10 @@ using System; -using System.ComponentModel; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformLifetimeEventsImpl { /// diff --git a/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs b/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs index 264f5e4667..6ad07b1b13 100644 --- a/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs +++ b/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformNativeSurfaceHandle : IPlatformHandle { PixelSize Size { get; } diff --git a/src/Avalonia.Controls/Platform/IPopupImpl.cs b/src/Avalonia.Controls/Platform/IPopupImpl.cs index 477d5fab43..cd86045dee 100644 --- a/src/Avalonia.Controls/Platform/IPopupImpl.cs +++ b/src/Avalonia.Controls/Platform/IPopupImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines a platform-specific popup window implementation. /// + [Unstable] public interface IPopupImpl : IWindowBaseImpl { IPopupPositioner PopupPositioner { get; } diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs index b68391aa52..fcae3b6493 100644 --- a/src/Avalonia.Controls/Platform/IScreenImpl.cs +++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IScreenImpl { int ScreenCount { get; } diff --git a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs index 1685a6a38c..715eda5cfa 100644 --- a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs +++ b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs @@ -1,10 +1,12 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Defines a platform-specific system dialog implementation. /// + [Unstable] public interface ISystemDialogImpl { /// diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 80434882f7..bd0339f525 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -4,6 +4,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.Rendering; using JetBrains.Annotations; @@ -50,6 +51,7 @@ namespace Avalonia.Platform /// This interface is the common interface to and /// . /// + [Unstable] public interface ITopLevelImpl : IDisposable { /// diff --git a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs index bafb973765..a2e426ca08 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs @@ -1,9 +1,11 @@ using Avalonia.Input; using Avalonia.Input.TextInput; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public interface ITopLevelImplWithTextInputMethod : ITopLevelImpl { public ITextInputMethodImpl? TextInputMethod { get; } diff --git a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs index 9e72a40439..149a978c54 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs @@ -1,13 +1,16 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public interface INativeMenuExporter { void SetNativeMenu(NativeMenu? menu); } + [Unstable] public interface ITopLevelNativeMenuExporter : INativeMenuExporter { bool IsNativeMenuExported { get; } @@ -15,11 +18,13 @@ namespace Avalonia.Controls.Platform event EventHandler OnIsNativeMenuExportedChanged; } + [Unstable] public interface INativeMenuExporterProvider { INativeMenuExporter? NativeMenuExporter { get; } } - + + [Unstable] public interface ITopLevelImplWithNativeMenuExporter : ITopLevelImpl { ITopLevelNativeMenuExporter? NativeMenuExporter { get; } diff --git a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs index 289b569645..4ef9397d04 100644 --- a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs +++ b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs @@ -1,8 +1,10 @@ using System; using Avalonia.Controls.Platform; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface ITrayIconImpl : IDisposable { /// diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 066f4579c0..512fad6dfc 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -1,8 +1,10 @@ using System; using Avalonia.Automation.Peers; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IWindowBaseImpl : ITopLevelImpl { /// diff --git a/src/Avalonia.Controls/Platform/IWindowIconImpl.cs b/src/Avalonia.Controls/Platform/IWindowIconImpl.cs index 7086b7651c..4bb8844d97 100644 --- a/src/Avalonia.Controls/Platform/IWindowIconImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowIconImpl.cs @@ -1,7 +1,9 @@ using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IWindowIconImpl { void Save(Stream outputStream); diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index d4be4f9f45..af9392d440 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines a platform-specific window implementation. /// + [Unstable] public interface IWindowImpl : IWindowBaseImpl { /// diff --git a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs index fa26fe8fdd..5acc5adccd 100644 --- a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs +++ b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { + [Unstable] public interface IWindowingPlatform { IWindowImpl CreateWindow(); diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs index 4e5908456e..630d2d8efb 100644 --- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs +++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; -using System.Threading.Tasks; +using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Threading; namespace Avalonia.Controls.Platform { + [Unstable] public class InternalPlatformThreadingInterface : IPlatformThreadingInterface { public InternalPlatformThreadingInterface() diff --git a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs index f3104e4360..620ac9303f 100644 --- a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs +++ b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Describes a Drive's properties. /// + [Unstable] public class MountedVolumeInfo : IEquatable { public string? VolumeLabel { get; set; } diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index ee62316922..92f6f1cb52 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -1,9 +1,11 @@ using System; using System.Reactive.Disposables; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public static partial class PlatformManager { static bool s_designerMode; diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs index 62cd012d51..0a7daeaa24 100644 --- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs +++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs @@ -1,7 +1,9 @@ -using Avalonia.Platform; +using Avalonia.Metadata; +using Avalonia.Platform; namespace Avalonia.Controls.Platform.Surfaces { + [Unstable] public interface IFramebufferPlatformSurface { /// diff --git a/src/Avalonia.Controls/Presenters/IContentPresenter.cs b/src/Avalonia.Controls/Presenters/IContentPresenter.cs index ab4d61e3bd..673de4700b 100644 --- a/src/Avalonia.Controls/Presenters/IContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IContentPresenter.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { @@ -6,6 +7,7 @@ namespace Avalonia.Controls.Presenters /// Interface for controls that present a single item of data inside a /// template. /// + [NotClientImplementable] public interface IContentPresenter : IPresenter { /// diff --git a/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs b/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs index 78c4affe44..562638e94a 100644 --- a/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs +++ b/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs @@ -1,5 +1,6 @@ using Avalonia.Collections; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Styling; namespace Avalonia.Controls.Presenters @@ -15,6 +16,7 @@ namespace Avalonia.Controls.Presenters /// parent control's template is instantiated so they register themselves using this /// interface. /// + [NotClientImplementable] public interface IContentPresenterHost : ITemplatedControl { /// diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs index e7da3d4618..7cc72ef0a7 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs @@ -1,8 +1,10 @@ using System.Collections; using System.Collections.Specialized; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { + [NotClientImplementable] public interface IItemsPresenter : IPresenter { IEnumerable? Items { get; set; } diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs b/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs index ba9ee0fe31..db11474871 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Styling; namespace Avalonia.Controls.Presenters @@ -13,6 +14,7 @@ namespace Avalonia.Controls.Presenters /// parent control's template is instantiated so they register themselves using this /// interface. /// + [NotClientImplementable] public interface IItemsPresenterHost : ITemplatedControl { /// diff --git a/src/Avalonia.Controls/Presenters/IPresenter.cs b/src/Avalonia.Controls/Presenters/IPresenter.cs index 5318ea2757..0399983189 100644 --- a/src/Avalonia.Controls/Presenters/IPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IPresenter.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { @@ -12,6 +13,7 @@ namespace Avalonia.Controls.Presenters /// of a then that signals that the visual child /// of the presenter is not a part of the template. /// + [NotClientImplementable] public interface IPresenter : IControl, INamed { } diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 36d2ae9230..99b7be6385 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -2,6 +2,7 @@ using System; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -14,6 +15,7 @@ namespace Avalonia.Controls.Primitives /// () or an which is created /// on an . /// + [NotClientImplementable] public interface IPopupHost : IDisposable, IFocusScope { /// diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 8daf1ac68a..8d35a91e00 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -45,6 +45,7 @@ Copyright © 2019 Nikita Tsukanov */ using System; +using Avalonia.Metadata; using Avalonia.VisualTree; using Avalonia.Media; @@ -61,6 +62,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// requirement that a popup must intersect with or be at least partially adjacent to its parent /// surface. /// + [Unstable] public struct PopupPositionerParameters { private PopupGravity _gravity; @@ -429,6 +431,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// managed implementation is provided in for platforms /// on which popups can be arbitrarily positioned. /// + [NotClientImplementable] public interface IPopupPositioner { /// @@ -439,6 +442,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning void Update(PopupPositionerParameters parameters); } + [Unstable] static class PopupPositionerExtensions { public static void ConfigurePosition(ref this PopupPositionerParameters positionerParameters, diff --git a/src/Avalonia.Controls/Remote/RemoteServer.cs b/src/Avalonia.Controls/Remote/RemoteServer.cs index f4cc91a0e6..2cf2d2b97d 100644 --- a/src/Avalonia.Controls/Remote/RemoteServer.cs +++ b/src/Avalonia.Controls/Remote/RemoteServer.cs @@ -1,11 +1,12 @@ using System; using Avalonia.Controls.Embedding; using Avalonia.Controls.Remote.Server; -using Avalonia.Platform; +using Avalonia.Metadata; using Avalonia.Remote.Protocol; namespace Avalonia.Controls.Remote { + [Unstable] public class RemoteServer { private EmbeddableControlRoot _topLevel; diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index c9fd1dc3b8..e800f2f4b0 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -6,6 +6,7 @@ using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Input; @@ -18,6 +19,7 @@ using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; namespace Avalonia.Controls.Remote.Server { + [Unstable] public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface { private readonly IAvaloniaRemoteTransportConnection _transport; diff --git a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs index 61986a0661..ce763c3336 100644 --- a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs +++ b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs @@ -1,9 +1,11 @@ - +using Avalonia.Metadata; + namespace Avalonia.Controls.Templates { /// /// Defines an element that has a collection. /// + [NotClientImplementable] public interface IDataTemplateHost { /// diff --git a/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs b/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs index aef4f601be..22f0cebf57 100644 --- a/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs +++ b/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs @@ -1,15 +1,17 @@ using System; -using Avalonia.Media.Imaging; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.OpenGL.Imaging { + [Unstable] public interface IOpenGlBitmapImpl : IBitmapImpl { IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback); bool SupportsContext(IGlContext context); } + [Unstable] public interface IOpenGlBitmapAttachment : IDisposable { void Present(); diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs index f59a0a32c2..bdc3d075cf 100644 --- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; using JetBrains.Annotations; using SkiaSharp; @@ -6,6 +7,7 @@ using SkiaSharp; namespace Avalonia.Skia { /// + [Unstable] public class GlyphRunImpl : IGlyphRunImpl { public GlyphRunImpl([NotNull] SKTextBlob textBlob) diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index 5b6e5af60f..dcb4eac7ca 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -1,11 +1,13 @@ using System; using System.Runtime.InteropServices; +using Avalonia.Metadata; using Avalonia.Platform; using HarfBuzzSharp; using SkiaSharp; namespace Avalonia.Skia { + [Unstable] public class GlyphTypefaceImpl : IGlyphTypefaceImpl { private bool _isDisposed; diff --git a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs index 38fa5a5253..1391a8f195 100644 --- a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs @@ -1,8 +1,10 @@ +using Avalonia.Metadata; using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia { + [Unstable] public interface ISkiaDrawingContextImpl : IDrawingContextImpl { SKCanvas SkCanvas { get; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs index ad609a0810..602ea9b568 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public abstract class BrushImpl : IDisposable { public SharpDX.Direct2D1.Brush PlatformBrush { get; set; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index a259d8fab9..a0f98bbbc9 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -10,12 +10,14 @@ using SharpDX; using SharpDX.Direct2D1; using SharpDX.Mathematics.Interop; using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { /// /// Draws using Direct2D1. /// + [Unstable] public class DrawingContextImpl : IDrawingContextImpl { private readonly IVisualBrushRenderer _visualBrushRenderer; diff --git a/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs index ec88347a17..c84c14daac 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs @@ -1,4 +1,5 @@ using Avalonia.Logging; +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; @@ -7,6 +8,7 @@ namespace Avalonia.Direct2D1.Media /// /// The platform-specific interface for . /// + [Unstable] public abstract class GeometryImpl : IGeometryImpl { private const float ContourApproximation = 0.0001f; diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs index 4f2ed22a25..4154b44702 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; using HarfBuzzSharp; using SharpDX.DirectWrite; namespace Avalonia.Direct2D1.Media { + [Unstable] public class GlyphTypefaceImpl : IGlyphTypefaceImpl { private bool _isDisposed; diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 296cefad4e..17dc359ed7 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering.Utilities; using Avalonia.Utilities; using SharpDX.Direct2D1; namespace Avalonia.Direct2D1.Media { + [Unstable] public sealed class ImageBrushImpl : BrushImpl { private readonly OptionalDispose _bitmap; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs index af6d5c5e7b..843efe2cc4 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs @@ -1,10 +1,12 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using D2DBitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media { + [Unstable] public abstract class BitmapImpl : IBitmapImpl, IDisposable { public abstract Vector Dpi { get; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs index 63676e30b5..2656ab4c58 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using SharpDX.WIC; using Bitmap = SharpDX.Direct2D1.Bitmap; @@ -8,6 +9,7 @@ namespace Avalonia.Direct2D1.Media /// /// A Direct2D Bitmap implementation that uses a GPU memory bitmap as its image. /// + [Unstable] public class D2DBitmapImpl : BitmapImpl { private readonly Bitmap _direct2DBitmap; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs index 9a0e2ec00c..357e472d34 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Utilities; @@ -9,6 +10,7 @@ using D2DBitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media.Imaging { + [Unstable] public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory { private readonly BitmapRenderTarget _renderTarget; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index df07f7f39c..1156246b29 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -5,12 +5,14 @@ using SharpDX.WIC; using APixelFormat = Avalonia.Platform.PixelFormat; using AlphaFormat = Avalonia.Platform.AlphaFormat; using D2DBitmap = SharpDX.Direct2D1.Bitmap; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { /// /// A WIC implementation of a . /// + [Unstable] public class WicBitmapImpl : BitmapImpl { private readonly BitmapDecoder _decoder; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs index 1265a7bdf0..8c9d01f37d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using SharpDX.Direct2D1; namespace Avalonia.Direct2D1.Media { + [Unstable] public class WicRenderTargetBitmapImpl : WicBitmapImpl, IDrawingContextLayerImpl { private readonly WicRenderTarget _renderTarget; diff --git a/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs index 0e63d4cc03..5dfe683f59 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs @@ -1,8 +1,10 @@ using System.Linq; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public class LinearGradientBrushImpl : BrushImpl { public LinearGradientBrushImpl( diff --git a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs index 1fca6d4e33..0069e47001 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs @@ -1,8 +1,10 @@ using System.Linq; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public class RadialGradientBrushImpl : BrushImpl { public RadialGradientBrushImpl( diff --git a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs index fea1ca9157..b85494e2c1 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs @@ -1,7 +1,9 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public class SolidColorBrushImpl : BrushImpl { public SolidColorBrushImpl(ISolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target) diff --git a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs index e1f7aad1b2..ec8f82556d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Logging; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; using D2D = SharpDX.Direct2D1; @@ -8,6 +9,7 @@ using SweepDirection = SharpDX.Direct2D1.SweepDirection; namespace Avalonia.Direct2D1.Media { + [Unstable] public class StreamGeometryContextImpl : IStreamGeometryContextImpl { private readonly GeometrySink _sink; diff --git a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs index 2bc2b2db71..e1677c0ed1 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; @@ -6,6 +7,7 @@ namespace Avalonia.Direct2D1.Media /// /// A Direct2D implementation of a . /// + [Unstable] public class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl { /// diff --git a/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs index fe274701bf..3ecdb49e46 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs @@ -1,8 +1,10 @@ +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; namespace Avalonia.Direct2D1.Media { + [Unstable] public class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl { /// diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 96e45927da..f3754cd58f 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { + [Unstable] public class ScreenImpl : IScreenImpl { public int ScreenCount diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 6484ae6c54..7c3b8cf2d2 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -5,6 +5,7 @@ using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Win32.Interop; @@ -14,6 +15,7 @@ using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { + [Unstable] public class TrayIconImpl : ITrayIconImpl { private readonly int _uniqueId; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index f0036236ec..8d836ef452 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -21,12 +21,14 @@ using Avalonia.Win32.OpenGl; using Avalonia.Win32.WinRT; using Avalonia.Win32.WinRT.Composition; using static Avalonia.Win32.Interop.UnmanagedMethods; +using Avalonia.Metadata; namespace Avalonia.Win32 { /// /// Window implementation for Win32 platform. /// + [Unstable] public partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithNativeControlHost, ITopLevelImplWithTextInputMethod From f63ec5f5a915789a0dff264585ec895bd1b3cf61 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:47:51 -0400 Subject: [PATCH 492/820] Separate color picker control styles --- .../Themes/Default.xaml | 133 +--------------- .../Themes/Default/ColorSpectrum.xaml | 134 ++++++++++++++++ .../Themes/Fluent.xaml | 146 +++--------------- .../Themes/Fluent/ColorSpectrum.xaml | 134 ++++++++++++++++ 4 files changed, 290 insertions(+), 257 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 832daf8853..528eed9969 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,134 +1,7 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml new file mode 100644 index 0000000000..9596ca9653 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 545702ea84..fb656ce964 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,134 +1,26 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml new file mode 100644 index 0000000000..b209fe75b3 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 21dd8392bf95045d404dbb67a4e5c589d691eb27 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:48:39 -0400 Subject: [PATCH 493/820] Add initial ColorPreviewer primitive --- .../ColorPreviewer/AccentColorConverter.cs | 112 ++++++++++++++ .../ColorPreviewer.Properties.cs | 69 +++++++++ .../ColorPreviewer/ColorPreviewer.cs | 138 ++++++++++++++++++ .../Themes/Fluent.xaml | 1 + .../Themes/Fluent/ColorPreviewer.xaml | 90 ++++++++++++ 5 files changed, 410 insertions(+) create mode 100644 src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs new file mode 100644 index 0000000000..ad8f66251a --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs @@ -0,0 +1,112 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Creates an accent color for a given base color value and step parameter. + /// + public class AccentColorConverter : IValueConverter + { + /// + /// The amount to change the Value channel for each accent color step. + /// + public const double ValueDelta = 0.1; + + /// + public object? Convert( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) + { + int accentStep; + Color? rgbColor = null; + HsvColor? hsvColor = null; + + // Get the current color in HSV + if (value is Color valueColor) + { + rgbColor = valueColor; + } + else if (value is HsvColor valueHsvColor) + { + hsvColor = valueHsvColor; + } + else if (value is SolidColorBrush valueBrush) + { + rgbColor = valueBrush.Color; + } + else + { + // Invalid color value provided + return AvaloniaProperty.UnsetValue; + } + + // Get the value component delta + try + { + accentStep = int.Parse(parameter?.ToString() ?? "", CultureInfo.InvariantCulture); + } + catch + { + // Invalid parameter provided, unable to convert to integer + return AvaloniaProperty.UnsetValue; + } + + if (hsvColor == null && + rgbColor != null) + { + hsvColor = rgbColor.Value.ToHsv(); + } + + if (hsvColor != null) + { + return new SolidColorBrush(GetAccent(hsvColor.Value, accentStep).ToRgb()); + } + else + { + return AvaloniaProperty.UnsetValue; + } + } + + /// + public object? ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) + { + return AvaloniaProperty.UnsetValue; + } + + /// + /// This does not account for perceptual differences and also does not match with + /// system accent color calculation. + /// + /// + /// Use the HSV representation as it's more perceptual. + /// In most cases only the value is changed by a fixed percentage so the algorithm is reproducible. + /// + /// The base color to calculate the accent from. + /// The number of accent color steps to move. + /// The new accent color. + public static HsvColor GetAccent(HsvColor hsvColor, int accentStep) + { + if (accentStep != 0) + { + double colorValue = hsvColor.V; + colorValue += (accentStep * AccentColorConverter.ValueDelta); + colorValue = Math.Round(colorValue, 2); + + return new HsvColor(hsvColor.A, hsvColor.H, hsvColor.S, colorValue); + } + else + { + return hsvColor; + } + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs new file mode 100644 index 0000000000..74c0943919 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -0,0 +1,69 @@ +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + public partial class ColorPreviewer + { + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Colors.White); + + /// + /// Gets or sets the currently previewed color in the RGB color model. + /// + /// + /// For control authors use instead to avoid loss + /// of precision and color drifting. + /// + public Color Color + { + get => GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.Transparent.ToHsv()); + + /// + /// Gets or sets the currently previewed color in the HSV color model. + /// + /// + /// This should be used in all cases instead of the property. + /// Internally, the uses the HSV color model and using + /// this property will avoid loss of precision and color drifting. + /// + public HsvColor HsvColor + { + get => GetValue(HsvColorProperty); + set => SetValue(HsvColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ShowAccentColorsProperty = + AvaloniaProperty.Register( + nameof(ShowAccentColors), + true); + + /// + /// Gets or sets a value indicating whether accent colors are shown along + /// with the preview color. + /// + public bool ShowAccentColors + { + get => (bool)this.GetValue(ShowAccentColorsProperty); + set => SetValue(ShowAccentColorsProperty, value); + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs new file mode 100644 index 0000000000..35bd62601f --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -0,0 +1,138 @@ +using System; +using System.Globalization; +using Avalonia.Controls.Metadata; +using Avalonia.Input; +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Presents a preview color with optional accent colors. + /// + [TemplatePart(Name = nameof(AccentDec1Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(AccentDec2Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(AccentInc1Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(AccentInc2Border), Type = typeof(Border))] + public partial class ColorPreviewer : TemplatedControl + { + /// + /// Event for when the selected color changes within the previewer. + /// This happens when an accent color is pressed. + /// + public event EventHandler? ColorChanged; + + private bool eventsConnected = false; + + private Border? AccentDec1Border; + private Border? AccentDec2Border; + private Border? AccentInc1Border; + private Border? AccentInc2Border; + + /// + /// Initializes a new instance of the class. + /// + public ColorPreviewer() : base() + { + } + + /// + /// Connects or disconnects all control event handlers. + /// + /// True to connect event handlers, otherwise false. + private void ConnectEvents(bool connected) + { + if (connected == true && eventsConnected == false) + { + // Add all events + if (AccentDec1Border != null) { AccentDec1Border.PointerPressed += AccentBorder_PointerPressed; } + if (AccentDec2Border != null) { AccentDec2Border.PointerPressed += AccentBorder_PointerPressed; } + if (AccentInc1Border != null) { AccentInc1Border.PointerPressed += AccentBorder_PointerPressed; } + if (AccentInc2Border != null) { AccentInc2Border.PointerPressed += AccentBorder_PointerPressed; } + + eventsConnected = true; + } + else if (connected == false && eventsConnected == true) + { + // Remove all events + if (AccentDec1Border != null) { AccentDec1Border.PointerPressed -= AccentBorder_PointerPressed; } + if (AccentDec2Border != null) { AccentDec2Border.PointerPressed -= AccentBorder_PointerPressed; } + if (AccentInc1Border != null) { AccentInc1Border.PointerPressed -= AccentBorder_PointerPressed; } + if (AccentInc2Border != null) { AccentInc2Border.PointerPressed -= AccentBorder_PointerPressed; } + + eventsConnected = false; + } + + return; + } + + /// + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + // Remove any existing events present if the control was previously loaded then unloaded + ConnectEvents(false); + + AccentDec1Border = e.NameScope.Find(nameof(AccentDec1Border)); + AccentDec2Border = e.NameScope.Find(nameof(AccentDec2Border)); + AccentInc1Border = e.NameScope.Find(nameof(AccentInc1Border)); + AccentInc2Border = e.NameScope.Find(nameof(AccentInc2Border)); + + // Must connect after controls are found + ConnectEvents(true); + + base.OnApplyTemplate(e); + } + + /// + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + // Always keep the two color properties in sync + if (change.Property == ColorProperty) + { + HsvColor = Color.ToHsv(); + } + else if (change.Property == HsvColorProperty) + { + Color = HsvColor.ToRgb(); + } + + base.OnPropertyChanged(change); + } + + /// + /// Called before the event occurs. + /// + /// The newly selected color. + protected virtual void OnColorChanged(HsvColor newColor) + { + var oldColor = HsvColor; + HsvColor = newColor; + + ColorChanged?.Invoke(this, new ColorChangedEventArgs(oldColor.ToRgb(), newColor.ToRgb())); + + return; + } + + /// + /// Event handler for when an accent color border is pressed. + /// This will update the color to the background of the pressed panel. + /// + private void AccentBorder_PointerPressed(object? sender, PointerPressedEventArgs e) + { + Border? border = sender as Border; + int accentStep = 0; + HsvColor hsvColor = HsvColor; + + // Get the value component delta + try + { + accentStep = int.Parse(border?.Tag?.ToString() ?? "", CultureInfo.InvariantCulture); + } + catch { } + + HsvColor newHsvColor = AccentColorConverter.GetAccent(hsvColor, accentStep); + OnColorChanged(newHsvColor); + + return; + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index fb656ce964..d96066e56b 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -21,6 +21,7 @@ + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml new file mode 100644 index 0000000000..a91da45578 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml @@ -0,0 +1,90 @@ + + + + + + + + + From ad5249992df8667f2785cf2504b2ddcc62a730c8 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:49:24 -0400 Subject: [PATCH 494/820] Add initial ColorSlider primitive --- .../ColorComponent.cs | 28 ++ .../{ColorSpectrum => }/ColorHelpers.cs | 344 +++++++++++++++++- .../ColorModel.cs | 18 + .../ColorSlider/ColorSlider.Properties.cs | 143 ++++++++ .../ColorSlider/ColorSlider.cs | 221 +++++++++++ .../Themes/Fluent.xaml | 1 + .../Themes/Fluent/ColorSlider.xaml | 172 +++++++++ 7 files changed, 925 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/ColorComponent.cs rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => }/ColorHelpers.cs (50%) create mode 100644 src/Avalonia.Controls.ColorPicker/ColorModel.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml diff --git a/src/Avalonia.Controls.ColorPicker/ColorComponent.cs b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs new file mode 100644 index 0000000000..a0385c03b4 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs @@ -0,0 +1,28 @@ +namespace Avalonia.Controls +{ + /// + /// Defines a specific component within a color model. + /// + public enum ColorComponent + { + /// + /// Represents the alpha component. + /// + Alpha, + + /// + /// Represents the first color component which is Red when RGB or Hue when HSV. + /// + Component1, + + /// + /// Represents the second color component which is Green when RGB or Saturation when HSV. + /// + Component2, + + /// + /// Represents the third color component which is Blue when RGB or Value when HSV. + /// + Component3 + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/ColorHelpers.cs similarity index 50% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs rename to src/Avalonia.Controls.ColorPicker/ColorHelpers.cs index b912d39aba..37c6f552d6 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorHelpers.cs @@ -6,9 +6,12 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -26,6 +29,291 @@ namespace Avalonia.Controls.Primitives return string.Empty; } + /// + /// Generates a new bitmap of the specified size by changing a specific color component. + /// This will produce a gradient representing a sweep of all possible values of the color component. + /// + /// The pixel width (X, horizontal) of the resulting bitmap. + /// The pixel height (Y, vertical) of the resulting bitmap. + /// The orientation of the resulting bitmap (gradient direction). + /// The color model being used: RGBA or HSVA. + /// The specific color component to sweep. + /// The base HSV color used for components not being changed. + /// Fix the alpha component value to maximum during calculation. + /// This will remove any alpha/transparency from the other component backgrounds. + /// Fix the saturation and value components to maximum + /// during calculation with the HSVA color model. + /// This will ensure colors are always discernible regardless of saturation/value. + /// A new bitmap representing a gradient of color component values. + internal static async Task CreateComponentBitmapAsync( + int width, + int height, + Orientation orientation, + ColorModel colorModel, + ColorComponent component, + HsvColor baseHsvColor, + bool isAlphaMaxForced, + bool isSaturationValueMaxForced) + { + if (width == 0 || height == 0) + { + return new byte[0]; + } + + var bitmap = await Task.Run(() => + { + int pixelDataIndex = 0; + double componentStep; + byte[] bgraPixelData; + Color baseRgbColor = Colors.White; + Color rgbColor; + int bgraPixelDataHeight; + int bgraPixelDataWidth; + + // Allocate the buffer + // BGRA formatted color components 1 byte each (4 bytes in a pixel) + bgraPixelData = new byte[width * height * 4]; + bgraPixelDataHeight = height * 4; + bgraPixelDataWidth = width * 4; + + // Maximize alpha component value + if (isAlphaMaxForced && + component != ColorComponent.Alpha) + { + baseHsvColor = new HsvColor(1.0, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); + } + + // Convert HSV to RGB once + if (colorModel == ColorModel.Rgba) + { + baseRgbColor = baseHsvColor.ToRgb(); + } + + // Maximize Saturation and Value components when in HSVA mode + if (isSaturationValueMaxForced && + colorModel == ColorModel.Hsva && + component != ColorComponent.Alpha) + { + switch (component) + { + case ColorComponent.Component1: + baseHsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, 1.0, 1.0); + break; + case ColorComponent.Component2: + baseHsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, 1.0); + break; + case ColorComponent.Component3: + baseHsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, 1.0, baseHsvColor.V); + break; + } + } + + // Create the color component gradient + if (orientation == Orientation.Horizontal) + { + // Determine the numerical increment of the color steps within the component + if (colorModel == ColorModel.Hsva) + { + if (component == ColorComponent.Component1) + { + componentStep = 360.0 / width; + } + else + { + componentStep = 1.0 / width; + } + } + else + { + componentStep = 255.0 / width; + } + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (y == 0) + { + rgbColor = GetColor(x * componentStep); + + // Get a new color + bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(rgbColor.B * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(rgbColor.G * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(rgbColor.R * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 3] = rgbColor.A; + } + else + { + // Use the color in the row above + // Remember the pixel data is 1 dimensional instead of 2 + bgraPixelData[pixelDataIndex + 0] = bgraPixelData[pixelDataIndex + 0 - bgraPixelDataWidth]; + bgraPixelData[pixelDataIndex + 1] = bgraPixelData[pixelDataIndex + 1 - bgraPixelDataWidth]; + bgraPixelData[pixelDataIndex + 2] = bgraPixelData[pixelDataIndex + 2 - bgraPixelDataWidth]; + bgraPixelData[pixelDataIndex + 3] = bgraPixelData[pixelDataIndex + 3 - bgraPixelDataWidth]; + } + + pixelDataIndex += 4; + } + } + } + else + { + // Determine the numerical increment of the color steps within the component + if (colorModel == ColorModel.Hsva) + { + if (component == ColorComponent.Component1) + { + componentStep = 360.0 / height; + } + else + { + componentStep = 1.0 / height; + } + } + else + { + componentStep = 255.0 / height; + } + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (x == 0) + { + // The lowest component value should be at the 'bottom' of the bitmap + rgbColor = GetColor((height - 1 - y) * componentStep); + + // Get a new color + bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(rgbColor.B * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(rgbColor.G * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(rgbColor.R * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 3] = rgbColor.A; + } + else + { + // Use the color in the column to the left + // Remember the pixel data is 1 dimensional instead of 2 + bgraPixelData[pixelDataIndex + 0] = bgraPixelData[pixelDataIndex - 4]; + bgraPixelData[pixelDataIndex + 1] = bgraPixelData[pixelDataIndex - 3]; + bgraPixelData[pixelDataIndex + 2] = bgraPixelData[pixelDataIndex - 2]; + bgraPixelData[pixelDataIndex + 3] = bgraPixelData[pixelDataIndex - 1]; + } + + pixelDataIndex += 4; + } + } + } + + Color GetColor(double componentValue) + { + Color newRgbColor = Colors.White; + + switch (component) + { + case ColorComponent.Component1: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep hue + newRgbColor = HsvColor.ToRgb( + MathUtilities.Clamp(componentValue, 0.0, 360.0), + baseHsvColor.S, + baseHsvColor.V, + baseHsvColor.A); + } + else + { + // Sweep red + newRgbColor = new Color( + baseRgbColor.A, + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0)), + baseRgbColor.G, + baseRgbColor.B); + } + + break; + } + case ColorComponent.Component2: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep saturation + newRgbColor = HsvColor.ToRgb( + baseHsvColor.H, + MathUtilities.Clamp(componentValue, 0.0, 1.0), + baseHsvColor.V, + baseHsvColor.A); + } + else + { + // Sweep green + newRgbColor = new Color( + baseRgbColor.A, + baseRgbColor.R, + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0)), + baseRgbColor.B); + } + + break; + } + case ColorComponent.Component3: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep value + newRgbColor = HsvColor.ToRgb( + baseHsvColor.H, + baseHsvColor.S, + MathUtilities.Clamp(componentValue, 0.0, 1.0), + baseHsvColor.A); + } + else + { + // Sweep blue + newRgbColor = new Color( + baseRgbColor.A, + baseRgbColor.R, + baseRgbColor.G, + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0))); + } + + break; + } + case ColorComponent.Alpha: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep alpha + newRgbColor = HsvColor.ToRgb( + baseHsvColor.H, + baseHsvColor.S, + baseHsvColor.V, + MathUtilities.Clamp(componentValue, 0.0, 1.0)); + } + else + { + // Sweep alpha + newRgbColor = new Color( + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0)), + baseRgbColor.R, + baseRgbColor.G, + baseRgbColor.B); + } + + break; + } + } + + return newRgbColor; + } + + return bgraPixelData; + }); + + return bitmap; + } + public static Hsv IncrementColorComponent( Hsv originalHsv, HsvComponent component, @@ -363,14 +651,22 @@ namespace Avalonia.Controls.Primitives return originalAlpha / 100; } + /// + /// + /// + /// The pixel width of the bitmap. + /// The pixel height of the bitmap. + /// + /// public static WriteableBitmap CreateBitmapFromPixelData( int pixelWidth, int pixelHeight, List bgraPixelData) { - Vector dpi = new Vector(96, 96); // Standard may need to change on some devices + // Standard may need to change on some devices + Vector dpi = new Vector(96, 96); - WriteableBitmap bitmap = new WriteableBitmap( + var bitmap = new WriteableBitmap( new PixelSize(pixelWidth, pixelHeight), dpi, PixelFormat.Bgra8888, @@ -385,6 +681,50 @@ namespace Avalonia.Controls.Primitives return bitmap; } + /// + /// Converts the given bitmap (in raw BGRA pre-multiplied alpha pixels) into an image brush + /// that can be used in the UI. + /// + /// The bitmap (in raw BGRA pre-multiplied alpha pixels) + /// to convert to a brush. + /// The pixel width of the bitmap. + /// The pixel height of the bitmap. + /// A new . + public static IBrush? BitmapToBrushAsync( + byte[] bgraPixelData, + int pixelWidth, + int pixelHeight) + { + if (bgraPixelData.Length == 0 || + (pixelWidth == 0 && + pixelHeight == 0)) + { + return null; + } + + // Standard may need to change on some devices + Vector dpi = new Vector(96, 96); + + var bitmap = new WriteableBitmap( + new PixelSize(pixelWidth, pixelHeight), + dpi, + PixelFormat.Bgra8888, + AlphaFormat.Premul); + + // Warning: This is highly questionable + using (var frameBuffer = bitmap.Lock()) + { + Marshal.Copy(bgraPixelData, 0, frameBuffer.Address, bgraPixelData.Length); + } + + var brush = new ImageBrush(bitmap) + { + Stretch = Stretch.Fill + }; + + return brush; + } + /// /// Gets the relative (perceptual) luminance/brightness of the given color. /// 1 is closer to white while 0 is closer to black. diff --git a/src/Avalonia.Controls.ColorPicker/ColorModel.cs b/src/Avalonia.Controls.ColorPicker/ColorModel.cs new file mode 100644 index 0000000000..f11b514706 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorModel.cs @@ -0,0 +1,18 @@ +namespace Avalonia.Controls +{ + /// + /// Defines the model used to represent colors. + /// + public enum ColorModel + { + /// + /// Color is represented by hue, saturation, value and alpha components. + /// + Hsva, + + /// + /// Color is represented by red, green, blue and alpha components. + /// + Rgba + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs new file mode 100644 index 0000000000..3aa3e3a789 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs @@ -0,0 +1,143 @@ +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + public partial class ColorSlider + { + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Colors.White); + + /// + /// Gets or sets the currently selected color in the RGB color model. + /// + /// + /// Use this property instead of when in + /// to avoid loss of precision and color drifting. + /// + public Color Color + { + get => GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorComponentProperty = + AvaloniaProperty.Register( + nameof(ColorComponent), + ColorComponent.Component1); + + /// + /// Gets or sets the color component represented by the slider. + /// + public ColorComponent ColorComponent + { + get => GetValue(ColorComponentProperty); + set => SetValue(ColorComponentProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorModelProperty = + AvaloniaProperty.Register( + nameof(ColorModel), + ColorModel.Rgba); + + /// + /// Gets or sets the active color model used by the slider. + /// + public ColorModel ColorModel + { + get => GetValue(ColorModelProperty); + set => SetValue(ColorModelProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.White.ToHsv()); + + /// + /// Gets or sets the currently selected color in the HSV color model. + /// + /// + /// Use this property instead of when in + /// to avoid loss of precision and color drifting. + /// + public HsvColor HsvColor + { + get => GetValue(HsvColorProperty); + set => SetValue(HsvColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAlphaMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsAlphaMaxForced), + true); + + /// + /// Gets or sets a value indicating whether the alpha component is always forced to maximum for components + /// other than . + /// This ensures that the background is always visible and never transparent regardless of the actual color. + /// + public bool IsAlphaMaxForced + { + get => GetValue(IsAlphaMaxForcedProperty); + set => SetValue(IsAlphaMaxForcedProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAutoUpdatingEnabledProperty = + AvaloniaProperty.Register( + nameof(IsAutoUpdatingEnabled), + true); + + /// + /// Gets or sets a value indicating whether automatic background and foreground updates will be + /// calculated when the set color changes. + /// + /// + /// This can be disabled for performance reasons when working with multiple sliders. + /// + public bool IsAutoUpdatingEnabled + { + get => GetValue(IsAutoUpdatingEnabledProperty); + set => SetValue(IsAutoUpdatingEnabledProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsSaturationValueMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsSaturationValueMaxForced), + true); + + /// + /// Gets or sets a value indicating whether the saturation and value components are always forced to maximum values + /// when using the HSVA color model. Only component values other than will be changed. + /// This ensures, for example, that the Hue background is always visible and never washed out regardless of the actual color. + /// + public bool IsSaturationValueMaxForced + { + get => GetValue(IsSaturationValueMaxForcedProperty); + set => SetValue(IsSaturationValueMaxForcedProperty, value); + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs new file mode 100644 index 0000000000..e3f2dc3555 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -0,0 +1,221 @@ +using System; +using Avalonia.Media; +using Avalonia.Utilities; + +namespace Avalonia.Controls.Primitives +{ + /// + /// A slider with a background that represents a single color component. + /// + public partial class ColorSlider : Slider + { + private Size cachedSize = Size.Empty; + + /// + /// Initializes a new instance of the class. + /// + public ColorSlider() : base() + { + } + + /// + /// Update the slider's Foreground and Background brushes based on the current slider state and color. + /// + /// + /// Manually refreshes the background gradient of the slider. + /// This is callable separately for performance reasons. + /// + public void UpdateColors() + { + HsvColor hsvColor = HsvColor; + + // Calculate and set the background + UpdateBackground(hsvColor); + + // Calculate and set the foreground ensuring contrast with the background + Color rgbColor = hsvColor.ToRgb(); + Color selectedRgbColor; + double sliderPercent = Value / (Maximum - Minimum); + + var component = ColorComponent; + + if (ColorModel == ColorModel.Hsva) + { + if (IsAlphaMaxForced && + component != ColorComponent.Alpha) + { + hsvColor = new HsvColor(1.0, hsvColor.H, hsvColor.S, hsvColor.V); + } + + switch (component) + { + case ColorComponent.Component1: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); + + hsvColor = new HsvColor( + hsvColor.A, + componentValue, + IsSaturationValueMaxForced ? 1.0 : hsvColor.S, + IsSaturationValueMaxForced ? 1.0 : hsvColor.V); + + break; + } + + case ColorComponent.Component2: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + + hsvColor = new HsvColor( + hsvColor.A, + hsvColor.H, + componentValue, + IsSaturationValueMaxForced ? 1.0 : hsvColor.V); + + break; + } + + case ColorComponent.Component3: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + + hsvColor = new HsvColor( + hsvColor.A, + hsvColor.H, + IsSaturationValueMaxForced ? 1.0 : hsvColor.S, + componentValue); + + break; + } + } + + selectedRgbColor = hsvColor.ToRgb(); + } + else + { + if (IsAlphaMaxForced && + component != ColorComponent.Alpha) + { + rgbColor = new Color(255, rgbColor.R, rgbColor.G, rgbColor.B); + } + + byte componentValue = Convert.ToByte(MathUtilities.Clamp(sliderPercent * 255, 0, 255)); + + switch (component) + { + case ColorComponent.Component1: + rgbColor = new Color(rgbColor.A, componentValue, rgbColor.G, rgbColor.B); + break; + case ColorComponent.Component2: + rgbColor = new Color(rgbColor.A, rgbColor.R, componentValue, rgbColor.B); + break; + case ColorComponent.Component3: + rgbColor = new Color(rgbColor.A, rgbColor.R, rgbColor.G, componentValue); + break; + } + + selectedRgbColor = rgbColor; + } + + //var converter = new ContrastBrushConverter(); + //this.Foreground = converter.Convert(selectedRgbColor, typeof(Brush), this.DefaultForeground, null) as Brush; + + return; + } + + /// + /// Generates a new background image for the color slider and applies it. + /// + private async void UpdateBackground(HsvColor color) + { + // Updates may be requested when sliders are not in the visual tree. + // For first-time load this is handled by the Loaded event. + // However, after that problems may arise, consider the following case: + // + // (1) Backgrounds are drawn normally the first time on Loaded. + // Actual height/width are available. + // (2) The palette tab is selected which has no sliders + // (3) The picker flyout is closed + // (4) Externally the color is changed + // The color change will trigger slider background updates but + // with the flyout closed, actual height/width are zero. + // No zero size bitmap can be generated. + // (5) The picker flyout is re-opened by the user and the default + // last-opened tab will be viewed: palette. + // No loaded events will be fired for sliders. The color change + // event was already handled in (4). The sliders will never + // be updated. + // + // In this case the sliders become out of sync with the Color because there is no way + // to tell when they actually come into view. To work around this, force a re-render of + // the background with the last size of the slider. This last size will be when it was + // last loaded or updated. + // + // In the future additional consideration may be required for SizeChanged of the control. + // This work-around will also cause issues if display scaling changes in the special + // case where cached sizes are required. + + var width = Convert.ToInt32(Bounds.Width); + var height = Convert.ToInt32(Bounds.Height); + + if (width == 0 || height == 0) + { + // Attempt to use the last size if it was available + if (cachedSize.IsDefault == false) + { + width = Convert.ToInt32(cachedSize.Width); + height = Convert.ToInt32(cachedSize.Height); + } + } + else + { + cachedSize = new Size(width, height); + } + + var bitmap = await ColorHelpers.CreateComponentBitmapAsync( + width, + height, + Orientation, + ColorModel, + ColorComponent, + color, + IsAlphaMaxForced, + IsSaturationValueMaxForced); + + if (bitmap != null) + { + Background = ColorHelpers.BitmapToBrushAsync(bitmap, width, height); + } + + return; + } + + /// + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + bool update = false; + + if (change.Property == ColorProperty) + { + // Sync with HSV (which is primary) + HsvColor = Color.ToHsv(); + update = true; + } + else if (change.Property == HsvColorProperty) + { + update = true; + } + else if (change.Property == BoundsProperty) + { + update = true; + } + + if (update && IsAutoUpdatingEnabled) + { + UpdateColors(); + } + + base.OnPropertyChanged(change); + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index d96066e56b..c25d79727f 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -22,6 +22,7 @@ + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml new file mode 100644 index 0000000000..1ca9b12ffe --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + From a5b3e85dfc519bee6a762e2d5c45869002e0d135 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:50:00 -0400 Subject: [PATCH 495/820] Simplify default color property values --- .../ColorSpectrum/ColorSpectrum.Properties.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index 824bf9ab05..ab5b83afcb 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -29,7 +29,7 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty ColorProperty = AvaloniaProperty.Register( nameof(Color), - Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF)); + Colors.White); /// /// Gets or sets the two HSV color components displayed by the spectrum. @@ -71,7 +71,7 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty HsvColorProperty = AvaloniaProperty.Register( nameof(HsvColor), - new HsvColor(1, 0, 0, 1)); + Colors.White.ToHsv()); /// /// Gets or sets the maximum value of the Hue component in the range from 0..359. From e9cb628f820ba7226cce379a601b9867053eef76 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:50:22 -0400 Subject: [PATCH 496/820] Improve formatting --- .../Converters/CornerRadiusFilterConverter.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs index b2433bfd97..a91f143019 100644 --- a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs +++ b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; - using Avalonia.Data.Converters; namespace Avalonia.Controls.Converters @@ -22,7 +21,12 @@ namespace Avalonia.Controls.Converters /// public double Scale { get; set; } = 1; - public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + /// + public object? Convert( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) { if (!(value is CornerRadius radius)) { @@ -36,7 +40,12 @@ namespace Avalonia.Controls.Converters Filter.HasAllFlags(Corners.BottomLeft) ? radius.BottomLeft * Scale : 0); } - public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + /// + public object? ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) { throw new NotImplementedException(); } From c068adb60944b8e6a2ba1bfc1a3666c8e0caf1fd Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:51:01 -0400 Subject: [PATCH 497/820] Update ColorPickerPage in ControlCatalog with ColorSlider and ColorPreviewer --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index ec34193f8c..f343fd8f59 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -3,14 +3,18 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" x:Class="ControlCatalog.Pages.ColorPickerPage"> - - + + + + + + + From b6b8e96fd97a5d77d7e26ab2e189ab9d84849e48 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:56:49 -0400 Subject: [PATCH 498/820] Move spectrum enums into ColorSpectrum directory --- .../{ => ColorSpectrum}/ColorSpectrumComponents.cs | 0 .../{ => ColorSpectrum}/ColorSpectrumShape.cs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ => ColorSpectrum}/ColorSpectrumComponents.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ => ColorSpectrum}/ColorSpectrumShape.cs (100%) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumComponents.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumComponents.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumShape.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumShape.cs From 1f5e3c0d9dcfa2f95ad2d217521f075d33364d5c Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 19:28:21 -0400 Subject: [PATCH 499/820] Move helpers into separate directory --- src/Avalonia.Controls.ColorPicker/{ => Helpers}/ColorHelpers.cs | 0 .../{ColorSpectrum => Helpers}/Hsv.cs | 0 .../{ColorSpectrum => Helpers}/IncrementAmount.cs | 0 .../{ColorSpectrum => Helpers}/IncrementDirection.cs | 0 .../{ColorSpectrum => Helpers}/Rgb.cs | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ => Helpers}/ColorHelpers.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/Hsv.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/IncrementAmount.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/IncrementDirection.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/Rgb.cs (100%) diff --git a/src/Avalonia.Controls.ColorPicker/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorHelpers.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls.ColorPicker/Helpers/Hsv.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/Hsv.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls.ColorPicker/Helpers/IncrementAmount.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/IncrementAmount.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls.ColorPicker/Helpers/IncrementDirection.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/IncrementDirection.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls.ColorPicker/Helpers/Rgb.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/Rgb.cs From 65e5e580acd359c0103e7b9bb3b0397d7676f1e1 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 21:57:19 -0400 Subject: [PATCH 500/820] Make ColorSlider fully functional --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 3 +- .../ColorSlider/ColorSlider.cs | 367 ++++++++++++------ .../Helpers/ColorHelpers.cs | 2 +- 3 files changed, 241 insertions(+), 131 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index f343fd8f59..09ec15ad15 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -25,7 +25,8 @@ Width="256" /> + RowDefinitions="Auto,Auto,Auto,Auto,Auto" + Margin="0,10,0,0"> public partial class ColorSlider : Slider { - private Size cachedSize = Size.Empty; + private bool disableUpdates = false; /// /// Initializes a new instance of the class. @@ -19,200 +20,308 @@ namespace Avalonia.Controls.Primitives } /// - /// Update the slider's Foreground and Background brushes based on the current slider state and color. + /// Generates a new background image for the color slider and applies it. /// - /// - /// Manually refreshes the background gradient of the slider. - /// This is callable separately for performance reasons. - /// - public void UpdateColors() + private async void UpdateBackground() { - HsvColor hsvColor = HsvColor; + // In Avalonia, Bounds returns the actual device-independent pixel size of a control. + // However, this is not necessarily the size of the control rendered on a display. + // A desktop or application scaling factor may be applied which must be accounted for here. + // Remember bitmaps in Avalonia are rendered mapping to actual device pixels, not the device- + // independent pixels of controls. - // Calculate and set the background - UpdateBackground(hsvColor); + var scale = LayoutHelper.GetLayoutScale(this); + var pixelWidth = Convert.ToInt32(Bounds.Width * scale); + var pixelHeight = Convert.ToInt32(Bounds.Height * scale); - // Calculate and set the foreground ensuring contrast with the background - Color rgbColor = hsvColor.ToRgb(); - Color selectedRgbColor; - double sliderPercent = Value / (Maximum - Minimum); + if (pixelWidth != 0 && pixelHeight != 0) + { + var bitmap = await ColorHelpers.CreateComponentBitmapAsync( + pixelWidth, + pixelHeight, + Orientation, + ColorModel, + ColorComponent, + HsvColor, + IsAlphaMaxForced, + IsSaturationValueMaxForced); + + if (bitmap != null) + { + Background = ColorHelpers.BitmapToBrushAsync(bitmap, pixelWidth, pixelHeight); + } + } + + return; + } + /// + /// Updates the slider property values by applying the current color. + /// + /// + /// Warning: This will trigger property changed updates. + /// Consider using externally. + /// + private void SetColorToSliderValues() + { + var hsvColor = HsvColor; + var rgbColor = Color; var component = ColorComponent; if (ColorModel == ColorModel.Hsva) { - if (IsAlphaMaxForced && - component != ColorComponent.Alpha) + // Note: Components converted into a usable range for the user + switch (component) { - hsvColor = new HsvColor(1.0, hsvColor.H, hsvColor.S, hsvColor.V); + case ColorComponent.Alpha: + Minimum = 0; + Maximum = 100; + Value = hsvColor.A * 100; + break; + case ColorComponent.Component1: // Hue + Minimum = 0; + Maximum = 359; + Value = hsvColor.H; + break; + case ColorComponent.Component2: // Saturation + Minimum = 0; + Maximum = 100; + Value = hsvColor.S * 100; + break; + case ColorComponent.Component3: // Value + Minimum = 0; + Maximum = 100; + Value = hsvColor.V * 100; + break; } - + } + else + { switch (component) { - case ColorComponent.Component1: - { - var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); - - hsvColor = new HsvColor( - hsvColor.A, - componentValue, - IsSaturationValueMaxForced ? 1.0 : hsvColor.S, - IsSaturationValueMaxForced ? 1.0 : hsvColor.V); - - break; - } + case ColorComponent.Alpha: + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.A); + break; + case ColorComponent.Component1: // Red + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.R); + break; + case ColorComponent.Component2: // Green + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.G); + break; + case ColorComponent.Component3: // Blue + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.B); + break; + } + } - case ColorComponent.Component2: - { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + return; + } - hsvColor = new HsvColor( - hsvColor.A, - hsvColor.H, - componentValue, - IsSaturationValueMaxForced ? 1.0 : hsvColor.V); + /// + /// Gets the current color determined by the slider values. + /// + private (Color, HsvColor) GetColorFromSliderValues() + { + HsvColor hsvColor = new HsvColor(); + Color rgbColor = new Color(); + double sliderPercent = Value / (Maximum - Minimum); - break; - } + var baseHsvColor = HsvColor; + var baseRgbColor = Color; + var component = ColorComponent; + if (ColorModel == ColorModel.Hsva) + { + switch (component) + { + case ColorComponent.Alpha: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + hsvColor = new HsvColor(componentValue, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); + break; + } + case ColorComponent.Component1: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); + hsvColor = new HsvColor(baseHsvColor.A, componentValue, baseHsvColor.S, baseHsvColor.V); + break; + } + case ColorComponent.Component2: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, componentValue, baseHsvColor.V); + break; + } case ColorComponent.Component3: - { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - - hsvColor = new HsvColor( - hsvColor.A, - hsvColor.H, - IsSaturationValueMaxForced ? 1.0 : hsvColor.S, - componentValue); - - break; - } + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, componentValue); + break; + } } - selectedRgbColor = hsvColor.ToRgb(); + return (hsvColor.ToRgb(), hsvColor); } else { - if (IsAlphaMaxForced && - component != ColorComponent.Alpha) - { - rgbColor = new Color(255, rgbColor.R, rgbColor.G, rgbColor.B); - } - byte componentValue = Convert.ToByte(MathUtilities.Clamp(sliderPercent * 255, 0, 255)); switch (component) { + case ColorComponent.Alpha: + rgbColor = new Color(componentValue, baseRgbColor.R, baseRgbColor.G, baseRgbColor.B); + break; case ColorComponent.Component1: - rgbColor = new Color(rgbColor.A, componentValue, rgbColor.G, rgbColor.B); + rgbColor = new Color(baseRgbColor.A, componentValue, baseRgbColor.G, baseRgbColor.B); break; case ColorComponent.Component2: - rgbColor = new Color(rgbColor.A, rgbColor.R, componentValue, rgbColor.B); + rgbColor = new Color(baseRgbColor.A, baseRgbColor.R, componentValue, baseRgbColor.B); break; case ColorComponent.Component3: - rgbColor = new Color(rgbColor.A, rgbColor.R, rgbColor.G, componentValue); + rgbColor = new Color(baseRgbColor.A, baseRgbColor.R, baseRgbColor.G, componentValue); break; } - selectedRgbColor = rgbColor; + return (rgbColor, rgbColor.ToHsv()); } - - //var converter = new ContrastBrushConverter(); - //this.Foreground = converter.Convert(selectedRgbColor, typeof(Brush), this.DefaultForeground, null) as Brush; - - return; } /// - /// Generates a new background image for the color slider and applies it. + /// Gets the actual background color displayed for the given HSV color. + /// This can differ due to the effects of certain properties intended to improve perception. /// - private async void UpdateBackground(HsvColor color) + /// The actual color to get the equivalent background color for. + /// The equivalent, perceived background color. + private HsvColor GetEquivalentBackgroundColor(HsvColor hsvColor) { - // Updates may be requested when sliders are not in the visual tree. - // For first-time load this is handled by the Loaded event. - // However, after that problems may arise, consider the following case: - // - // (1) Backgrounds are drawn normally the first time on Loaded. - // Actual height/width are available. - // (2) The palette tab is selected which has no sliders - // (3) The picker flyout is closed - // (4) Externally the color is changed - // The color change will trigger slider background updates but - // with the flyout closed, actual height/width are zero. - // No zero size bitmap can be generated. - // (5) The picker flyout is re-opened by the user and the default - // last-opened tab will be viewed: palette. - // No loaded events will be fired for sliders. The color change - // event was already handled in (4). The sliders will never - // be updated. - // - // In this case the sliders become out of sync with the Color because there is no way - // to tell when they actually come into view. To work around this, force a re-render of - // the background with the last size of the slider. This last size will be when it was - // last loaded or updated. - // - // In the future additional consideration may be required for SizeChanged of the control. - // This work-around will also cause issues if display scaling changes in the special - // case where cached sizes are required. - - var width = Convert.ToInt32(Bounds.Width); - var height = Convert.ToInt32(Bounds.Height); - - if (width == 0 || height == 0) + var component = ColorComponent; + var isAlphaMaxForced = IsAlphaMaxForced; + var isSaturationValueMaxForced = IsSaturationValueMaxForced; + + if (isAlphaMaxForced && + component != ColorComponent.Alpha) { - // Attempt to use the last size if it was available - if (cachedSize.IsDefault == false) - { - width = Convert.ToInt32(cachedSize.Width); - height = Convert.ToInt32(cachedSize.Height); - } + hsvColor = new HsvColor(1.0, hsvColor.H, hsvColor.S, hsvColor.V); } - else + + switch (component) { - cachedSize = new Size(width, height); + case ColorComponent.Component1: + return new HsvColor( + hsvColor.A, + hsvColor.H, + isSaturationValueMaxForced ? 1.0 : hsvColor.S, + isSaturationValueMaxForced ? 1.0 : hsvColor.V); + case ColorComponent.Component2: + return new HsvColor( + hsvColor.A, + hsvColor.H, + hsvColor.S, + isSaturationValueMaxForced ? 1.0 : hsvColor.V); + case ColorComponent.Component3: + return new HsvColor( + hsvColor.A, + hsvColor.H, + isSaturationValueMaxForced ? 1.0 : hsvColor.S, + hsvColor.V); + default: + return hsvColor; } + } + + /// + /// Gets the actual background color displayed for the given RGB color. + /// This can differ due to the effects of certain properties intended to improve perception. + /// + /// The actual color to get the equivalent background color for. + /// The equivalent, perceived background color. + private Color GetEquivalentBackgroundColor(Color rgbColor) + { + var component = ColorComponent; + var isAlphaMaxForced = IsAlphaMaxForced; - var bitmap = await ColorHelpers.CreateComponentBitmapAsync( - width, - height, - Orientation, - ColorModel, - ColorComponent, - color, - IsAlphaMaxForced, - IsSaturationValueMaxForced); - - if (bitmap != null) + if (isAlphaMaxForced && + component != ColorComponent.Alpha) { - Background = ColorHelpers.BitmapToBrushAsync(bitmap, width, height); + rgbColor = new Color(255, rgbColor.R, rgbColor.G, rgbColor.B); } - return; + return rgbColor; } /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - bool update = false; + if (disableUpdates) + { + base.OnPropertyChanged(change); + return; + } + // Always keep the two color properties in sync if (change.Property == ColorProperty) { - // Sync with HSV (which is primary) + disableUpdates = true; + HsvColor = Color.ToHsv(); - update = true; + + if (IsAutoUpdatingEnabled) + { + SetColorToSliderValues(); + UpdateBackground(); + } + + disableUpdates = false; } else if (change.Property == HsvColorProperty) { - update = true; + disableUpdates = true; + + Color = HsvColor.ToRgb(); + + if (IsAutoUpdatingEnabled) + { + SetColorToSliderValues(); + UpdateBackground(); + } + + disableUpdates = false; } else if (change.Property == BoundsProperty) { - update = true; + if (IsAutoUpdatingEnabled) + { + UpdateBackground(); + } } - - if (update && IsAutoUpdatingEnabled) + else if (change.Property == ValueProperty || + change.Property == MinimumProperty || + change.Property == MaximumProperty) { - UpdateColors(); + disableUpdates = true; + + (var color, var hsvColor) = GetColorFromSliderValues(); + + if (ColorModel == ColorModel.Hsva) + { + HsvColor = hsvColor; + Color = hsvColor.ToRgb(); + } + else + { + Color = color; + HsvColor = color.ToHsv(); + } + + disableUpdates = false; } base.OnPropertyChanged(change); diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs index 37c6f552d6..6500d10fe9 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs @@ -719,7 +719,7 @@ namespace Avalonia.Controls.Primitives var brush = new ImageBrush(bitmap) { - Stretch = Stretch.Fill + Stretch = Stretch.None }; return brush; From aae4b6708d15343f5f962e0c7a7c63d626e44064 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 21:57:55 -0400 Subject: [PATCH 501/820] Remove Color property from ColorPreviewer --- .../ColorPreviewer.Properties.cs | 27 +++---------------- .../ColorPreviewer/ColorPreviewer.cs | 18 +------------ 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs index 74c0943919..903b5fb52b 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -5,27 +5,6 @@ namespace Avalonia.Controls.Primitives /// public partial class ColorPreviewer { - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorProperty = - AvaloniaProperty.Register( - nameof(Color), - Colors.White); - - /// - /// Gets or sets the currently previewed color in the RGB color model. - /// - /// - /// For control authors use instead to avoid loss - /// of precision and color drifting. - /// - public Color Color - { - get => GetValue(ColorProperty); - set => SetValue(ColorProperty, value); - } - /// /// Defines the property. /// @@ -38,9 +17,9 @@ namespace Avalonia.Controls.Primitives /// Gets or sets the currently previewed color in the HSV color model. /// /// - /// This should be used in all cases instead of the property. - /// Internally, the uses the HSV color model and using - /// this property will avoid loss of precision and color drifting. + /// Only an HSV color is supported in this control to ensure there is never any + /// loss of precision or color information. Accent colors, like the color spectrum, + /// only operate with the HSV color model. /// public HsvColor HsvColor { diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs index 35bd62601f..1c0dd2154a 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives { /// /// Event for when the selected color changes within the previewer. - /// This happens when an accent color is pressed. + /// This occurs when an accent color is pressed. /// public event EventHandler? ColorChanged; @@ -82,22 +82,6 @@ namespace Avalonia.Controls.Primitives base.OnApplyTemplate(e); } - /// - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - // Always keep the two color properties in sync - if (change.Property == ColorProperty) - { - HsvColor = Color.ToHsv(); - } - else if (change.Property == HsvColorProperty) - { - Color = HsvColor.ToRgb(); - } - - base.OnPropertyChanged(change); - } - /// /// Called before the event occurs. /// From 357eddf5e933f4797e21d2235e19936c66878b21 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 22:52:00 -0400 Subject: [PATCH 502/820] Implement ColorSlider PseudoClasses --- .../ColorSlider/ColorSlider.cs | 66 ++++++++++++++++--- .../Themes/Fluent/ColorSlider.xaml | 20 ++++-- .../Themes/Fluent/ColorSpectrum.xaml | 3 + 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 61df36c806..9d9e9f393e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Controls.Metadata; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Utilities; @@ -8,8 +9,13 @@ namespace Avalonia.Controls.Primitives /// /// A slider with a background that represents a single color component. /// + [PseudoClasses(pcDarkSelector, pcLightSelector)] public partial class ColorSlider : Slider { + protected const string pcDarkSelector = ":dark-selector"; + protected const string pcLightSelector = ":light-selector"; + + private const double MaxHue = 359.99999999999999999; private bool disableUpdates = false; /// @@ -19,6 +25,49 @@ namespace Avalonia.Controls.Primitives { } + /// + /// Updates the visual state of the control by applying latest PseudoClasses. + /// + private void UpdatePseudoClasses() + { + // The slider itself can be transparent for certain color values. + // This causes an issue where a white selector thumb over a light window background or + // a black selector thumb over a dark window background is not visible. + // This means under a certain alpha threshold, neither a white or black selector thumb + // should be shown and instead the default slider thumb color should be used instead. + if (Color.A < 128 && + (IsAlphaMaxForced == false || + ColorComponent == ColorComponent.Alpha)) + { + PseudoClasses.Set(pcDarkSelector, false); + PseudoClasses.Set(pcLightSelector, false); + } + else + { + Color perceivedColor; + + if (ColorModel == ColorModel.Hsva) + { + perceivedColor = GetEquivalentBackgroundColor(HsvColor).ToRgb(); + } + else + { + perceivedColor = GetEquivalentBackgroundColor(Color); + } + + if (ColorHelpers.GetRelativeLuminance(perceivedColor) <= 0.5) + { + PseudoClasses.Set(pcDarkSelector, false); + PseudoClasses.Set(pcLightSelector, true); + } + else + { + PseudoClasses.Set(pcDarkSelector, true); + PseudoClasses.Set(pcLightSelector, false); + } + } + } + /// /// Generates a new background image for the color slider and applies it. /// @@ -80,7 +129,7 @@ namespace Avalonia.Controls.Primitives break; case ColorComponent.Component1: // Hue Minimum = 0; - Maximum = 359; + Maximum = MaxHue; Value = hsvColor.H; break; case ColorComponent.Component2: // Saturation @@ -144,26 +193,22 @@ namespace Avalonia.Controls.Primitives { case ColorComponent.Alpha: { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - hsvColor = new HsvColor(componentValue, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); + hsvColor = new HsvColor(sliderPercent, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); break; } case ColorComponent.Component1: { - var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); - hsvColor = new HsvColor(baseHsvColor.A, componentValue, baseHsvColor.S, baseHsvColor.V); + hsvColor = new HsvColor(baseHsvColor.A, sliderPercent * MaxHue, baseHsvColor.S, baseHsvColor.V); break; } case ColorComponent.Component2: { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, componentValue, baseHsvColor.V); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, sliderPercent, baseHsvColor.V); break; } case ColorComponent.Component3: { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, componentValue); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, sliderPercent); break; } } @@ -279,6 +324,7 @@ namespace Avalonia.Controls.Primitives UpdateBackground(); } + UpdatePseudoClasses(); disableUpdates = false; } else if (change.Property == HsvColorProperty) @@ -293,6 +339,7 @@ namespace Avalonia.Controls.Primitives UpdateBackground(); } + UpdatePseudoClasses(); disableUpdates = false; } else if (change.Property == BoundsProperty) @@ -321,6 +368,7 @@ namespace Avalonia.Controls.Primitives HsvColor = color.ToHsv(); } + UpdatePseudoClasses(); disableUpdates = false; } diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml index 1ca9b12ffe..620e9f658d 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -159,14 +159,26 @@ - + + + + + - - + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml index b209fe75b3..8e5139975a 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml @@ -118,6 +118,9 @@ + From 005907a93bf5f594694007de8b1fd91fda0cc466 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 27 Apr 2022 16:53:18 +0200 Subject: [PATCH 503/820] More hit testing fixes for embedded content runs --- src/Avalonia.Base/Media/GlyphRun.cs | 61 ++++++++++--------- .../Media/TextFormatting/TextFormatterImpl.cs | 24 ++++---- .../Media/TextFormatting/TextLineImpl.cs | 44 +++++++------ src/Skia/Avalonia.Skia/TextShaperImpl.cs | 3 +- .../Media/TextFormatting/TextLineTests.cs | 21 +++++-- 5 files changed, 81 insertions(+), 72 deletions(-) diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 9a2645f03d..22be8d8865 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -28,6 +28,8 @@ namespace Avalonia.Media private IReadOnlyList? _glyphOffsets; private IReadOnlyList? _glyphClusters; + private int _offsetToFirstCharacter; + /// /// Initializes a new instance of the class by specifying properties of the class. /// @@ -203,7 +205,7 @@ namespace Avalonia.Media /// public double GetDistanceFromCharacterHit(CharacterHit characterHit) { - var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength - _offsetToFirstCharacter; var distance = 0.0; @@ -552,30 +554,20 @@ namespace Avalonia.Media } nextCluster = GlyphClusters[currentIndex]; - } - - if (nextCluster < Characters.Start) - { - nextCluster = Characters.Start; - } - - if (cluster < Characters.Start) - { - cluster = Characters.Start; - } + } int trailingLength; if (nextCluster == cluster) { - trailingLength = Characters.Start + Characters.Length - cluster; + trailingLength = Characters.Start + Characters.Length - _offsetToFirstCharacter - cluster; } else { trailingLength = nextCluster - cluster; } - return new CharacterHit(cluster, trailingLength); + return new CharacterHit(_offsetToFirstCharacter + cluster, trailingLength); } /// @@ -609,6 +601,13 @@ namespace Avalonia.Media private GlyphRunMetrics CreateGlyphRunMetrics() { + if (GlyphClusters != null && GlyphClusters.Count > 0) + { + var firstCluster = GlyphClusters[0]; + + _offsetToFirstCharacter = Math.Max(0, Characters.Start - firstCluster); + } + var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; var widthIncludingTrailingWhitespace = 0d; @@ -680,34 +679,40 @@ namespace Avalonia.Media { for (var i = GlyphClusters.Count - 1; i >= 0; i--) { - var cluster = GlyphClusters[i]; - - var codepointIndex = IsLeftToRight ? cluster - _characters.Start : _characters.End - cluster; + var currentCluster = GlyphClusters[i]; + var characterIndex = Math.Max(0, currentCluster - _characters.BufferOffset); + var codepoint = Codepoint.ReadAt(_characters, characterIndex, out _); - if (codepointIndex < 0) + if (!codepoint.IsWhiteSpace) { - trailingWhitespaceLength = _characters.Length; - - glyphCount = GlyphClusters.Count; - break; } - var codepoint = Codepoint.ReadAt(_characters, codepointIndex, out _); + var clusterLength = 1; - if (!codepoint.IsWhiteSpace) + while(i - 1 >= 0) { + var nextCluster = GlyphClusters[i - 1]; + + if(currentCluster == nextCluster) + { + clusterLength++; + i--; + + continue; + } + break; } if (codepoint.IsBreakChar) { - newLineLength++; + newLineLength += clusterLength; } - trailingWhitespaceLength++; - - glyphCount++; + trailingWhitespaceLength += clusterLength; + + glyphCount++; } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 67fba00ee8..7f0f204886 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -79,14 +79,14 @@ namespace Avalonia.Media.TextFormatting { var currentRun = textRuns[i]; - if (currentLength + currentRun.Text.Length < length) + if (currentLength + currentRun.TextSourceLength < length) { currentLength += currentRun.TextSourceLength; continue; } - var firstCount = currentRun.Text.Length >= 1 ? i + 1 : i; + var firstCount = currentRun.TextSourceLength >= 1 ? i + 1 : i; var first = new List(firstCount); @@ -100,13 +100,13 @@ namespace Avalonia.Media.TextFormatting var secondCount = textRuns.Count - firstCount; - if (currentLength + currentRun.Text.Length == length) + if (currentLength + currentRun.TextSourceLength == length) { var second = secondCount > 0 ? new List(secondCount) : null; if (second != null) { - var offset = currentRun.Text.Length >= 1 ? 1 : 0; + var offset = currentRun.TextSourceLength >= 1 ? 1 : 0; for (var j = 0; j < secondCount; j++) { @@ -124,16 +124,14 @@ namespace Avalonia.Media.TextFormatting var second = new List(secondCount); - if (currentRun is not ShapedTextCharacters shapedTextCharacters) + if (currentRun is ShapedTextCharacters shapedTextCharacters) { - throw new NotSupportedException("Only shaped runs can be split in between."); - } - - var split = shapedTextCharacters.Split(length - currentLength); + var split = shapedTextCharacters.Split(length - currentLength); - first.Add(split.First); + first.Add(split.First); - second.Add(split.Second!); + second.Add(split.Second!); + } for (var j = 1; j < secondCount; j++) { @@ -483,7 +481,7 @@ namespace Avalonia.Media.TextFormatting { case ShapedTextCharacters shapedTextCharacters: { - var firstCluster = shapedTextCharacters.Text.Start; + var firstCluster = shapedTextCharacters.ShapedBuffer.GlyphClusters[0]; var lastCluster = firstCluster; for (var i = 0; i < shapedTextCharacters.ShapedBuffer.Length; i++) @@ -492,7 +490,7 @@ namespace Avalonia.Media.TextFormatting if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth) { - measuredLength += Math.Max(0, lastCluster - firstCluster + 1); + measuredLength += Math.Max(0, lastCluster - firstCluster); goto found; } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index b480774d1d..6a704f6f3e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -404,7 +404,7 @@ namespace Avalonia.Media.TextFormatting var result = new List(TextRuns.Count); var lastDirection = _flowDirection; var currentDirection = lastDirection; - var currentPosition = 0; + var currentPosition = FirstTextSourceIndex; var currentRect = Rect.Empty; var startX = Start; @@ -418,6 +418,11 @@ namespace Avalonia.Media.TextFormatting continue; } + if(currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex) + { + continue; + } + TextRun? nextRun = null; if (index + 1 < TextRuns.Count) @@ -1018,31 +1023,21 @@ namespace Avalonia.Media.TextFormatting private TextLineMetrics CreateLineMetrics() { - var start = 0d; - var height = 0d; + var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; + var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; + var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; + var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; var newLineLength = 0; - var ascent = 0d; - var descent = 0d; - var lineGap = 0d; - var fontRenderingEmSize = 0d; + var ascent = glyphTypeface.Ascent * scale; + var descent = glyphTypeface.Descent * scale; + var lineGap = glyphTypeface.LineGap * scale; - var lineHeight = _paragraphProperties.LineHeight; - - if (_textRuns.Count == 0) - { - var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; - fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; - var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; - ascent = glyphTypeface.Ascent * scale; - height = double.IsNaN(lineHeight) || MathUtilities.IsZero(lineHeight) ? - descent - ascent + lineGap : - lineHeight; - - return new TextLineMetrics(false, height, 0, start, -ascent, 0, 0, 0); - } + var height = descent - ascent + lineGap; + + var lineHeight = _paragraphProperties.LineHeight; for (var index = 0; index < _textRuns.Count; index++) { @@ -1166,12 +1161,15 @@ namespace Avalonia.Media.TextFormatting } } - start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, + var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, _paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection); if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) { - height = lineHeight; + if(lineHeight > height) + { + height = lineHeight; + } } return new TextLineMetrics(widthIncludingWhitespace > _paragraphWidth, height, newLineLength, start, diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index a0890262e7..ebaa247da8 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; @@ -59,7 +58,7 @@ namespace Avalonia.Skia var glyphIndex = (ushort)sourceInfo.Codepoint; - var glyphCluster = (int)sourceInfo.Cluster; + var glyphCluster = (int)(sourceInfo.Cluster); var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index e3b9e5a8b1..a47638d2ec 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -665,17 +665,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var text = "0123".AsMemory(); var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); - var shapedBuffer = TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption); - var firstRun = new ShapedTextCharacters(shapedBuffer, defaultProperties); + var firstRun = new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, 1, text.Length), shaperOption), defaultProperties); var textRuns = new List { new CustomDrawableRun(), firstRun, new CustomDrawableRun(), - new ShapedTextCharacters(shapedBuffer, defaultProperties), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length + 2, text.Length), shaperOption), defaultProperties), new CustomDrawableRun(), - new ShapedTextCharacters(shapedBuffer, defaultProperties) + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2 + 3, text.Length), shaperOption), defaultProperties) }; var textSource = new FixedRunsTextSource(textRuns); @@ -691,15 +690,25 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, textBounds.Count); Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); - textBounds = textLine.GetTextBounds(0, firstRun.Text.Length); + textBounds = textLine.GetTextBounds(0, 1); Assert.Equal(1, textBounds.Count); - Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + Assert.Equal(14, textBounds[0].Rectangle.Width); textBounds = textLine.GetTextBounds(0, firstRun.Text.Length + 1); Assert.Equal(1, textBounds.Count); Assert.Equal(firstRun.Size.Width + 14, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(1, firstRun.Text.Length); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(1, firstRun.Text.Length + 1); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width + 14, textBounds[0].Rectangle.Width); } } From a34e0f50e2367a45da815ea8dd36b0de4dca167c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 27 Apr 2022 17:55:00 +0200 Subject: [PATCH 504/820] Bump --- .../Media/TextFormatting/TextLayoutTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs index 6ed4ba0d4a..b668f4d39e 100644 --- a/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs @@ -1,11 +1,8 @@ using Avalonia.Media; -using Avalonia.Platform; using System; -using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Media.TextFormatting; From 25a34efd9a01cfb86698038cc54c4292ec1947ff Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:08:33 -0400 Subject: [PATCH 505/820] Implement color display name in ColorSpectrum --- .../ColorPreviewer.Properties.cs | 2 +- .../ColorSpectrum/ColorSpectrum.cs | 60 +++++---- .../Helpers/ColorHelpers.cs | 24 +--- .../Helpers/ColorNameHelpers.cs | 116 ++++++++++++++++++ .../Themes/Default/ColorSpectrum.xaml | 16 +-- .../Themes/Fluent/ColorSpectrum.xaml | 16 +-- 6 files changed, 174 insertions(+), 60 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs index 903b5fb52b..f90f02551d 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -41,7 +41,7 @@ namespace Avalonia.Controls.Primitives /// public bool ShowAccentColors { - get => (bool)this.GetValue(ShowAccentColorsProperty); + get => GetValue(ShowAccentColorsProperty); set => SetValue(ShowAccentColorsProperty, value); } } diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index fe9a2fac43..9b2459265d 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -20,7 +20,6 @@ namespace Avalonia.Controls.Primitives /// /// A two dimensional spectrum for color selection. /// - [TemplatePart("PART_ColorNameToolTip", typeof(ToolTip))] [TemplatePart("PART_InputTarget", typeof(Canvas))] [TemplatePart("PART_LayoutRoot", typeof(Panel))] [TemplatePart("PART_SelectionEllipsePanel", typeof(Panel))] @@ -60,7 +59,6 @@ namespace Avalonia.Controls.Primitives private Ellipse? _spectrumOverlayEllipse; private Canvas? _inputTarget; private Panel? _selectionEllipsePanel; - private ToolTip? _colorNameToolTip; // Put the spectrum images in a bitmap, which is then given to an ImageBrush. private WriteableBitmap? _hueRedBitmap; @@ -117,7 +115,6 @@ namespace Avalonia.Controls.Primitives UnregisterEvents(); // Failsafe - _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); _inputTarget = e.NameScope.Find("PART_InputTarget"); _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); @@ -152,10 +149,10 @@ namespace Avalonia.Controls.Primitives }); } - if (ColorHelpers.ToDisplayNameExists && - _colorNameToolTip != null) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + ToolTip.SetTip(_selectionEllipsePanel, ColorNameHelpers.ToDisplayName(Color)); } // If we haven't yet created our bitmaps, do so now. @@ -338,26 +335,45 @@ namespace Avalonia.Controls.Primitives protected override void OnGotFocus(GotFocusEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. - if (_colorNameToolTip != null && - ColorHelpers.ToDisplayNameExists) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - ToolTip.SetIsOpen(_colorNameToolTip, true); + ToolTip.SetIsOpen(_selectionEllipsePanel, true); } UpdatePseudoClasses(); + + base.OnGotFocus(e); } /// protected override void OnLostFocus(RoutedEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. - if (_colorNameToolTip != null && - ColorHelpers.ToDisplayNameExists) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) + { + ToolTip.SetIsOpen(_selectionEllipsePanel, false); + } + + UpdatePseudoClasses(); + + base.OnLostFocus(e); + } + + /// + protected override void OnPointerLeave(PointerEventArgs e) + { + // We only want to bother with the color name tool tip if we can provide color names. + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - ToolTip.SetIsOpen(_colorNameToolTip, false); + ToolTip.SetIsOpen(_selectionEllipsePanel, false); } UpdatePseudoClasses(); + + base.OnPointerLeave(e); } /// @@ -516,12 +532,10 @@ namespace Avalonia.Controls.Primitives var colorChangedEventArgs = new ColorChangedEventArgs(_oldColor, newColor); ColorChanged?.Invoke(this, colorChangedEventArgs); - if (ColorHelpers.ToDisplayNameExists) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - if (_colorNameToolTip != null) - { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(newColor); - } + ToolTip.SetTip(_selectionEllipsePanel, ColorNameHelpers.ToDisplayName(Color)); } } } @@ -811,15 +825,11 @@ namespace Avalonia.Controls.Primitives Canvas.SetTop(_selectionEllipsePanel, yPosition - (_selectionEllipsePanel.Height / 2)); // We only want to bother with the color name tool tip if we can provide color names. - if (ColorHelpers.ToDisplayNameExists) + if (IsFocused && + _selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - if (_colorNameToolTip != null) - { - // ToolTip doesn't currently provide any way to re-run its placement logic if its placement target moves, - // so toggling IsEnabled induces it to do that without incurring any visual glitches. - _colorNameToolTip.IsEnabled = false; - _colorNameToolTip.IsEnabled = true; - } + ToolTip.SetIsOpen(_selectionEllipsePanel, true); } UpdatePseudoClasses(); diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs index 6500d10fe9..36ee478c7b 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs @@ -17,18 +17,6 @@ namespace Avalonia.Controls.Primitives { internal static class ColorHelpers { - public const int CheckerSize = 4; - - public static bool ToDisplayNameExists - { - get => false; - } - - public static string ToDisplayName(Color color) - { - return string.Empty; - } - /// /// Generates a new bitmap of the specified size by changing a specific color component. /// This will produce a gradient representing a sweep of all possible values of the color component. @@ -325,7 +313,7 @@ namespace Avalonia.Controls.Primitives { Hsv newHsv = originalHsv; - if (amount == IncrementAmount.Small || !ToDisplayNameExists) + if (amount == IncrementAmount.Small || !ColorNameHelpers.ToDisplayNameExists) { // In order to avoid working with small values that can incur rounding issues, // we'll multiple saturation and value by 100 to put them in the range of 0-100 instead of 0-1. @@ -416,7 +404,7 @@ namespace Avalonia.Controls.Primitives // in the middle of that color's bounds. Hsv newHsv = originalHsv; - string originalColorName = ColorHelpers.ToDisplayName(originalHsv.ToRgb().ToColor()); + string originalColorName = ColorNameHelpers.ToDisplayName(originalHsv.ToRgb().ToColor()); string newColorName = originalColorName; // Note: *newValue replaced with ref local variable for C#, must be initialized @@ -471,7 +459,7 @@ namespace Avalonia.Controls.Primitives { newValue = maxBound; shouldFindMidPoint = false; - newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + newColorName = ColorNameHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); break; } } @@ -486,7 +474,7 @@ namespace Avalonia.Controls.Primitives { newValue = minBound; shouldFindMidPoint = false; - newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + newColorName = ColorNameHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); break; } } @@ -501,7 +489,7 @@ namespace Avalonia.Controls.Primitives break; } - newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + newColorName = ColorNameHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); } if (shouldFindMidPoint) @@ -574,7 +562,7 @@ namespace Avalonia.Controls.Primitives } } - currentColorName = ColorHelpers.ToDisplayName(currentHsv.ToRgb().ToColor()); + currentColorName = ColorNameHelpers.ToDisplayName(currentHsv.ToRgb().ToColor()); } newValue = (startValue + currentValue + startEndOffset) / 2; diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs new file mode 100644 index 0000000000..303a52f00b --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs @@ -0,0 +1,116 @@ +using System; +using System.Globalization; +using System.Collections.Generic; +using Avalonia.Media; +using System.Text; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Contains helpers useful when working with color names. + /// + public static class ColorNameHelpers + { + private static readonly Dictionary cachedDisplayNames = new Dictionary(); + private static readonly object cacheMutex = new object(); + + /// + /// Determines if color display names are supported based on the current thread culture. + /// + /// + /// Only English names are currently supported following known color names. + /// In the future known color names could be localized. + /// + public static bool ToDisplayNameExists + { + get => CultureInfo.CurrentUICulture.Name.StartsWith("EN", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Determines an approximate display name for the given color. + /// + /// The color to get the display name for. + /// The approximate color display name. + public static string ToDisplayName(Color color) + { + // Without rounding, there are 16,777,216 possible RGB colors (without alpha). + // This is too many to cache and search through for performance reasons. + // It is also needlessly large as there are only ~140 known/named colors. + // Therefore, rounding of the input color's component values is done to + // reduce the color space into something more useful. + double rounding = 5; + var roundedColor = new Color( + 0xFF, + Convert.ToByte(Math.Round(color.R / rounding) * rounding), + Convert.ToByte(Math.Round(color.G / rounding) * rounding), + Convert.ToByte(Math.Round(color.B / rounding) * rounding)); + + // Attempt to use a previously cached display name + lock (cacheMutex) + { + if (cachedDisplayNames.TryGetValue(roundedColor, out var displayName)) + { + return displayName; + } + } + + // Find the closest known color by measuring 3D Euclidean distance (ignore alpha) + var closestKnownColor = KnownColor.None; + var closestKnownColorDistance = double.PositiveInfinity; + var knownColors = (KnownColor[])Enum.GetValues(typeof(KnownColor)); + + for (int i = 1; i < knownColors.Length; i++) // Skip 'None' + { + // Transparent is skipped since alpha is ignored making it equivalent to White + if (knownColors[i] != KnownColor.Transparent) + { + Color knownColor = KnownColors.ToColor(knownColors[i]); + + double distance = Math.Sqrt( + Math.Pow((double)(roundedColor.R - knownColor.R), 2.0) + + Math.Pow((double)(roundedColor.G - knownColor.G), 2.0) + + Math.Pow((double)(roundedColor.B - knownColor.B), 2.0)); + + if (distance < closestKnownColorDistance) + { + closestKnownColor = knownColors[i]; + closestKnownColorDistance = distance; + } + } + } + + // Return the closest known color as the display name + // Cache results for next time as well + if (closestKnownColor != KnownColor.None) + { + StringBuilder sb = new StringBuilder(); + string name = closestKnownColor.ToString(); + + // Add spaces converting PascalCase to human-readable names + for (int i = 0; i < name.Length; i++) + { + if (i != 0 && + char.IsUpper(name[i])) + { + sb.Append(' '); + } + + sb.Append(name[i]); + } + + string displayName = sb.ToString(); + + lock (cacheMutex) + { + cachedDisplayNames.Add(roundedColor, displayName); + } + + return displayName; + } + else + { + return string.Empty; + } + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml index 9596ca9653..78e6da8aa3 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml @@ -48,7 +48,10 @@ Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + + - - - - + VerticalAlignment="Stretch" /> + + + - + + - - - - + VerticalAlignment="Stretch" /> + + + Date: Wed, 27 Apr 2022 22:37:36 -0400 Subject: [PATCH 506/820] Follow Avalonia convention --- .../ColorPreviewer/ColorPreviewer.cs | 6 ------ .../ColorSlider/ColorSlider.cs | 4 ---- .../ColorSpectrum/ColorSpectrum.cs | 2 -- 3 files changed, 12 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs index 1c0dd2154a..35072d6a42 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -61,8 +61,6 @@ namespace Avalonia.Controls.Primitives eventsConnected = false; } - - return; } /// @@ -92,8 +90,6 @@ namespace Avalonia.Controls.Primitives HsvColor = newColor; ColorChanged?.Invoke(this, new ColorChangedEventArgs(oldColor.ToRgb(), newColor.ToRgb())); - - return; } /// @@ -115,8 +111,6 @@ namespace Avalonia.Controls.Primitives HsvColor newHsvColor = AccentColorConverter.GetAccent(hsvColor, accentStep); OnColorChanged(newHsvColor); - - return; } } } diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 9d9e9f393e..c73f2b1cea 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -100,8 +100,6 @@ namespace Avalonia.Controls.Primitives Background = ColorHelpers.BitmapToBrushAsync(bitmap, pixelWidth, pixelHeight); } } - - return; } /// @@ -170,8 +168,6 @@ namespace Avalonia.Controls.Primitives break; } } - - return; } /// diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 9b2459265d..7b68068d46 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -327,8 +327,6 @@ namespace Avalonia.Controls.Primitives maxBound)); e.Handled = true; - - return; } /// From d9ef01acfcd9d7d504558ca7ac92941351a94cb3 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:40:26 -0400 Subject: [PATCH 507/820] Render the ColorSpectrum to physical device pixel resolution --- .../ColorSpectrum/ColorSpectrum.cs | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 7b68068d46..4cabb5c5b7 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -10,6 +10,7 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Shapes; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; @@ -587,8 +588,10 @@ namespace Avalonia.Controls.Primitives return; } - double xPosition = point.Position.X; - double yPosition = point.Position.Y; + // Remember the bitmap size follows physical device pixels + var scale = LayoutHelper.GetLayoutScale(this); + double xPosition = point.Position.X * scale; + double yPosition = point.Position.Y * scale; double radius = Math.Min(_imageWidthFromLastBitmapCreation, _imageHeightFromLastBitmapCreation) / 2; double distanceFromRadius = Math.Sqrt(Math.Pow(xPosition - radius, 2) + Math.Pow(yPosition - radius, 2)); @@ -819,8 +822,10 @@ namespace Avalonia.Controls.Primitives yPosition = (Math.Sin((thetaValue * Math.PI / 180.0) + Math.PI) * radius * rValue) + radius; } - Canvas.SetLeft(_selectionEllipsePanel, xPosition - (_selectionEllipsePanel.Width / 2)); - Canvas.SetTop(_selectionEllipsePanel, yPosition - (_selectionEllipsePanel.Height / 2)); + // Remember the bitmap size follows physical device pixels + var scale = LayoutHelper.GetLayoutScale(this); + Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2)); + Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2)); // We only want to bother with the color name tool tip if we can provide color names. if (IsFocused && @@ -969,7 +974,14 @@ namespace Avalonia.Controls.Primitives List bgraMaxPixelData = new List(); List newHsvValues = new List(); - var pixelCount = (int)(Math.Round(minDimension) * Math.Round(minDimension)); + // In Avalonia, Bounds returns the actual device-independent pixel size of a control. + // However, this is not necessarily the size of the control rendered on a display. + // A desktop or application scaling factor may be applied which must be accounted for here. + // Remember bitmaps in Avalonia are rendered mapping to actual device pixels, not the device- + // independent pixels of controls. + var scale = LayoutHelper.GetLayoutScale(this); + int pixelDimension = (int)Math.Round(minDimension * scale); + var pixelCount = pixelDimension * pixelDimension; var pixelDataSize = pixelCount * 4; bgraMinPixelData.Capacity = pixelDataSize; @@ -986,8 +998,6 @@ namespace Avalonia.Controls.Primitives bgraMaxPixelData.Capacity = pixelDataSize; newHsvValues.Capacity = pixelCount; - int minDimensionInt = (int)Math.Round(minDimension); - await Task.Run(() => { // As the user perceives it, every time the third dimension not represented in the ColorSpectrum changes, @@ -1006,12 +1016,12 @@ namespace Avalonia.Controls.Primitives // but the running time savings after that are *huge* when we can just set an opacity instead of generating a brand new bitmap. if (shape == ColorSpectrumShape.Box) { - for (int x = minDimensionInt - 1; x >= 0; --x) + for (int x = pixelDimension - 1; x >= 0; --x) { - for (int y = minDimensionInt - 1; y >= 0; --y) + for (int y = pixelDimension - 1; y >= 0; --y) { FillPixelForBox( - x, y, hsv, minDimensionInt, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, hsv, pixelDimension, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1019,12 +1029,12 @@ namespace Avalonia.Controls.Primitives } else { - for (int y = 0; y < minDimensionInt; ++y) + for (int y = 0; y < pixelDimension; ++y) { - for (int x = 0; x < minDimensionInt; ++x) + for (int x = 0; x < pixelDimension; ++x) { FillPixelForRing( - x, y, minDimensionInt / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, pixelDimension / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1034,8 +1044,8 @@ namespace Avalonia.Controls.Primitives Dispatcher.UIThread.Post(() => { - int pixelWidth = (int)Math.Round(minDimension); - int pixelHeight = (int)Math.Round(minDimension); + int pixelWidth = pixelDimension; + int pixelHeight = pixelDimension; ColorSpectrumComponents components2 = Components; @@ -1066,8 +1076,8 @@ namespace Avalonia.Controls.Primitives _shapeFromLastBitmapCreation = Shape; _componentsFromLastBitmapCreation = Components; - _imageWidthFromLastBitmapCreation = minDimension; - _imageHeightFromLastBitmapCreation = minDimension; + _imageWidthFromLastBitmapCreation = pixelDimension; + _imageHeightFromLastBitmapCreation = pixelDimension; _minHueFromLastBitmapCreation = MinHue; _maxHueFromLastBitmapCreation = MaxHue; _minSaturationFromLastBitmapCreation = MinSaturation; @@ -1086,7 +1096,7 @@ namespace Avalonia.Controls.Primitives double x, double y, Hsv baseHsv, - double minDimension, + int minDimension, ColorSpectrumComponents components, double minHue, double maxHue, From aef0d012258da94978d96ff38ce19af1c6554090 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:47:29 -0400 Subject: [PATCH 508/820] Reorder properties following Avalonia convention --- .../ColorPreviewer.Properties.cs | 16 +-- .../ColorSlider/ColorSlider.Properties.cs | 96 ++++++------- .../ColorSpectrum/ColorSpectrum.Properties.cs | 136 +++++++++--------- 3 files changed, 124 insertions(+), 124 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs index f90f02551d..c545f25298 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -13,6 +13,14 @@ namespace Avalonia.Controls.Primitives nameof(HsvColor), Colors.Transparent.ToHsv()); + /// + /// Defines the property. + /// + public static readonly StyledProperty ShowAccentColorsProperty = + AvaloniaProperty.Register( + nameof(ShowAccentColors), + true); + /// /// Gets or sets the currently previewed color in the HSV color model. /// @@ -27,14 +35,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(HsvColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ShowAccentColorsProperty = - AvaloniaProperty.Register( - nameof(ShowAccentColors), - true); - /// /// Gets or sets a value indicating whether accent colors are shown along /// with the preview color. diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs index 3aa3e3a789..12dce0b03e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs @@ -13,6 +13,54 @@ namespace Avalonia.Controls.Primitives nameof(Color), Colors.White); + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorComponentProperty = + AvaloniaProperty.Register( + nameof(ColorComponent), + ColorComponent.Component1); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorModelProperty = + AvaloniaProperty.Register( + nameof(ColorModel), + ColorModel.Rgba); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.White.ToHsv()); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAlphaMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsAlphaMaxForced), + true); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAutoUpdatingEnabledProperty = + AvaloniaProperty.Register( + nameof(IsAutoUpdatingEnabled), + true); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsSaturationValueMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsSaturationValueMaxForced), + true); + /// /// Gets or sets the currently selected color in the RGB color model. /// @@ -26,14 +74,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorComponentProperty = - AvaloniaProperty.Register( - nameof(ColorComponent), - ColorComponent.Component1); - /// /// Gets or sets the color component represented by the slider. /// @@ -43,14 +83,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorComponentProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorModelProperty = - AvaloniaProperty.Register( - nameof(ColorModel), - ColorModel.Rgba); - /// /// Gets or sets the active color model used by the slider. /// @@ -60,14 +92,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorModelProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty HsvColorProperty = - AvaloniaProperty.Register( - nameof(HsvColor), - Colors.White.ToHsv()); - /// /// Gets or sets the currently selected color in the HSV color model. /// @@ -81,14 +105,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(HsvColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty IsAlphaMaxForcedProperty = - AvaloniaProperty.Register( - nameof(IsAlphaMaxForced), - true); - /// /// Gets or sets a value indicating whether the alpha component is always forced to maximum for components /// other than . @@ -100,14 +116,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(IsAlphaMaxForcedProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty IsAutoUpdatingEnabledProperty = - AvaloniaProperty.Register( - nameof(IsAutoUpdatingEnabled), - true); - /// /// Gets or sets a value indicating whether automatic background and foreground updates will be /// calculated when the set color changes. @@ -121,14 +129,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(IsAutoUpdatingEnabledProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty IsSaturationValueMaxForcedProperty = - AvaloniaProperty.Register( - nameof(IsSaturationValueMaxForced), - true); - /// /// Gets or sets a value indicating whether the saturation and value components are always forced to maximum values /// when using the HSVA color model. Only component values other than will be changed. diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index ab5b83afcb..a1cb43a95a 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -10,6 +10,74 @@ namespace Avalonia.Controls.Primitives /// public partial class ColorSpectrum { + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Colors.White); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ComponentsProperty = + AvaloniaProperty.Register( + nameof(Components), + ColorSpectrumComponents.HueSaturation); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.White.ToHsv()); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxHueProperty = + AvaloniaProperty.Register(nameof(MaxHue), 359); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxSaturationProperty = + AvaloniaProperty.Register(nameof(MaxSaturation), 100); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxValueProperty = + AvaloniaProperty.Register(nameof(MaxValue), 100); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinHueProperty = + AvaloniaProperty.Register(nameof(MinHue), 0); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinSaturationProperty = + AvaloniaProperty.Register(nameof(MinSaturation), 0); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinValueProperty = + AvaloniaProperty.Register(nameof(MinValue), 0); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ShapeProperty = + AvaloniaProperty.Register( + nameof(Shape), + ColorSpectrumShape.Box); + /// /// Gets or sets the currently selected color in the RGB color model. /// @@ -23,14 +91,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorProperty = - AvaloniaProperty.Register( - nameof(Color), - Colors.White); - /// /// Gets or sets the two HSV color components displayed by the spectrum. /// @@ -43,14 +103,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ComponentsProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ComponentsProperty = - AvaloniaProperty.Register( - nameof(Components), - ColorSpectrumComponents.HueSaturation); - /// /// Gets or sets the currently selected color in the HSV color model. /// @@ -65,14 +117,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(HsvColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty HsvColorProperty = - AvaloniaProperty.Register( - nameof(HsvColor), - Colors.White.ToHsv()); - /// /// Gets or sets the maximum value of the Hue component in the range from 0..359. /// This property must be greater than . @@ -86,12 +130,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MaxHueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MaxHueProperty = - AvaloniaProperty.Register(nameof(MaxHue), 359); - /// /// Gets or sets the maximum value of the Saturation component in the range from 0..100. /// This property must be greater than . @@ -105,12 +143,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MaxSaturationProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MaxSaturationProperty = - AvaloniaProperty.Register(nameof(MaxSaturation), 100); - /// /// Gets or sets the maximum value of the Value component in the range from 0..100. /// This property must be greater than . @@ -124,12 +156,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MaxValueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MaxValueProperty = - AvaloniaProperty.Register(nameof(MaxValue), 100); - /// /// Gets or sets the minimum value of the Hue component in the range from 0..359. /// This property must be less than . @@ -143,12 +169,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MinHueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MinHueProperty = - AvaloniaProperty.Register(nameof(MinHue), 0); - /// /// Gets or sets the minimum value of the Saturation component in the range from 0..100. /// This property must be less than . @@ -162,12 +182,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MinSaturationProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MinSaturationProperty = - AvaloniaProperty.Register(nameof(MinSaturation), 0); - /// /// Gets or sets the minimum value of the Value component in the range from 0..100. /// This property must be less than . @@ -181,12 +195,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MinValueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MinValueProperty = - AvaloniaProperty.Register(nameof(MinValue), 0); - /// /// Gets or sets the displayed shape of the spectrum. /// @@ -195,13 +203,5 @@ namespace Avalonia.Controls.Primitives get => GetValue(ShapeProperty); set => SetValue(ShapeProperty, value); } - - /// - /// Defines the property. - /// - public static readonly StyledProperty ShapeProperty = - AvaloniaProperty.Register( - nameof(Shape), - ColorSpectrumShape.Box); } } From 21190ff39bba3fcb242cf3225247b36d74b3d082 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:53:16 -0400 Subject: [PATCH 509/820] Add :dark-selector PseudoClass to ColorSpectrum This standardizes with ColorSlider (which requires three states) but so far isn't needed in the templates. --- .../ColorSpectrum/ColorSpectrum.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 4cabb5c5b7..563fa24c08 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -29,10 +29,11 @@ namespace Avalonia.Controls.Primitives [TemplatePart("PART_SpectrumRectangle", typeof(Rectangle))] [TemplatePart("PART_SpectrumOverlayEllipse", typeof(Ellipse))] [TemplatePart("PART_SpectrumOverlayRectangle", typeof(Rectangle))] - [PseudoClasses(pcPressed, pcLargeSelector, pcLightSelector)] + [PseudoClasses(pcPressed, pcLargeSelector, pcDarkSelector, pcLightSelector)] public partial class ColorSpectrum : TemplatedControl { protected const string pcPressed = ":pressed"; + protected const string pcDarkSelector = ":dark-selector"; protected const string pcLargeSelector = ":large-selector"; protected const string pcLightSelector = ":light-selector"; @@ -556,7 +557,16 @@ namespace Avalonia.Controls.Primitives PseudoClasses.Set(pcLargeSelector, false); } - PseudoClasses.Set(pcLightSelector, SelectionEllipseShouldBeLight()); + if (SelectionEllipseShouldBeLight()) + { + PseudoClasses.Set(pcDarkSelector, false); + PseudoClasses.Set(pcLightSelector, true); + } + else + { + PseudoClasses.Set(pcDarkSelector, true); + PseudoClasses.Set(pcLightSelector, false); + } } private void UpdateColor(Hsv newHsv) From c3cdf856a3345f74413aed31febb61d4f214eb21 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:14:29 -0400 Subject: [PATCH 510/820] Add default themes for ColorSlider and ColorPreviewer --- .../Themes/Default.xaml | 21 ++ .../Themes/Default/ColorPreviewer.xaml | 90 ++++++++ .../Themes/Default/ColorSlider.xaml | 198 ++++++++++++++++++ .../Themes/Fluent/ColorSlider.xaml | 32 ++- 4 files changed, 332 insertions(+), 9 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 528eed9969..6d2f979f6e 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,7 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml new file mode 100644 index 0000000000..fe35dbd587 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml @@ -0,0 +1,90 @@ + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml new file mode 100644 index 0000000000..c0b78d628a --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml index 620e9f658d..54f58d2a8f 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -8,6 +8,20 @@ + + - - - - - - - From a86e0cc64e18787cae2ebb7b531966ee8b3ec0dd Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:19:02 -0400 Subject: [PATCH 511/820] Move ColorPicker theme definitions into theme folders --- samples/ControlCatalog/App.xaml.cs | 4 ++-- .../Themes/{ => Default}/Default.xaml | 0 .../Themes/{ => Fluent}/Fluent.xaml | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/Avalonia.Controls.ColorPicker/Themes/{ => Default}/Default.xaml (100%) rename src/Avalonia.Controls.ColorPicker/Themes/{ => Fluent}/Fluent.xaml (100%) diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 6539cdaee6..7ebb87094a 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -20,12 +20,12 @@ namespace ControlCatalog public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent.xaml") + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml") }; public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default.xaml") + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml") }; public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml similarity index 100% rename from src/Avalonia.Controls.ColorPicker/Themes/Default.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml similarity index 100% rename from src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml From e0bc2e35c50e96563e9dd0f24cc33160cf8765be Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:25:11 -0400 Subject: [PATCH 512/820] Add RgbComponent enum and support direct casting with all component enums --- .../ColorComponent.cs | 8 ++-- .../HsvComponent.cs | 22 +++++----- .../RgbComponent.cs | 42 +++++++++++++++++++ 3 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/RgbComponent.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorComponent.cs b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs index a0385c03b4..71725056cf 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs @@ -8,21 +8,21 @@ /// /// Represents the alpha component. /// - Alpha, + Alpha = 0, /// /// Represents the first color component which is Red when RGB or Hue when HSV. /// - Component1, + Component1 = 1, /// /// Represents the second color component which is Green when RGB or Saturation when HSV. /// - Component2, + Component2 = 2, /// /// Represents the third color component which is Blue when RGB or Value when HSV. /// - Component3 + Component3 = 3 } } diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs index 1132bd7bbb..1a7a13166a 100644 --- a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -12,13 +12,21 @@ namespace Avalonia.Controls /// public enum HsvComponent { + /// + /// The Alpha component. + /// + /// + /// Also see: + /// + Alpha = 0, + /// /// The Hue component. /// /// /// Also see: /// - Hue, + Hue = 1, /// /// The Saturation component. @@ -26,7 +34,7 @@ namespace Avalonia.Controls /// /// Also see: /// - Saturation, + Saturation = 2, /// /// The Value component. @@ -34,14 +42,6 @@ namespace Avalonia.Controls /// /// Also see: /// - Value, - - /// - /// The Alpha component. - /// - /// - /// Also see: - /// - Alpha + Value = 3 }; } diff --git a/src/Avalonia.Controls.ColorPicker/RgbComponent.cs b/src/Avalonia.Controls.ColorPicker/RgbComponent.cs new file mode 100644 index 0000000000..c3591573bb --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/RgbComponent.cs @@ -0,0 +1,42 @@ +using Avalonia.Media; + +namespace Avalonia.Controls +{ + /// + /// Defines a specific component in the RGB color model. + /// + public enum RgbComponent + { + /// + /// The Alpha component. + /// + /// + /// Also see: + /// + Alpha = 0, + + /// + /// The Red component. + /// + /// + /// Also see: + /// + Red = 1, + + /// + /// The Green component. + /// + /// + /// Also see: + /// + Green = 2, + + /// + /// The Blue component. + /// + /// + /// Also see: + /// + Blue = 3 + }; +} From c3ce137bda4483c51d2cca1b79c761ca326f6be8 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:27:05 -0400 Subject: [PATCH 513/820] Move AccentColorConverter in Converters directory --- .../{ColorPreviewer => Converters}/AccentColorConverter.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ColorPreviewer => Converters}/AccentColorConverter.cs (100%) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs rename to src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs From f550b8f9e82fa0f02490316a2e0d07840d73b0b4 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:30:22 -0400 Subject: [PATCH 514/820] Move AccentColorConverter in Avalonia.Controls.Primitives.Converters namespace This better hides these special-purpose converters --- .../ColorPreviewer/ColorPreviewer.cs | 1 + .../Converters/AccentColorConverter.cs | 2 +- .../Themes/Default/ColorPreviewer.xaml | 4 ++-- .../Themes/Fluent/ColorPreviewer.xaml | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs index 35072d6a42..3c429783d5 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives.Converters; using Avalonia.Input; using Avalonia.Media; diff --git a/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs index ad8f66251a..07ebc899db 100644 --- a/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs +++ b/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs @@ -3,7 +3,7 @@ using System.Globalization; using Avalonia.Data.Converters; using Avalonia.Media; -namespace Avalonia.Controls.Primitives +namespace Avalonia.Controls.Primitives.Converters { /// /// Creates an accent color for a given base color value and step parameter. diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml index fe35dbd587..9100bf0440 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml @@ -1,10 +1,10 @@  - + - - - - diff --git a/src/Avalonia.Themes.Default/IBitmapToImageConverter.cs b/src/Avalonia.Themes.Default/IBitmapToImageConverter.cs new file mode 100644 index 0000000000..9b7fcecf45 --- /dev/null +++ b/src/Avalonia.Themes.Default/IBitmapToImageConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media.Imaging; + +namespace Avalonia.Themes.Default +{ + internal class IBitmapToImageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value != null && value is IBitmap bm) + return new Image { Source=bm }; + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 35603fe216..ede0791438 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -10,6 +10,15 @@ + + + + + + + MSBuild:Compile + + diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 7860e08ef5..6251c86720 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -4,12 +4,13 @@ x:CompileBindings="True" Selector="NativeMenuBar"> - + + diff --git a/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs b/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs new file mode 100644 index 0000000000..34670882f8 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media.Imaging; + +namespace Avalonia.Themes.Fluent +{ + internal class IBitmapToImageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value != null && value is IBitmap bm) + return new Image { Source=bm }; + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} From 8772a46bbaab12d3d77b76fd97eed4cffd11e365 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 29 Apr 2022 14:26:28 +0200 Subject: [PATCH 533/820] Fix scaled text measurements --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 4 +++- src/Avalonia.Controls/TextBlock.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index db1bbdbc6c..c3912077ee 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -532,7 +532,9 @@ namespace Avalonia.Controls.Presenters return finalSize; } - _constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height)); + var textSize = PixelSize.FromSize(finalSize, 1); + + _constraint = new Size(textSize.Width, textSize.Height); _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c04a62008b..7427f21134 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -626,9 +626,9 @@ namespace Avalonia.Controls var padding = Padding; - var textSize = finalSize.Deflate(padding); + var textSize = PixelSize.FromSize(finalSize.Deflate(padding), 1); - _constraint = new Size(textSize.Width, Math.Ceiling(textSize.Height)); + _constraint = new Size(textSize.Width, textSize.Height); _textLayout = null; From 2425cbf7aa2504b96b6fe441ef9522de92bbc98b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 29 Apr 2022 17:03:05 +0200 Subject: [PATCH 534/820] Use RenderScaling in the ArrangeOverride --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- src/Avalonia.Controls/TextBlock.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index c3912077ee..eecf0d9101 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -532,7 +532,7 @@ namespace Avalonia.Controls.Presenters return finalSize; } - var textSize = PixelSize.FromSize(finalSize, 1); + var textSize = PixelSize.FromSize(finalSize, VisualRoot?.RenderScaling ?? 1); _constraint = new Size(textSize.Width, textSize.Height); diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 7427f21134..1087310841 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -626,7 +626,7 @@ namespace Avalonia.Controls var padding = Padding; - var textSize = PixelSize.FromSize(finalSize.Deflate(padding), 1); + var textSize = PixelSize.FromSize(finalSize.Deflate(padding), VisualRoot?.RenderScaling ?? 1); _constraint = new Size(textSize.Width, textSize.Height); From bf61dd9a1ed4237ac611c2d986b12a7e64f33216 Mon Sep 17 00:00:00 2001 From: Lubomir Tetak Date: Mon, 2 May 2022 10:17:00 +0200 Subject: [PATCH 535/820] macos - disable native menus completely --- native/Avalonia.Native/src/OSX/window.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index d16c466fe6..6adb177ae9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -2233,10 +2233,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent [NSApp setMenu:nativeAppMenu->GetNative()]; } - else - { - [NSApp setMenu:nullptr]; - } } -(void) applyMenu:(AvnMenu *)menu From a056f0b654ae220ddbb58a2648fd286676769fef Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 2 May 2022 11:39:08 +0200 Subject: [PATCH 536/820] Second attempt to fix text measurements with scaling --- .../Presenters/TextPresenter.cs | 27 ++++++++++++------- src/Avalonia.Controls/TextBlock.cs | 19 +++++++------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index eecf0d9101..0785149a73 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -511,30 +511,39 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } - + + protected override Size MeasureOverride(Size availableSize) { + if (string.IsNullOrEmpty(Text)) + { + return new Size(); + } + _constraint = availableSize; - + _textLayout = null; - + InvalidateArrange(); - var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1); - - return new Size(measuredSize.Width, measuredSize.Height); + var measuredSize = TextLayout.Bounds.Size; + + return measuredSize; } protected override Size ArrangeOverride(Size finalSize) { + if (finalSize.Width < TextLayout.Bounds.Width) + { + finalSize = finalSize.WithWidth(TextLayout.Bounds.Width); + } + if (MathUtilities.AreClose(_constraint.Width, finalSize.Width)) { return finalSize; } - var textSize = PixelSize.FromSize(finalSize, VisualRoot?.RenderScaling ?? 1); - - _constraint = new Size(textSize.Width, textSize.Height); + _constraint = new Size(finalSize.Width, double.PositiveInfinity); _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 1087310841..bbe6aeb7ee 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -604,7 +604,9 @@ namespace Avalonia.Controls return new Size(); } - var padding = Padding; + var scale = LayoutHelper.GetLayoutScale(this); + + var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); _constraint = availableSize.Deflate(padding); @@ -612,23 +614,24 @@ namespace Avalonia.Controls InvalidateArrange(); - var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1); + var measuredSize = TextLayout.Bounds.Size.Inflate(padding); - return new Size(measuredSize.Width, measuredSize.Height).Inflate(padding); + return measuredSize; } protected override Size ArrangeOverride(Size finalSize) { + if(finalSize.Width < TextLayout.Bounds.Width) + { + finalSize = finalSize.WithWidth(TextLayout.Bounds.Width); + } + if (MathUtilities.AreClose(_constraint.Width, finalSize.Width)) { return finalSize; } - var padding = Padding; - - var textSize = PixelSize.FromSize(finalSize.Deflate(padding), VisualRoot?.RenderScaling ?? 1); - - _constraint = new Size(textSize.Width, textSize.Height); + _constraint = new Size(finalSize.Width, double.PositiveInfinity); _textLayout = null; From bb8aaee1e0da5116d06febbe8aa512add3be0ce0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 2 May 2022 23:09:13 +0200 Subject: [PATCH 537/820] Optimizing resource related code. --- .../Controls/ResourceNodeExtensions.cs | 11 ++++------- src/Avalonia.Base/Styling/IStyle.cs | 2 +- src/Avalonia.Base/Styling/Styles.cs | 2 +- .../Controls/DataValidationErrors.xaml | 10 +++++++--- .../MarkupExtensions/StaticResourceExtension.cs | 17 +++++++---------- .../Styling/StyleInclude.cs | 6 +++--- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs index 513b3f2424..1758c45650 100644 --- a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs +++ b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs @@ -40,19 +40,16 @@ namespace Avalonia.Controls control = control ?? throw new ArgumentNullException(nameof(control)); key = key ?? throw new ArgumentNullException(nameof(key)); - IResourceHost? current = control; + IResourceNode? current = control; while (current != null) { - if (current is IResourceHost host) + if (current.TryGetResource(key, out value)) { - if (host.TryGetResource(key, out value)) - { - return true; - } + return true; } - current = (current as IStyledElement)?.StylingParent as IResourceHost; + current = (current as IStyledElement)?.StylingParent as IResourceNode; } value = null; diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs index 78fbe0f2b5..738a69cb88 100644 --- a/src/Avalonia.Base/Styling/IStyle.cs +++ b/src/Avalonia.Base/Styling/IStyle.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Defines the interface for styles. /// - public interface IStyle + public interface IStyle : IResourceNode { /// /// Gets a collection of child styles. diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index 81502f1570..d79081152e 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -160,7 +160,7 @@ namespace Avalonia.Styling for (var i = Count - 1; i >= 0; --i) { - if (this[i] is IResourceProvider p && p.TryGetResource(key, out value)) + if (this[i].TryGetResource(key, out value)) { return true; } diff --git a/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml index a3a4cf4662..d7bf4bbbf1 100644 --- a/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml @@ -1,9 +1,13 @@ - + diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index db33b88cc3..f865f87220 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -39,17 +39,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions targetType = setter.Property.PropertyType; } - // Look upwards though the ambient context for IResourceHosts and IResourceProviders + // Look upwards though the ambient context for IResourceNodes // which might be able to give us the resource. - foreach (var e in stack.Parents) + foreach (var parent in stack.Parents) { - object value; - - if (e is IResourceHost host && host.TryGetResource(ResourceKey, out value)) - { - return ColorToBrushConverter.Convert(value, targetType); - } - else if (e is IResourceProvider provider && provider.TryGetResource(ResourceKey, out value)) + if (parent is IResourceNode node && node.TryGetResource(ResourceKey, out var value)) { return ColorToBrushConverter.Convert(value, targetType); } @@ -58,7 +52,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (provideTarget.TargetObject is IControl target && provideTarget.TargetProperty is PropertyInfo property) { - DelayedBinding.Add(target, property, x => GetValue(x, targetType)); + var localTargetType = targetType; + var localInstance = this; + + DelayedBinding.Add(target, property, x => localInstance.GetValue(x, localTargetType)); return AvaloniaProperty.UnsetValue; } diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 607b552c28..fa4a27fc50 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -60,7 +60,7 @@ namespace Avalonia.Markup.Xaml.Styling } } - bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; + bool IResourceNode.HasResources => Loaded?.HasResources ?? false; IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); @@ -86,9 +86,9 @@ namespace Avalonia.Markup.Xaml.Styling public bool TryGetResource(object key, out object? value) { - if (!_isLoading && Loaded is IResourceProvider p) + if (!_isLoading) { - return p.TryGetResource(key, out value); + return Loaded.TryGetResource(key, out value); } value = null; From 4aa0f878c2890dca2ade446832192dafae5d0675 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 2 May 2022 23:25:18 +0200 Subject: [PATCH 538/820] Add an explanation why certain locals are copied. --- .../MarkupExtensions/StaticResourceExtension.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index f865f87220..add97a660b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -52,6 +52,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (provideTarget.TargetObject is IControl target && provideTarget.TargetProperty is PropertyInfo property) { + // This is stored locally to avoid allocating closure in the outer scope. var localTargetType = targetType; var localInstance = this; From 0a8d679ab29e3364cce0b716d7c9bc9f55380441 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 3 May 2022 11:54:35 +0200 Subject: [PATCH 539/820] Register StretchProperty for Viewbox instead of Image --- src/Avalonia.Controls/Viewbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index dd74d549bd..33a05f126d 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty StretchProperty = - AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); + AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); /// /// Defines the property. From bdadb6a35111899747c353aa01a1ab334805a384 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 3 May 2022 12:04:26 +0200 Subject: [PATCH 540/820] Fix GetTextBounds for mixed runs --- .../Media/TextFormatting/TextLineImpl.cs | 47 ++++-------------- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 7 +-- .../Media/TextShaperImpl.cs | 33 +++++------- ...estrictedHeight_VerticalAlign.expected.png | Bin 768 -> 752 bytes ...estrictedHeight_VerticalAlign.expected.png | Bin 532 -> 557 bytes 5 files changed, 24 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 35ada3c7ee..a518e2ffb8 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -536,6 +536,11 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } + if(currentPosition < firstTextSourceCharacterIndex) + { + startX += currentRun.Size.Width; + } + currentPosition += currentRun.TextSourceLength; break; @@ -590,10 +595,10 @@ namespace Avalonia.Media.TextFormatting public TextLineImpl FinalizeLine() { - BidiReorder(); - _textLineMetrics = CreateLineMetrics(); + BidiReorder(); + return this; } @@ -1068,41 +1073,11 @@ namespace Avalonia.Media.TextFormatting } } - switch (_paragraphProperties.FlowDirection) + if (index == _textRuns.Count - 1) { - case FlowDirection.LeftToRight: - { - if (index == _textRuns.Count - 1) - { - width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; - trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = textRun.GlyphRun.Metrics.NewlineLength; - } - - break; - } - - case FlowDirection.RightToLeft: - { - if (index == _textRuns.Count - 1) - { - var firstRun = _textRuns[0]; - - if (firstRun is ShapedTextCharacters shapedTextCharacters) - { - var offset = shapedTextCharacters.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - - shapedTextCharacters.GlyphRun.Metrics.Width; - - width = widthIncludingWhitespace + - textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset; - - trailingWhitespaceLength = shapedTextCharacters.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = shapedTextCharacters.GlyphRun.Metrics.NewlineLength; - } - } - - break; - } + width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; + trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; + newLineLength = textRun.GlyphRun.Metrics.NewlineLength; } widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index ebaa247da8..908b0ffa47 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -27,7 +27,7 @@ namespace Avalonia.Skia buffer.GuessSegmentProperties(); - buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; + buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); @@ -35,11 +35,6 @@ namespace Avalonia.Skia font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 59027a663f..f4e4b00147 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; @@ -11,8 +10,7 @@ using GlyphInfo = HarfBuzzSharp.GlyphInfo; namespace Avalonia.Direct2D1.Media { - -internal class TextShaperImpl : ITextShaperImpl + internal class TextShaperImpl : ITextShaperImpl { public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { @@ -23,25 +21,20 @@ internal class TextShaperImpl : ITextShaperImpl using (var buffer = new Buffer()) { - buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length); + buffer.AddUtf16(text.Buffer.Span, text.BufferOffset, text.Length); MergeBreakPair(buffer); - + buffer.GuessSegmentProperties(); - buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; + buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight - buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); + buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font; font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; @@ -60,13 +53,13 @@ internal class TextShaperImpl : ITextShaperImpl var glyphIndex = (ushort)sourceInfo.Codepoint; - var glyphCluster = (int)sourceInfo.Cluster; + var glyphCluster = (int)(sourceInfo.Cluster); var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale); var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); - if (glyphIndex == 0 && text[glyphCluster] == '\t') + if (glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t') { glyphIndex = typeface.GetGlyph(' '); @@ -75,9 +68,7 @@ internal class TextShaperImpl : ITextShaperImpl 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; } - var targetInfo = - new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, - glyphOffset); + var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); shapedBuffer[i] = targetInfo; } @@ -91,7 +82,7 @@ internal class TextShaperImpl : ITextShaperImpl var length = buffer.Length; var glyphInfos = buffer.GetGlyphInfoSpan(); - + var second = glyphInfos[length - 1]; if (!new Codepoint((int)second.Codepoint).IsBreakChar) @@ -102,7 +93,7 @@ internal class TextShaperImpl : ITextShaperImpl if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n') { var first = glyphInfos[length - 2]; - + first.Codepoint = '\u200C'; second.Codepoint = '\u200C'; second.Cluster = first.Cluster; @@ -113,7 +104,7 @@ internal class TextShaperImpl : ITextShaperImpl { *p = first; } - + fixed (GlyphInfo* p = &glyphInfos[length - 1]) { *p = second; @@ -148,7 +139,7 @@ internal class TextShaperImpl : ITextShaperImpl private static double GetGlyphAdvance(ReadOnlySpan glyphPositions, int index, double textScale) { // Depends on direction of layout - // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; + // glyphPositions[index].YAdvance * textScale; return glyphPositions[index].XAdvance * textScale; } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png index edd4dfd263d0aeddca97eb4f38b4a5310ef6441f..e8624fa457f37565fdc483c474424991e7b696d3 100644 GIT binary patch delta 663 zcmV;I0%-k!2Ji)tK~kDYL_t(|UhUgIa??N*$8nhhgpv-9Kst&PbZ~-{bWo>HI%eP$ zbliY0bTFi#WQHbHCZVECE@0mCYC&Ev)~@tc(Vy>U=9gVr&L&sF5? z2a=zgt*ZJWIdk3Ow4gWZj_kMw*zr#C&2{Yze1z^>+>Eo|l*ZA6u^yCDYTA#}F7b|b z1-+DUQlBp7XTR-n+9lr6_JMtu-Y0j%e~v?mF0?|+c*2;U(Dpd(9@K4;4-y?YmRFK4 z^g(@eT|_WHMd&L`zBFy;-KXYy|(KU{b8zDnNKxsvFW zH-0;ezHL90^u_#aD^}3IBtx9|jEVjQPyh0jnrD(F8Df4|Lt|DbXw1sJ6tu3b_2LwN zfr_3W`0X%Otk(2a znEu5s`c3DXRyVzJM1r%nkRJ0aMM(FjDeD5@R(^af#<|TF^PCdjI796AKN{2Wx1| x3I&Z>NnUc)osL*?lhFbeli&gb8GzoZ>JL{KmA-v_Vov}7002ovPDHLkV1g47Q*QtO delta 680 zcmV;Z0$2U;1%L*SK~kzoL_t(|UhUg4a??N*#&MYggpv-903AgNIyeCxbfl&a9W!tW zI&MH0Iv6M@nW3S|1S(3nfO#)Z%WR}I+O^(-y!!uUzG!9H8)yBZ?8;6DlfeQOe*gdg zXxpaWPS|ey#tfUx?*;5HyP&tZcTL!I`ujjZqdp26OL9YU+nOU6lCRd>je^FKJd`}O zW_MF^B>8f=*LCkC {7WCyrdau|9DDpz`!J7I0!x`G6xaq6RA@-#wV?8P7*0lB2 zcR0njpx4+}$;-od?LS_b&&a;ve=BL7*bO6(B(JSGl6vx=r1i#b7`Y?S$yCbPULeM6 zTHMz){gUg6L?5a-l^jR=yNWr7KJht^UmM12HvJ8fFHxkRw^Gof+f%VkP`9s=*e0mk z7vps`wh8=x#-cMc>d`3$^6eTg3VIo*Bf3vR=Kl|;! z(DHaSQP7@?)mjVRC24uQm~x;g)e2hQi1%9P=%2M8);uWsoOPb3`6l@l?GNK=&Er+A zpubDJ=7suZ&Sz^Ll=qV7ea`gjQ**ChgxBkvx2!pv=VH~fuM&*cZ0&rgMIUtbr>&cyZeJy_O~80f#WsdHG}g%XEHOVp z>R4%l)Unb8sbi%HQpZXYq>i0Rjt95J(bw-R-tWct3GwW!G(|iMC>jgkxg4sswAxF~ z1qq=Aoq5-UtN(5T)WaMaO`)LClwW{S>gC-`V7Q6^ O0000Wy2)Tif!}f*BA9YQBr<>Yv<0+ed$LhGhl$6qoFtRV$DAqv>tEFoHy;= zjj8AUHLmvgqjP}4f0`Ym-l@|ncRSzK*B<`-V%Iyxa-Mt3j*FJP@}0!LwOcIxMdrlE z`EBc_9=&?AZmz%QzU4W~PG3(r%l<3q)s%g)(bchAU-i5`|6g~~!uOv(cvj{7-f@I; zZO^|R^Na=Arn~kV?eHzSTz#h~bwk)M4ct2k#G&)%(W*Dsx)C6j$|eyBU2 z#3_f(+TH&xR=QqWGxcS(?x8Q~dmJTy>788NygzpSjqgWi)>y6GoVM2Iepc=R`^UZi z3We58xyrynyb(IzS%pj{N(kv(qntpW!+KP9r*m+Cc8U!>XmlI yb8njNTQ2ZS)p@fv+pJj4eL2tLs~9ojqI);H&U7`F+XGKFP=N&RV07srr_IdAW5%)1;Q!uDXlX#W9ggKvom4qEyL4~4P0&0{{ck2_jY zz?fNE$h%Z>(W=lXQ?j31ZCk(J|C5mA@slS#KgDbNF)^S4hwFN;%CwgrT&UI0`gre( zyBn{Z`^PAr`Jt|XYkILgL&iym%_{3BPpMbF-QIRwbLpF=FDusivZW`ln|z07Z}8Ve zAHMMX-Jp@P^FYYz`%&?e|M>j%d7pLG=tA@w(@X6Ni@OMP+s%{|8UZRef2 zOxJB?{S~_W*Ou5%Gj2?N7|#)W?H1GDRgZPpV+}8q9Cgq?Fkj{U;Tz2DaF{%vno$(gm^S9kO54e|f1`v3Z(cq0>#J*i*%C*Mn*p3gV8zGmi= zcRgFP)>dv@6<_N1-{NIR@src0`e9#J_nmMI=P`-C@@dwg Date: Tue, 3 May 2022 14:38:34 +0200 Subject: [PATCH 541/820] Fix crash when property getter throws --- .../ViewModels/AvaloniaPropertyViewModel.cs | 64 +++++++++++++------ .../ViewModels/ClrPropertyViewModel.cs | 41 ++++++++---- 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index aa03330cc5..0a2a6cac25 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -1,13 +1,17 @@ +using System; +using Avalonia.Data; +using Avalonia.Media; + namespace Avalonia.Diagnostics.ViewModels { internal class AvaloniaPropertyViewModel : PropertyViewModel { private readonly AvaloniaObject _target; - private System.Type _assignedType; + private Type _assignedType; private object? _value; private string _priority; private string _group; - private readonly System.Type _propertyType; + private readonly Type _propertyType; #nullable disable // Remove "nullable disable" after MemberNotNull will work on our CI. @@ -28,13 +32,9 @@ namespace Avalonia.Diagnostics.ViewModels public AvaloniaProperty Property { get; } public override object Key => Property; public override string Name { get; } - public override bool? IsAttached => - Property.IsAttached; - - public override string Priority => - _priority; - - public override System.Type AssignedType => _assignedType; + public override bool? IsAttached => Property.IsAttached; + public override string Priority => _priority; + public override Type AssignedType => _assignedType; public override string? Value { @@ -53,30 +53,58 @@ namespace Avalonia.Diagnostics.ViewModels public override string Group => _group; - public override System.Type? DeclaringType { get; } - public override System.Type PropertyType => _propertyType; + public override Type? DeclaringType { get; } + public override Type PropertyType => _propertyType; // [MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))] public override void Update() { if (Property.IsDirect) { - RaiseAndSetIfChanged(ref _value, _target.GetValue(Property), nameof(Value)); - RaiseAndSetIfChanged(ref _assignedType,_value?.GetType() ?? Property.PropertyType, nameof(AssignedType)); + object? value; + Type? valueType = null; + + try + { + value = _target.GetValue(Property); + valueType = value?.GetType(); + } + catch (Exception e) + { + value = e; + } + + RaiseAndSetIfChanged(ref _value, value, nameof(Value)); + RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType)); RaiseAndSetIfChanged(ref _priority, "Direct", nameof(Priority)); _group = "Properties"; } else { - var val = _target.GetDiagnostic(Property); + object? value; + Type? valueType = null; + BindingPriority? priority = null; + + try + { + var diag = _target.GetDiagnostic(Property); + + value = diag.Value; + valueType = value?.GetType(); + priority = diag.Priority; + } + catch (Exception e) + { + value = e; + } - RaiseAndSetIfChanged(ref _value, val?.Value, nameof(Value)); - RaiseAndSetIfChanged(ref _assignedType, _value?.GetType() ?? Property.PropertyType, nameof(AssignedType)); + RaiseAndSetIfChanged(ref _value, value, nameof(Value)); + RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType)); - if (val != null) + if (priority != null) { - RaiseAndSetIfChanged(ref _priority, val.Priority.ToString(), nameof(Priority)); + RaiseAndSetIfChanged(ref _priority, priority.ToString()!, nameof(Priority)); RaiseAndSetIfChanged(ref _group, IsAttached == true ? "Attached Properties" : "Properties", nameof(Group)); } else diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index e2d8a30c8a..296413de78 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -1,13 +1,15 @@ -using System.Reflection; +using System; +using System.Reflection; +using Avalonia.Media; namespace Avalonia.Diagnostics.ViewModels { internal class ClrPropertyViewModel : PropertyViewModel { private readonly object _target; - private System.Type _assignedType; + private Type _assignedType; private object? _value; - private readonly System.Type _propertyType; + private readonly Type _propertyType; #nullable disable // Remove "nullable disable" after MemberNotNull will work on our CI. @@ -25,6 +27,7 @@ namespace Avalonia.Diagnostics.ViewModels { Name = property.DeclaringType.Name + '.' + property.Name; } + DeclaringType = property.DeclaringType; _propertyType = property.PropertyType; @@ -36,10 +39,10 @@ namespace Avalonia.Diagnostics.ViewModels public override string Name { get; } public override string Group => "CLR Properties"; - public override System.Type AssignedType => _assignedType; - public override System.Type PropertyType => _propertyType; + public override Type AssignedType => _assignedType; + public override Type PropertyType => _propertyType; - public override string? Value + public override string? Value { get => ConvertToString(_value); set @@ -54,20 +57,30 @@ namespace Avalonia.Diagnostics.ViewModels } } - public override string Priority => - string.Empty; + public override string Priority => string.Empty; - public override bool? IsAttached => - default; + public override bool? IsAttached => default; - public override System.Type? DeclaringType { get; } + public override Type? DeclaringType { get; } // [MemberNotNull(nameof(_type))] public override void Update() { - var val = Property.GetValue(_target); - RaiseAndSetIfChanged(ref _value, val, nameof(Value)); - RaiseAndSetIfChanged(ref _assignedType, _value?.GetType() ?? Property.PropertyType, nameof(AssignedType)); + object? value; + Type? valueType = null; + + try + { + value = Property.GetValue(_target); + valueType = value?.GetType(); + } + catch (Exception e) + { + value = e; + } + + RaiseAndSetIfChanged(ref _value, value, nameof(Value)); + RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType)); RaisePropertyChanged(nameof(Type)); } } From b5391f9419bd32f6410dc8c378ee3db3a9ad2e54 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 3 May 2022 16:34:15 +0200 Subject: [PATCH 542/820] Fix GetTextBounds for some Bidi scenarios --- .../Media/TextFormatting/TextLineImpl.cs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index a518e2ffb8..73ec055bbe 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -252,7 +252,7 @@ namespace Avalonia.Media.TextFormatting //Look at the left and right edge of the current run if (currentRun.IsLeftToRight) { - if (lastRun == null || lastRun.IsLeftToRight) + if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) { if (characterIndex <= textRun.Text.Start) { @@ -455,7 +455,7 @@ namespace Avalonia.Media.TextFormatting } default: { - goto noop; + goto noop; } } @@ -536,7 +536,7 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } - if(currentPosition < firstTextSourceCharacterIndex) + if (currentPosition < firstTextSourceCharacterIndex) { startX += currentRun.Size.Width; } @@ -554,24 +554,29 @@ namespace Avalonia.Media.TextFormatting var width = endX - startX; - if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + if (!MathUtilities.IsZero(width)) { - currentRect = currentRect.WithWidth(currentRect.Width + width); + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentRect.Width + width); - var textBounds = new TextBounds(currentRect, currentDirection); + var textBounds = new TextBounds(currentRect, currentDirection); - result[result.Count - 1] = textBounds; - } - else - { - currentRect = new Rect(startX, 0, width, Height); + result[result.Count - 1] = textBounds; + } + else + { + + currentRect = new Rect(startX, 0, width, Height); - result.Add(new TextBounds(currentRect, currentDirection)); + result.Add(new TextBounds(currentRect, currentDirection)); + + } } if (currentDirection == FlowDirection.LeftToRight) { - if (currentPosition >= firstTextSourceCharacterIndex + textLength) + if (currentPosition > firstTextSourceCharacterIndex + textLength) { break; } @@ -1026,7 +1031,7 @@ namespace Avalonia.Media.TextFormatting var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; - + var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; @@ -1036,8 +1041,8 @@ namespace Avalonia.Media.TextFormatting var lineGap = glyphTypeface.LineGap * scale; var height = descent - ascent + lineGap; - - var lineHeight = _paragraphProperties.LineHeight; + + var lineHeight = _paragraphProperties.LineHeight; for (var index = 0; index < _textRuns.Count; index++) { @@ -1136,10 +1141,10 @@ namespace Avalonia.Media.TextFormatting if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) { - if(lineHeight > height) + if (lineHeight > height) { height = lineHeight; - } + } } return new TextLineMetrics(widthIncludingWhitespace > _paragraphWidth, height, newLineLength, start, From 6a8eb5a1cf551503c2090e3a0b8dfdc4eec8497c Mon Sep 17 00:00:00 2001 From: peter kuhn Date: Wed, 4 May 2022 06:58:39 +0200 Subject: [PATCH 543/820] Fix Typo --- src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj | 9 --------- src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index ede0791438..35603fe216 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -10,15 +10,6 @@ - - - - - - - MSBuild:Compile - - diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 6251c86720..d40ba0cc1d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -5,7 +5,7 @@ Selector="NativeMenuBar"> - + @@ -21,7 +21,7 @@ - + From c34b7f2d31531c0f1210d2ffd8adf132e574e3dd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 4 May 2022 13:35:34 +0200 Subject: [PATCH 544/820] Remove generic methods from ILogSink. Part of the push to remove generic virtual methods from Avalonia for performance reasons. Generic methods were added to this interface in #3055 to prevent boxing before we added `Logger.TryGet` (#4135). Given that since we added `Logger.TryGet`, only enabled logging levels will result in a call to the logger, we can move back to using `params object[]` and boxing; removing the generic interface methods. --- src/Avalonia.Base/Logging/ILogSink.cs | 51 ----------------------- src/Avalonia.Base/Logging/TraceLogSink.cs | 38 ++--------------- tests/Avalonia.UnitTests/TestLogSink.cs | 17 -------- 3 files changed, 3 insertions(+), 103 deletions(-) diff --git a/src/Avalonia.Base/Logging/ILogSink.cs b/src/Avalonia.Base/Logging/ILogSink.cs index 27558ba0ee..60709776c6 100644 --- a/src/Avalonia.Base/Logging/ILogSink.cs +++ b/src/Avalonia.Base/Logging/ILogSink.cs @@ -26,57 +26,6 @@ namespace Avalonia.Logging object? source, string messageTemplate); - /// - /// Logs an event. - /// - /// The log event level. - /// The area that the event originates. - /// The object from which the event originates. - /// The message template. - /// Message property value. - void Log( - LogEventLevel level, - string area, - object? source, - string messageTemplate, - T0 propertyValue0); - - /// - /// Logs an event. - /// - /// The log event level. - /// The area that the event originates. - /// The object from which the event originates. - /// The message template. - /// Message property value. - /// Message property value. - void Log( - LogEventLevel level, - string area, - object? source, - string messageTemplate, - T0 propertyValue0, - T1 propertyValue1); - - /// - /// Logs an event. - /// - /// The log event level. - /// The area that the event originates. - /// The object from which the event originates. - /// The message template. - /// Message property value. - /// Message property value. - /// Message property value. - void Log( - LogEventLevel level, - string area, - object? source, - string messageTemplate, - T0 propertyValue0, - T1 propertyValue1, - T2 propertyValue2); - /// /// Logs a new event. /// diff --git a/src/Avalonia.Base/Logging/TraceLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs index 02ed191d2c..05e4b8bc5a 100644 --- a/src/Avalonia.Base/Logging/TraceLogSink.cs +++ b/src/Avalonia.Base/Logging/TraceLogSink.cs @@ -28,31 +28,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Trace.WriteLine(Format(area, messageTemplate, source)); - } - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0) - { - if (IsEnabled(level, area)) - { - Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0)); - } - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - if (IsEnabled(level, area)) - { - Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); - } - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - if (IsEnabled(level, area)) - { - Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); + Trace.WriteLine(Format(area, messageTemplate, source, null)); } } @@ -68,9 +44,7 @@ namespace Avalonia.Logging string area, string template, object? source, - T0? v0 = default, - T1? v1 = default, - T2? v2 = default) + object?[]? values) { var result = new StringBuilder(template.Length); var r = new CharacterReader(template.AsSpan()); @@ -93,13 +67,7 @@ namespace Avalonia.Logging if (r.Peek != '{') { result.Append('\''); - result.Append(i++ switch - { - 0 => v0, - 1 => v1, - 2 => v2, - _ => null - }); + result.Append(values?[i++]); result.Append('\''); r.TakeUntil('}'); r.Take(); diff --git a/tests/Avalonia.UnitTests/TestLogSink.cs b/tests/Avalonia.UnitTests/TestLogSink.cs index e10292a59b..8b8084ca17 100644 --- a/tests/Avalonia.UnitTests/TestLogSink.cs +++ b/tests/Avalonia.UnitTests/TestLogSink.cs @@ -37,23 +37,6 @@ namespace Avalonia.UnitTests _callback(level, area, source, messageTemplate); } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) - { - _callback(level, area, source, messageTemplate, propertyValue0); - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, - T0 propertyValue0, T1 propertyValue1) - { - _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1); - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, - T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2); - } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) { From a3112b49e5c310a685d49876352257d85cac22f5 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 4 May 2022 14:02:21 +0200 Subject: [PATCH 545/820] Unwrap exceptions --- .../Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs | 4 ++-- .../Diagnostics/ViewModels/ClrPropertyViewModel.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index 0a2a6cac25..7384daae30 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -71,7 +71,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch (Exception e) { - value = e; + value = e.GetBaseException(); } RaiseAndSetIfChanged(ref _value, value, nameof(Value)); @@ -96,7 +96,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch (Exception e) { - value = e; + value = e.GetBaseException(); } RaiseAndSetIfChanged(ref _value, value, nameof(Value)); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index 296413de78..73fb615b32 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -76,7 +76,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch (Exception e) { - value = e; + value = e.GetBaseException(); } RaiseAndSetIfChanged(ref _value, value, nameof(Value)); From b3af7f0c6a012512520a09ac06a39fa5fc1b24c0 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 4 May 2022 14:45:02 +0200 Subject: [PATCH 546/820] Proper Canvas positioning for adorner layer --- src/Avalonia.Controls/Canvas.cs | 75 +++++++++++-------- .../Primitives/AdornerLayer.cs | 2 +- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/Avalonia.Controls/Canvas.cs b/src/Avalonia.Controls/Canvas.cs index fabf8978c7..adee7d4d90 100644 --- a/src/Avalonia.Controls/Canvas.cs +++ b/src/Avalonia.Controls/Canvas.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Avalonia.Input; using Avalonia.Layout; @@ -159,47 +160,57 @@ namespace Avalonia.Controls } /// - /// Arranges the control's children. + /// Arranges a single child. /// - /// The size allocated to the control. - /// The space taken. - protected override Size ArrangeOverride(Size finalSize) + /// The child to arrange. + /// The size allocated to the canvas. + protected virtual void ArrangeChild(Control child, Size finalSize) { - foreach (Control child in Children) - { - double x = 0.0; - double y = 0.0; - double elementLeft = GetLeft(child); + double x = 0.0; + double y = 0.0; + double elementLeft = GetLeft(child); - if (!double.IsNaN(elementLeft)) - { - x = elementLeft; - } - else + if (!double.IsNaN(elementLeft)) + { + x = elementLeft; + } + else + { + // Arrange with right. + double elementRight = GetRight(child); + if (!double.IsNaN(elementRight)) { - // Arrange with right. - double elementRight = GetRight(child); - if (!double.IsNaN(elementRight)) - { - x = finalSize.Width - child.DesiredSize.Width - elementRight; - } + x = finalSize.Width - child.DesiredSize.Width - elementRight; } + } - double elementTop = GetTop(child); - if (!double.IsNaN(elementTop) ) - { - y = elementTop; - } - else + double elementTop = GetTop(child); + if (!double.IsNaN(elementTop)) + { + y = elementTop; + } + else + { + double elementBottom = GetBottom(child); + if (!double.IsNaN(elementBottom)) { - double elementBottom = GetBottom(child); - if (!double.IsNaN(elementBottom)) - { - y = finalSize.Height - child.DesiredSize.Height - elementBottom; - } + y = finalSize.Height - child.DesiredSize.Height - elementBottom; } + } - child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); + child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); + } + + /// + /// Arranges the control's children. + /// + /// The size allocated to the control. + /// The space taken. + protected override Size ArrangeOverride(Size finalSize) + { + foreach (Control child in Children) + { + ArrangeChild(child, finalSize); } return finalSize; diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index fb047d93df..5ad4e39baf 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Primitives } else { - child.Arrange(new Rect(finalSize)); + ArrangeChild((Control) child, finalSize); } } } From 2cf78ac11c7badc0f9ddb015809266d052a810f6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 17:38:03 +0100 Subject: [PATCH 547/820] move IWindowBaseImpl to its own file. --- .../Avalonia.Native/src/OSX/INSWindowHolder.h | 15 + .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 121 ++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 511 ++++++++++++++ native/Avalonia.Native/src/OSX/window.h | 7 +- native/Avalonia.Native/src/OSX/window.mm | 621 +----------------- 5 files changed, 650 insertions(+), 625 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/INSWindowHolder.h create mode 100644 native/Avalonia.Native/src/OSX/WindowBaseImpl.h create mode 100644 native/Avalonia.Native/src/OSX/WindowBaseImpl.mm diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h new file mode 100644 index 0000000000..aa8c34ef00 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -0,0 +1,15 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H +#define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H + +struct INSWindowHolder +{ + virtual AvnWindow* _Nonnull GetNSWindow () = 0; + virtual AvnView* _Nonnull GetNSView () = 0; +}; + +#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h new file mode 100644 index 0000000000..2f9b05988f --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -0,0 +1,121 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H +#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H + +#include "INSWindowHolder.h" + +class WindowBaseImpl : public virtual ComObject, + public virtual IAvnWindowBase, + public INSWindowHolder { +private: + NSCursor *cursor; + +public: + FORWARD_IUNKNOWN() + +BEGIN_INTERFACE_MAP() + INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) + END_INTERFACE_MAP() + + virtual ~WindowBaseImpl() { + View = NULL; + Window = NULL; + } + + AutoFitContentView *StandardContainer; + AvnView *View; + AvnWindow *Window; + ComPtr BaseEvents; + ComPtr _glContext; + NSObject *renderTarget; + AvnPoint lastPositionSet; + NSString *_lastTitle; + IAvnMenu *_mainMenu; + + bool _shown; + bool _inResize; + + WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); + + virtual HRESULT ObtainNSWindowHandle(void **ret) override; + + virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override; + + virtual HRESULT ObtainNSViewHandle(void **ret) override; + + virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; + + virtual AvnWindow *GetNSWindow() override; + + virtual AvnView *GetNSView() override; + + virtual HRESULT Show(bool activate, bool isDialog) override; + + virtual bool ShouldTakeFocusOnShow(); + + virtual HRESULT Hide() override; + + virtual HRESULT Activate() override; + + virtual HRESULT SetTopMost(bool value) override; + + virtual HRESULT Close() override; + + virtual HRESULT GetClientSize(AvnSize *ret) override; + + virtual HRESULT GetFrameSize(AvnSize *ret) override; + + virtual HRESULT GetScaling(double *ret) override; + + virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override; + + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override; + + virtual HRESULT Invalidate(AvnRect rect) override; + + virtual HRESULT SetMainMenu(IAvnMenu *menu) override; + + virtual HRESULT BeginMoveDrag() override; + + virtual HRESULT BeginResizeDrag(AvnWindowEdge edge) override; + + virtual HRESULT GetPosition(AvnPoint *ret) override; + + virtual HRESULT SetPosition(AvnPoint point) override; + + virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override; + + virtual HRESULT SetCursor(IAvnCursor *cursor) override; + + virtual void UpdateCursor(); + + virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override; + + virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; + + virtual HRESULT SetBlurEnabled(bool enable) override; + + virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, + IAvnClipboard *clipboard, IAvnDndResultCallback *cb, + void *sourceHandle) override; + + virtual bool IsDialog(); + +protected: + virtual NSWindowStyleMask GetStyle(); + + void UpdateStyle(); + +public: + virtual void OnResized(); +}; + +#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm new file mode 100644 index 0000000000..482b6172af --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -0,0 +1,511 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#include "common.h" +#import "window.h" +#include "menu.h" +#include "rendertarget.h" +#include "automation.h" +#import "WindowBaseImpl.h" +#import "cursor.h" + +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { + _shown = false; + _inResize = false; + _mainMenu = nullptr; + BaseEvents = events; + _glContext = gl; + renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; + View = [[AvnView alloc] initWithParent:this]; + StandardContainer = [[AutoFitContentView new] initWithContent:View]; + + Window = [[AvnWindow alloc] initWithParent:this]; + [Window setContentView:StandardContainer]; + + lastPositionSet.X = 100; + lastPositionSet.Y = 100; + _lastTitle = @""; + + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setOpaque:false]; +} + +HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) View; + + return S_OK; +} + +HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) View; + + return S_OK; +} + +AvnWindow *WindowBaseImpl::GetNSWindow() { + return Window; +} + +AvnView *WindowBaseImpl::GetNSView() { + return View; +} + +HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) Window; + + return S_OK; +} + +HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { + START_COM_CALL; + + @autoreleasepool { + SetPosition(lastPositionSet); + UpdateStyle(); + + [Window setTitle:_lastTitle]; + + if (ShouldTakeFocusOnShow() && activate) { + [Window orderFront:Window]; + [Window makeKeyAndOrderFront:Window]; + [Window makeFirstResponder:View]; + [NSApp activateIgnoringOtherApps:YES]; + } else { + [Window orderFront:Window]; + } + + _shown = true; + + return S_OK; + } +} + +bool WindowBaseImpl::ShouldTakeFocusOnShow() { + return true; +} + +HRESULT WindowBaseImpl::ObtainNSWindowHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) Window; + + return S_OK; +} + +HRESULT WindowBaseImpl::Hide() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + [Window orderOut:Window]; + [Window restoreParentWindow]; + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Activate() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + [Window makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + } + } + + return S_OK; +} + +HRESULT WindowBaseImpl::SetTopMost(bool value) { + START_COM_CALL; + + @autoreleasepool { + [Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Close() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + auto window = Window; + Window = nullptr; + + try { + // Seems to throw sometimes on application exit. + [window close]; + } + catch (NSException *) {} + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + auto frame = [View frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + auto frame = [Window frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetScaling(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + if (Window == nullptr) { + *ret = 1; + return S_OK; + } + + *ret = [Window backingScaleFactor]; + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { + START_COM_CALL; + + @autoreleasepool { + [Window setMinSize:ToNSSize(minSize)]; + [Window setMaxSize:ToNSSize(maxSize)]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reason) { + if (_inResize) { + return S_OK; + } + + _inResize = true; + + START_COM_CALL; + auto resizeBlock = ResizeScope(View, reason); + + @autoreleasepool { + auto maxSize = [Window maxSize]; + auto minSize = [Window minSize]; + + if (x < minSize.width) { + x = minSize.width; + } + + if (y < minSize.height) { + y = minSize.height; + } + + if (x > maxSize.width) { + x = maxSize.width; + } + + if (y > maxSize.height) { + y = maxSize.height; + } + + @try { + if (!_shown) { + BaseEvents->Resized(AvnSize{x, y}, reason); + } + + [Window setContentSize:NSSize{x, y}]; + [Window invalidateShadow]; + } + @finally { + _inResize = false; + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Invalidate(AvnRect rect) { + START_COM_CALL; + + @autoreleasepool { + [View setNeedsDisplayInRect:[View frame]]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { + START_COM_CALL; + + _mainMenu = menu; + + auto nativeMenu = dynamic_cast(menu); + + auto nsmenu = nativeMenu->GetNative(); + + [Window applyMenu:nsmenu]; + + if ([Window isKeyWindow]) { + [Window showWindowMenuWithAppMenu]; + } + + return S_OK; +} + +HRESULT WindowBaseImpl::BeginMoveDrag() { + START_COM_CALL; + + @autoreleasepool { + auto lastEvent = [View lastMouseDownEvent]; + + if (lastEvent == nullptr) { + return S_OK; + } + + [Window performWindowDragWithEvent:lastEvent]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::BeginResizeDrag(AvnWindowEdge edge) { + START_COM_CALL; + + return S_OK; +} + +HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto frame = [Window frame]; + + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; + + *ret = ConvertPointY(*ret); + + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { + START_COM_CALL; + + @autoreleasepool { + lastPositionSet = point; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + point = ConvertPointY(point); + NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); + + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); + NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)]; + auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y)); + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; + } +} + +HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) { + START_COM_CALL; + + [View setSwRenderedFrame:fb dispose:dispose]; + return S_OK; +} + +HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) { + START_COM_CALL; + + @autoreleasepool { + Cursor *avnCursor = dynamic_cast(cursor); + this->cursor = avnCursor->GetNative(); + UpdateCursor(); + + if (avnCursor->IsHidden()) { + [NSCursor hide]; + } else { + [NSCursor unhide]; + } + + return S_OK; + } +} + +void WindowBaseImpl::UpdateCursor() { + if (cursor != nil) { + [cursor set]; + } +} + +HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *ppv = [renderTarget createSurfaceRenderTarget]; + return *ppv == nil ? E_FAIL : S_OK; +} + +HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *retOut = ::CreateNativeControlHost(View); + return S_OK; +} + +HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) { + START_COM_CALL; + + [StandardContainer ShowBlur:enable]; + + return S_OK; +} + +HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) { + START_COM_CALL; + + auto item = TryGetPasteboardItem(clipboard); + [item setString:@"" forType:GetAvnCustomDataType()]; + if (item == nil) + return E_INVALIDARG; + if (View == NULL) + return E_FAIL; + + auto nsevent = [NSApp currentEvent]; + auto nseventType = [nsevent type]; + + // If current event isn't a mouse one (probably due to malfunctioning user app) + // attempt to forge a new one + if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) + || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) { + NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); + CGPoint cgpoint = NSPointToCGPoint(nspoint); + auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); + nsevent = [NSEvent eventWithCGEvent:cgevent]; + CFRelease(cgevent); + } + + auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item]; + + auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; + NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height}; + [dragItem setDraggingFrame:dragItemRect contents:dragItemImage]; + + int op = 0; + int ieffects = (int) effects; + if ((ieffects & (int) AvnDragDropEffects::Copy) != 0) + op |= NSDragOperationCopy; + if ((ieffects & (int) AvnDragDropEffects::Link) != 0) + op |= NSDragOperationLink; + if ((ieffects & (int) AvnDragDropEffects::Move) != 0) + op |= NSDragOperationMove; + [View beginDraggingSessionWithItems:@[dragItem] event:nsevent + source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; + return S_OK; +} + +bool WindowBaseImpl::IsDialog() { + return false; +} + +NSWindowStyleMask WindowBaseImpl::GetStyle() { + return NSWindowStyleMaskBorderless; +} + +void WindowBaseImpl::UpdateStyle() { + [Window setStyleMask:GetStyle()]; +} + +void WindowBaseImpl::OnResized() { + +} diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 1369ceaea0..68dc673917 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -1,6 +1,7 @@ #ifndef window_h #define window_h + class WindowBaseImpl; @interface AvnView : NSView @@ -40,12 +41,6 @@ class WindowBaseImpl; -(bool) isDialog; @end -struct INSWindowHolder -{ - virtual AvnWindow* _Nonnull GetNSWindow () = 0; - virtual AvnView* _Nonnull GetNSView () = 0; -}; - struct IWindowStateChanged { virtual void WindowStateChanged () = 0; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 4426e7fdff..9676515b16 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,628 +1,11 @@ #include "common.h" -#include "window.h" +#import "window.h" #include "KeyTransform.h" -#include "cursor.h" #include "menu.h" -#include #include "rendertarget.h" -#include "AvnString.h" #include "automation.h" +#import "WindowBaseImpl.h" -class WindowBaseImpl : public virtual ComObject, - public virtual IAvnWindowBase, - public INSWindowHolder -{ -private: - NSCursor* cursor; - -public: - FORWARD_IUNKNOWN() - BEGIN_INTERFACE_MAP() - INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) - END_INTERFACE_MAP() - - virtual ~WindowBaseImpl() - { - View = NULL; - Window = NULL; - } - AutoFitContentView* StandardContainer; - AvnView* View; - AvnWindow* Window; - ComPtr BaseEvents; - ComPtr _glContext; - NSObject* renderTarget; - AvnPoint lastPositionSet; - NSString* _lastTitle; - IAvnMenu* _mainMenu; - - bool _shown; - bool _inResize; - - WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) - { - _shown = false; - _inResize = false; - _mainMenu = nullptr; - BaseEvents = events; - _glContext = gl; - renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl]; - View = [[AvnView alloc] initWithParent:this]; - StandardContainer = [[AutoFitContentView new] initWithContent:View]; - - Window = [[AvnWindow alloc] initWithParent:this]; - [Window setContentView: StandardContainer]; - - lastPositionSet.X = 100; - lastPositionSet.Y = 100; - _lastTitle = @""; - - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setOpaque:false]; - } - - virtual HRESULT ObtainNSWindowHandle(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge void*)Window; - - return S_OK; - } - - virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge_retained void*)Window; - - return S_OK; - } - - virtual HRESULT ObtainNSViewHandle(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge void*)View; - - return S_OK; - } - - virtual HRESULT ObtainNSViewHandleRetained(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge_retained void*)View; - - return S_OK; - } - - virtual AvnWindow* GetNSWindow() override - { - return Window; - } - - virtual AvnView* GetNSView() override - { - return View; - } - - virtual HRESULT Show(bool activate, bool isDialog) override - { - START_COM_CALL; - - @autoreleasepool - { - SetPosition(lastPositionSet); - UpdateStyle(); - - [Window setTitle:_lastTitle]; - - if(ShouldTakeFocusOnShow() && activate) - { - [Window orderFront: Window]; - [Window makeKeyAndOrderFront:Window]; - [Window makeFirstResponder:View]; - [NSApp activateIgnoringOtherApps:YES]; - } - else - { - [Window orderFront: Window]; - } - - _shown = true; - - return S_OK; - } - } - - virtual bool ShouldTakeFocusOnShow() - { - return true; - } - - virtual HRESULT Hide () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window != nullptr) - { - [Window orderOut:Window]; - [Window restoreParentWindow]; - } - - return S_OK; - } - } - - virtual HRESULT Activate () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window != nullptr) - { - [Window makeKeyAndOrderFront:nil]; - [NSApp activateIgnoringOtherApps:YES]; - } - } - - return S_OK; - } - - virtual HRESULT SetTopMost (bool value) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel]; - - return S_OK; - } - } - - virtual HRESULT Close() override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - auto window = Window; - Window = nullptr; - - try{ - // Seems to throw sometimes on application exit. - [window close]; - } - catch(NSException*){} - } - - return S_OK; - } - } - - virtual HRESULT GetClientSize(AvnSize* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - auto frame = [View frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - - return S_OK; - } - } - - virtual HRESULT GetFrameSize(AvnSize* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - auto frame = [Window frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - - return S_OK; - } - } - - virtual HRESULT GetScaling (double* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - if(Window == nullptr) - { - *ret = 1; - return S_OK; - } - - *ret = [Window backingScaleFactor]; - return S_OK; - } - } - - virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setMinSize: ToNSSize(minSize)]; - [Window setMaxSize: ToNSSize(maxSize)]; - - return S_OK; - } - } - - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - if(_inResize) - { - return S_OK; - } - - _inResize = true; - - START_COM_CALL; - auto resizeBlock = ResizeScope(View, reason); - - @autoreleasepool - { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; - - if (x < minSize.width) - { - x = minSize.width; - } - - if (y < minSize.height) - { - y = minSize.height; - } - - if (x > maxSize.width) - { - x = maxSize.width; - } - - if (y > maxSize.height) - { - y = maxSize.height; - } - - @try - { - if(!_shown) - { - BaseEvents->Resized(AvnSize{x,y}, reason); - } - - [Window setContentSize:NSSize{x,y}]; - [Window invalidateShadow]; - } - @finally - { - _inResize = false; - } - - return S_OK; - } - } - - virtual HRESULT Invalidate (AvnRect rect) override - { - START_COM_CALL; - - @autoreleasepool - { - [View setNeedsDisplayInRect:[View frame]]; - - return S_OK; - } - } - - virtual HRESULT SetMainMenu(IAvnMenu* menu) override - { - START_COM_CALL; - - _mainMenu = menu; - - auto nativeMenu = dynamic_cast(menu); - - auto nsmenu = nativeMenu->GetNative(); - - [Window applyMenu:nsmenu]; - - if ([Window isKeyWindow]) - { - [Window showWindowMenuWithAppMenu]; - } - - return S_OK; - } - - virtual HRESULT BeginMoveDrag () override - { - START_COM_CALL; - - @autoreleasepool - { - auto lastEvent = [View lastMouseDownEvent]; - - if(lastEvent == nullptr) - { - return S_OK; - } - - [Window performWindowDragWithEvent:lastEvent]; - - return S_OK; - } - } - - virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) override - { - START_COM_CALL; - - return S_OK; - } - - virtual HRESULT GetPosition (AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto frame = [Window frame]; - - ret->X = frame.origin.x; - ret->Y = frame.origin.y + frame.size.height; - - *ret = ConvertPointY(*ret); - - return S_OK; - } - } - - virtual HRESULT SetPosition (AvnPoint point) override - { - START_COM_CALL; - - @autoreleasepool - { - lastPositionSet = point; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; - - return S_OK; - } - } - - virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - point = ConvertPointY(point); - NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; - auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); - - *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; - - return S_OK; - } - } - - virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)]; - auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y)); - *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); - - return S_OK; - } - } - - virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) override - { - START_COM_CALL; - - [View setSwRenderedFrame: fb dispose: dispose]; - return S_OK; - } - - virtual HRESULT SetCursor(IAvnCursor* cursor) override - { - START_COM_CALL; - - @autoreleasepool - { - Cursor* avnCursor = dynamic_cast(cursor); - this->cursor = avnCursor->GetNative(); - UpdateCursor(); - - if(avnCursor->IsHidden()) - { - [NSCursor hide]; - } - else - { - [NSCursor unhide]; - } - - return S_OK; - } - } - - virtual void UpdateCursor() - { - if (cursor != nil) - { - [cursor set]; - } - } - - virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override - { - START_COM_CALL; - - if(View == NULL) - return E_FAIL; - *ppv = [renderTarget createSurfaceRenderTarget]; - return *ppv == nil ? E_FAIL : S_OK; - } - - virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override - { - START_COM_CALL; - - if(View == NULL) - return E_FAIL; - *retOut = ::CreateNativeControlHost(View); - return S_OK; - } - - virtual HRESULT SetBlurEnabled (bool enable) override - { - START_COM_CALL; - - [StandardContainer ShowBlur:enable]; - - return S_OK; - } - - virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, - IAvnClipboard* clipboard, IAvnDndResultCallback* cb, - void* sourceHandle) override - { - START_COM_CALL; - - auto item = TryGetPasteboardItem(clipboard); - [item setString:@"" forType:GetAvnCustomDataType()]; - if(item == nil) - return E_INVALIDARG; - if(View == NULL) - return E_FAIL; - - auto nsevent = [NSApp currentEvent]; - auto nseventType = [nsevent type]; - - // If current event isn't a mouse one (probably due to malfunctioning user app) - // attempt to forge a new one - if(!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) - || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) - { - NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; - auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); - CGPoint cgpoint = NSPointToCGPoint(nspoint); - auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); - nsevent = [NSEvent eventWithCGEvent: cgevent]; - CFRelease(cgevent); - } - - auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: item]; - - auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; - NSRect dragItemRect = {(float)point.X, (float)point.Y, [dragItemImage size].width, [dragItemImage size].height}; - [dragItem setDraggingFrame: dragItemRect contents: dragItemImage]; - - int op = 0; int ieffects = (int)effects; - if((ieffects & (int)AvnDragDropEffects::Copy) != 0) - op |= NSDragOperationCopy; - if((ieffects & (int)AvnDragDropEffects::Link) != 0) - op |= NSDragOperationLink; - if((ieffects & (int)AvnDragDropEffects::Move) != 0) - op |= NSDragOperationMove; - [View beginDraggingSessionWithItems: @[dragItem] event: nsevent - source: CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; - return S_OK; - } - - virtual bool IsDialog() - { - return false; - } - -protected: - virtual NSWindowStyleMask GetStyle() - { - return NSWindowStyleMaskBorderless; - } - - void UpdateStyle() - { - [Window setStyleMask: GetStyle()]; - } - -public: - virtual void OnResized () - { - - } -}; class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { From 6eb40ac09d1b38fd80b2ca4be7799f9d048dc42c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 17:40:33 +0100 Subject: [PATCH 548/820] make avalonia-native compile again with import instead of include. --- native/Avalonia.Native/src/OSX/SystemDialogs.mm | 1 + native/Avalonia.Native/src/OSX/automation.h | 2 +- native/Avalonia.Native/src/OSX/automation.mm | 4 ++-- native/Avalonia.Native/src/OSX/menu.mm | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index a47221056b..21ad9cfa7c 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,6 @@ #include "common.h" #include "window.h" +#include "INSWindowHolder.h" class SystemDialogs : public ComSingleObject { diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 4a12a965fd..79ccfd0eaa 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,5 @@ #import -#include "window.h" +#import "window.h" NS_ASSUME_NONNULL_BEGIN diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7d697140c2..226e8810c7 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,7 +1,7 @@ #include "common.h" -#include "automation.h" +#import "automation.h" #include "AvnString.h" -#include "window.h" +#import "INSWindowHolder.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 2dbe76bc6d..726e58478b 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -1,7 +1,7 @@ #include "common.h" #include "menu.h" -#include "window.h" +#import "window.h" #include "KeyTransform.h" #include #include /* For kVK_ constants, and TIS functions. */ From e2b76313b76fb261a7b92542660561ccfebd983d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:01:31 +0100 Subject: [PATCH 549/820] further seperate out files. --- .../project.pbxproj | 32 + .../src/OSX/IWindowStateChanged.h | 18 + native/Avalonia.Native/src/OSX/ResizeScope.h | 23 + native/Avalonia.Native/src/OSX/ResizeScope.mm | 17 + .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 + native/Avalonia.Native/src/OSX/WindowImpl.h | 99 +++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 546 ++++++++++++++ native/Avalonia.Native/src/OSX/automation.h | 1 - native/Avalonia.Native/src/OSX/automation.mm | 1 + native/Avalonia.Native/src/OSX/window.h | 30 +- native/Avalonia.Native/src/OSX/window.mm | 667 +----------------- 12 files changed, 741 insertions(+), 695 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/IWindowStateChanged.h create mode 100644 native/Avalonia.Native/src/OSX/ResizeScope.h create mode 100644 native/Avalonia.Native/src/OSX/ResizeScope.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowImpl.h create mode 100644 native/Avalonia.Native/src/OSX/WindowImpl.mm diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 7571d51c9f..2a206b0692 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -7,6 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; + 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; + 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; + 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; + 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; + 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; + 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -35,6 +43,14 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; + 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; + 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; + 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; + 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; + 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; + 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; + 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; }; @@ -130,6 +146,14 @@ 37C09D8721580FE4006A6758 /* SystemDialogs.mm */, AB7A61F02147C815003C5833 /* Products */, AB661C1C2148230E00291242 /* Frameworks */, + 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */, + 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */, + 18391BBB7782C296D424071F /* INSWindowHolder.h */, + 183919BF108EB72A029F7671 /* WindowImpl.mm */, + 18391CD090AA776E7E841AC9 /* WindowImpl.h */, + 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */, + 18391E45702740FE9DD69695 /* ResizeScope.mm */, + 1839171D898F9BFC1373631A /* ResizeScope.h */, ); sourceTree = ""; }; @@ -150,6 +174,11 @@ files = ( 37155CE4233C00EB0034DCE9 /* menu.h in Headers */, BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */, + 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */, + 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */, + 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */, + 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, + 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -229,6 +258,9 @@ AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, AB661C202148286E00291242 /* window.mm in Sources */, + 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, + 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, + 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/IWindowStateChanged.h b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h new file mode 100644 index 0000000000..f0905da3ac --- /dev/null +++ b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h @@ -0,0 +1,18 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H +#define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H + +struct IWindowStateChanged +{ + virtual void WindowStateChanged () = 0; + virtual void StartStateTransition () = 0; + virtual void EndStateTransition () = 0; + virtual SystemDecorations Decorations () = 0; + virtual AvnWindowState WindowState () = 0; +}; + +#endif //AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h new file mode 100644 index 0000000000..c57dc96690 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -0,0 +1,23 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H +#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H + +#include "window.h" +#include "avalonia-native.h" + +class ResizeScope +{ +public: + ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason); + + ~ResizeScope(); +private: + AvnView* _Nonnull _view; + AvnPlatformResizeReason _restore; +}; + +#endif //AVALONIA_NATIVE_OSX_RESIZESCOPE_H diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm new file mode 100644 index 0000000000..8644b41fba --- /dev/null +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -0,0 +1,17 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#include "ResizeScope.h" + +ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { + _view = view; + _restore = [view getResizeReason]; + [view setResizeReason:reason]; +} + +ResizeScope::~ResizeScope() { + [_view setResizeReason:_restore]; +} diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 2f9b05988f..ec013657dc 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -6,6 +6,7 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H +#import "rendertarget.h" #include "INSWindowHolder.h" class WindowBaseImpl : public virtual ComObject, diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 482b6172af..bb40cd2a7e 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -11,6 +11,7 @@ #include "automation.h" #import "WindowBaseImpl.h" #import "cursor.h" +#include "ResizeScope.h" WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h new file mode 100644 index 0000000000..b16e72fb32 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -0,0 +1,99 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H +#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H + + +#import "WindowBaseImpl.h" +#include "IWindowStateChanged.h" + +class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged +{ +private: + bool _canResize; + bool _fullScreenActive; + SystemDecorations _decorations; + AvnWindowState _lastWindowState; + AvnWindowState _actualWindowState; + bool _inSetWindowState; + NSRect _preZoomSize; + bool _transitioningWindowState; + bool _isClientAreaExtended; + bool _isDialog; + AvnExtendClientAreaChromeHints _extendClientHints; + + FORWARD_IUNKNOWN() +BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) + END_INTERFACE_MAP() + virtual ~WindowImpl() + { + } + + ComPtr WindowEvents; + + WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl); + + void HideOrShowTrafficLights (); + + virtual HRESULT Show (bool activate, bool isDialog) override; + + virtual HRESULT SetEnabled (bool enable) override; + + virtual HRESULT SetParent (IAvnWindow* parent) override; + + void StartStateTransition (); + + void EndStateTransition (); + + SystemDecorations Decorations (); + + AvnWindowState WindowState (); + + void WindowStateChanged (); + + bool UndecoratedIsMaximized (); + + bool IsZoomed (); + + void DoZoom(); + + virtual HRESULT SetCanResize(bool value) override; + + virtual HRESULT SetDecorations(SystemDecorations value) override; + + virtual HRESULT SetTitle (char* utf8title) override; + + virtual HRESULT SetTitleBarColor(AvnColor color) override; + + virtual HRESULT GetWindowState (AvnWindowState*ret) override; + + virtual HRESULT TakeFocusFromChildren () override; + + virtual HRESULT SetExtendClientArea (bool enable) override; + + virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override; + + virtual HRESULT GetExtendTitleBarHeight (double*ret) override; + + virtual HRESULT SetExtendTitleBarHeight (double value) override; + + void EnterFullScreenMode (); + + void ExitFullScreenMode (); + + virtual HRESULT SetWindowState (AvnWindowState state) override; + + virtual void OnResized () override; + + virtual bool IsDialog() override; + +protected: + virtual NSWindowStyleMask GetStyle() override; +}; + +#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm new file mode 100644 index 0000000000..0b7f9e0a13 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -0,0 +1,546 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "window.h" +#include "automation.h" +#include "menu.h" +#import "WindowImpl.h" + + +WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { + _isClientAreaExtended = false; + _extendClientHints = AvnDefaultChrome; + _fullScreenActive = false; + _canResize = true; + _decorations = SystemDecorationsFull; + _transitioningWindowState = false; + _inSetWindowState = false; + _lastWindowState = Normal; + _actualWindowState = Normal; + WindowEvents = events; + [Window setCanBecomeKeyAndMain]; + [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; +} + +void WindowImpl::HideOrShowTrafficLights() { + if (Window == nil) { + return; + } + + for (id subview in Window.contentView.superview.subviews) { + if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { + NSView *titlebarView = [subview subviews][0]; + for (id button in titlebarView.subviews) { + if ([button isKindOfClass:[NSButton class]]) { + if (_isClientAreaExtended) { + auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + [button setHidden:!wantsChrome]; + } else { + [button setHidden:(_decorations != SystemDecorationsFull)]; + } + + [button setWantsLayer:true]; + } + } + } + } +} + +HRESULT WindowImpl::Show(bool activate, bool isDialog) { + START_COM_CALL; + + @autoreleasepool { + _isDialog = isDialog; + WindowBaseImpl::Show(activate, isDialog); + + HideOrShowTrafficLights(); + + return SetWindowState(_lastWindowState); + } +} + +HRESULT WindowImpl::SetEnabled(bool enable) { + START_COM_CALL; + + @autoreleasepool { + [Window setEnabled:enable]; + return S_OK; + } +} + +HRESULT WindowImpl::SetParent(IAvnWindow *parent) { + START_COM_CALL; + + @autoreleasepool { + if (parent == nullptr) + return E_POINTER; + + auto cparent = dynamic_cast(parent); + if (cparent == nullptr) + 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 + // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. + if (cparent->WindowState() == Minimized) + cparent->SetWindowState(Normal); + + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; + + UpdateStyle(); + + return S_OK; + } +} + +void WindowImpl::StartStateTransition() { + _transitioningWindowState = true; +} + +void WindowImpl::EndStateTransition() { + _transitioningWindowState = false; +} + +SystemDecorations WindowImpl::Decorations() { + return _decorations; +} + +AvnWindowState WindowImpl::WindowState() { + return _lastWindowState; +} + +void WindowImpl::WindowStateChanged() { + if (_shown && !_inSetWindowState && !_transitioningWindowState) { + AvnWindowState state; + GetWindowState(&state); + + if (_lastWindowState != state) { + if (_isClientAreaExtended) { + if (_lastWindowState == FullScreen) { + // we exited fs. + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } + + [Window setTitlebarAppearsTransparent:true]; + + [StandardContainer setFrameSize:StandardContainer.frame.size]; + } else if (state == FullScreen) { + // we entered fs. + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = nullptr; + } + + [Window setTitlebarAppearsTransparent:false]; + + [StandardContainer setFrameSize:StandardContainer.frame.size]; + } + } + + _lastWindowState = state; + _actualWindowState = state; + WindowEvents->WindowStateChanged(state); + } + } +} + +bool WindowImpl::UndecoratedIsMaximized() { + auto windowSize = [Window frame]; + auto available = [Window screen].visibleFrame; + return CGRectEqualToRect(windowSize, available); +} + +bool WindowImpl::IsZoomed() { + return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); +} + +void WindowImpl::DoZoom() { + switch (_decorations) { + case SystemDecorationsNone: + case SystemDecorationsBorderOnly: + [Window setFrame:[Window screen].visibleFrame display:true]; + break; + + + case SystemDecorationsFull: + [Window performZoom:Window]; + break; + } +} + +HRESULT WindowImpl::SetCanResize(bool value) { + START_COM_CALL; + + @autoreleasepool { + _canResize = value; + UpdateStyle(); + return S_OK; + } +} + +HRESULT WindowImpl::SetDecorations(SystemDecorations value) { + START_COM_CALL; + + @autoreleasepool { + auto currentWindowState = _lastWindowState; + _decorations = value; + + if (_fullScreenActive) { + return S_OK; + } + + UpdateStyle(); + + HideOrShowTrafficLights(); + + switch (_decorations) { + case SystemDecorationsNone: + [Window setHasShadow:NO]; + [Window setTitleVisibility:NSWindowTitleHidden]; + [Window setTitlebarAppearsTransparent:YES]; + + if (currentWindowState == Maximized) { + if (!UndecoratedIsMaximized()) { + DoZoom(); + } + } + break; + + case SystemDecorationsBorderOnly: + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleHidden]; + [Window setTitlebarAppearsTransparent:YES]; + + if (currentWindowState == Maximized) { + if (!UndecoratedIsMaximized()) { + DoZoom(); + } + } + break; + + case SystemDecorationsFull: + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleVisible]; + [Window setTitlebarAppearsTransparent:NO]; + [Window setTitle:_lastTitle]; + + if (currentWindowState == Maximized) { + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + break; + } + + return S_OK; + } +} + +HRESULT WindowImpl::SetTitle(char *utf8title) { + START_COM_CALL; + + @autoreleasepool { + _lastTitle = [NSString stringWithUTF8String:(const char *) utf8title]; + [Window setTitle:_lastTitle]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetTitleBarColor(AvnColor color) { + START_COM_CALL; + + @autoreleasepool { + float a = (float) color.Alpha / 255.0f; + float r = (float) color.Red / 255.0f; + float g = (float) color.Green / 255.0f; + float b = (float) color.Blue / 255.0f; + + auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + + // Based on the titlebar color we have to choose either light or dark + // OSX doesnt let you set a foreground color for titlebar. + if ((r * 0.299 + g * 0.587 + b * 0.114) > 186.0f / 255.0f) { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; + } else { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; + } + + [Window setTitlebarAppearsTransparent:true]; + [Window setBackgroundColor:nscolor]; + } + + return S_OK; +} + +HRESULT WindowImpl::GetWindowState(AvnWindowState *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + if (([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) { + *ret = FullScreen; + return S_OK; + } + + if ([Window isMiniaturized]) { + *ret = Minimized; + return S_OK; + } + + if (IsZoomed()) { + *ret = Maximized; + return S_OK; + } + + *ret = Normal; + + return S_OK; + } +} + +HRESULT WindowImpl::TakeFocusFromChildren() { + START_COM_CALL; + + @autoreleasepool { + if (Window == nil) + return S_OK; + if ([Window isKeyWindow]) + [Window makeFirstResponder:View]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendClientArea(bool enable) { + START_COM_CALL; + + @autoreleasepool { + _isClientAreaExtended = enable; + + if (enable) { + Window.titleVisibility = NSWindowTitleHidden; + + [Window setTitlebarAppearsTransparent:true]; + + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + if (wantsTitleBar) { + [StandardContainer ShowTitleBar:true]; + } else { + [StandardContainer ShowTitleBar:false]; + } + + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } else { + Window.toolbar = nullptr; + } + } else { + Window.titleVisibility = NSWindowTitleVisible; + Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; + } + + [Window setIsExtended:enable]; + + HideOrShowTrafficLights(); + + UpdateStyle(); + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints) { + START_COM_CALL; + + @autoreleasepool { + _extendClientHints = hints; + + SetExtendClientArea(_isClientAreaExtended); + return S_OK; + } +} + +HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + *ret = [Window getExtendedTitleBarHeight]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendTitleBarHeight(double value) { + START_COM_CALL; + + @autoreleasepool { + [StandardContainer SetTitleBarHeightHint:value]; + return S_OK; + } +} + +void WindowImpl::EnterFullScreenMode() { + _fullScreenActive = true; + + [Window setTitle:_lastTitle]; + [Window toggleFullScreen:nullptr]; +} + +void WindowImpl::ExitFullScreenMode() { + [Window toggleFullScreen:nullptr]; + + _fullScreenActive = false; + + SetDecorations(_decorations); +} + +HRESULT WindowImpl::SetWindowState(AvnWindowState state) { + START_COM_CALL; + + @autoreleasepool { + if (Window == nullptr) { + return S_OK; + } + + if (_actualWindowState == state) { + return S_OK; + } + + _inSetWindowState = true; + + auto currentState = _actualWindowState; + _lastWindowState = state; + + if (currentState == Normal) { + _preZoomSize = [Window frame]; + } + + if (_shown) { + switch (state) { + case Maximized: + if (currentState == FullScreen) { + ExitFullScreenMode(); + } + + lastPositionSet.X = 0; + lastPositionSet.Y = 0; + + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + if (!IsZoomed()) { + DoZoom(); + } + break; + + case Minimized: + if (currentState == FullScreen) { + ExitFullScreenMode(); + } else { + [Window miniaturize:Window]; + } + break; + + case FullScreen: + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + EnterFullScreenMode(); + break; + + case Normal: + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + if (currentState == FullScreen) { + ExitFullScreenMode(); + } + + if (IsZoomed()) { + if (_decorations == SystemDecorationsFull) { + DoZoom(); + } else { + [Window setFrame:_preZoomSize display:true]; + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + + } + break; + } + + _actualWindowState = _lastWindowState; + WindowEvents->WindowStateChanged(_actualWindowState); + } + + + _inSetWindowState = false; + + return S_OK; + } +} + +void WindowImpl::OnResized() { + if (_shown && !_inSetWindowState && !_transitioningWindowState) { + WindowStateChanged(); + } +} + +bool WindowImpl::IsDialog() { + return _isDialog; +} + +NSWindowStyleMask WindowImpl::GetStyle() { + unsigned long s = NSWindowStyleMaskBorderless; + + switch (_decorations) { + case SystemDecorationsNone: + s = s | NSWindowStyleMaskFullSizeContentView; + break; + + case SystemDecorationsBorderOnly: + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; + break; + + case SystemDecorationsFull: + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; + + if (_canResize) { + s = s | NSWindowStyleMaskResizable; + } + break; + } + + if ([Window parentWindow] == nullptr) { + s |= NSWindowStyleMaskMiniaturizable; + } + + if (_isClientAreaExtended) { + s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; + } + return s; +} diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 79ccfd0eaa..1727d21f27 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,4 @@ #import -#import "window.h" NS_ASSUME_NONNULL_BEGIN diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 226e8810c7..087d15a248 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,5 +1,6 @@ #include "common.h" #import "automation.h" +#import "window.h" #include "AvnString.h" #import "INSWindowHolder.h" diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 68dc673917..365172622f 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -1,7 +1,7 @@ #ifndef window_h #define window_h - +#import "avalonia-native.h" class WindowBaseImpl; @interface AvnView : NSView @@ -41,32 +41,4 @@ class WindowBaseImpl; -(bool) isDialog; @end -struct IWindowStateChanged -{ - virtual void WindowStateChanged () = 0; - virtual void StartStateTransition () = 0; - virtual void EndStateTransition () = 0; - virtual SystemDecorations Decorations () = 0; - virtual AvnWindowState WindowState () = 0; -}; - -class ResizeScope -{ -public: - ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason) - { - _view = view; - _restore = [view getResizeReason]; - [view setResizeReason:reason]; - } - - ~ResizeScope() - { - [_view setResizeReason:_restore]; - } -private: - AvnView* _Nonnull _view; - AvnPlatformResizeReason _restore; -}; - #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 9676515b16..a1d231f30a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -5,673 +5,10 @@ #include "rendertarget.h" #include "automation.h" #import "WindowBaseImpl.h" +#include "WindowImpl.h" +#include "IWindowStateChanged.h" -class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged -{ -private: - bool _canResize; - bool _fullScreenActive; - SystemDecorations _decorations; - AvnWindowState _lastWindowState; - AvnWindowState _actualWindowState; - bool _inSetWindowState; - NSRect _preZoomSize; - bool _transitioningWindowState; - bool _isClientAreaExtended; - bool _isDialog; - AvnExtendClientAreaChromeHints _extendClientHints; - - FORWARD_IUNKNOWN() - BEGIN_INTERFACE_MAP() - INHERIT_INTERFACE_MAP(WindowBaseImpl) - INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) - END_INTERFACE_MAP() - virtual ~WindowImpl() - { - } - - ComPtr WindowEvents; - WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) - { - _isClientAreaExtended = false; - _extendClientHints = AvnDefaultChrome; - _fullScreenActive = false; - _canResize = true; - _decorations = SystemDecorationsFull; - _transitioningWindowState = false; - _inSetWindowState = false; - _lastWindowState = Normal; - _actualWindowState = Normal; - WindowEvents = events; - [Window setCanBecomeKeyAndMain]; - [Window disableCursorRects]; - [Window setTabbingMode:NSWindowTabbingModeDisallowed]; - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - } - - void HideOrShowTrafficLights () - { - if (Window == nil) - { - return; - } - - for (id subview in Window.contentView.superview.subviews) { - if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { - NSView *titlebarView = [subview subviews][0]; - for (id button in titlebarView.subviews) { - if ([button isKindOfClass:[NSButton class]]) - { - if(_isClientAreaExtended) - { - auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - [button setHidden: !wantsChrome]; - } - else - { - [button setHidden: (_decorations != SystemDecorationsFull)]; - } - - [button setWantsLayer:true]; - } - } - } - } - } - - virtual HRESULT Show (bool activate, bool isDialog) override - { - START_COM_CALL; - - @autoreleasepool - { - _isDialog = isDialog; - WindowBaseImpl::Show(activate, isDialog); - - HideOrShowTrafficLights(); - - return SetWindowState(_lastWindowState); - } - } - - virtual HRESULT SetEnabled (bool enable) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setEnabled:enable]; - return S_OK; - } - } - - virtual HRESULT SetParent (IAvnWindow* parent) override - { - START_COM_CALL; - - @autoreleasepool - { - if(parent == nullptr) - return E_POINTER; - - auto cparent = dynamic_cast(parent); - if(cparent == nullptr) - 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 - // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. - if (cparent->WindowState() == Minimized) - cparent->SetWindowState(Normal); - - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; - - UpdateStyle(); - - return S_OK; - } - } - - void StartStateTransition () override - { - _transitioningWindowState = true; - } - - void EndStateTransition () override - { - _transitioningWindowState = false; - } - - SystemDecorations Decorations () override - { - return _decorations; - } - - AvnWindowState WindowState () override - { - return _lastWindowState; - } - - void WindowStateChanged () override - { - if(_shown && !_inSetWindowState && !_transitioningWindowState) - { - AvnWindowState state; - GetWindowState(&state); - - if(_lastWindowState != state) - { - if(_isClientAreaExtended) - { - if(_lastWindowState == FullScreen) - { - // we exited fs. - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; - } - - [Window setTitlebarAppearsTransparent:true]; - - [StandardContainer setFrameSize: StandardContainer.frame.size]; - } - else if(state == FullScreen) - { - // we entered fs. - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = nullptr; - } - - [Window setTitlebarAppearsTransparent:false]; - - [StandardContainer setFrameSize: StandardContainer.frame.size]; - } - } - - _lastWindowState = state; - _actualWindowState = state; - WindowEvents->WindowStateChanged(state); - } - } - } - - bool UndecoratedIsMaximized () - { - auto windowSize = [Window frame]; - auto available = [Window screen].visibleFrame; - return CGRectEqualToRect(windowSize, available); - } - - bool IsZoomed () - { - return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); - } - - void DoZoom() - { - switch (_decorations) - { - case SystemDecorationsNone: - case SystemDecorationsBorderOnly: - [Window setFrame:[Window screen].visibleFrame display:true]; - break; - - - case SystemDecorationsFull: - [Window performZoom:Window]; - break; - } - } - - virtual HRESULT SetCanResize(bool value) override - { - START_COM_CALL; - - @autoreleasepool - { - _canResize = value; - UpdateStyle(); - return S_OK; - } - } - - virtual HRESULT SetDecorations(SystemDecorations value) override - { - START_COM_CALL; - - @autoreleasepool - { - auto currentWindowState = _lastWindowState; - _decorations = value; - - if(_fullScreenActive) - { - return S_OK; - } - - UpdateStyle(); - - HideOrShowTrafficLights(); - - switch (_decorations) - { - case SystemDecorationsNone: - [Window setHasShadow:NO]; - [Window setTitleVisibility:NSWindowTitleHidden]; - [Window setTitlebarAppearsTransparent:YES]; - - if(currentWindowState == Maximized) - { - if(!UndecoratedIsMaximized()) - { - DoZoom(); - } - } - break; - - case SystemDecorationsBorderOnly: - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleHidden]; - [Window setTitlebarAppearsTransparent:YES]; - - if(currentWindowState == Maximized) - { - if(!UndecoratedIsMaximized()) - { - DoZoom(); - } - } - break; - - case SystemDecorationsFull: - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleVisible]; - [Window setTitlebarAppearsTransparent:NO]; - [Window setTitle:_lastTitle]; - - if(currentWindowState == Maximized) - { - auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; - - [View setFrameSize:newFrame]; - } - break; - } - - return S_OK; - } - } - - virtual HRESULT SetTitle (char* utf8title) override - { - START_COM_CALL; - - @autoreleasepool - { - _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title]; - [Window setTitle:_lastTitle]; - - return S_OK; - } - } - - virtual HRESULT SetTitleBarColor(AvnColor color) override - { - START_COM_CALL; - - @autoreleasepool - { - float a = (float)color.Alpha / 255.0f; - float r = (float)color.Red / 255.0f; - float g = (float)color.Green / 255.0f; - float b = (float)color.Blue / 255.0f; - - auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; - - // Based on the titlebar color we have to choose either light or dark - // OSX doesnt let you set a foreground color for titlebar. - if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f) - { - [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; - } - else - { - [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; - } - - [Window setTitlebarAppearsTransparent:true]; - [Window setBackgroundColor:nscolor]; - } - - return S_OK; - } - - virtual HRESULT GetWindowState (AvnWindowState*ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) - { - *ret = FullScreen; - return S_OK; - } - - if([Window isMiniaturized]) - { - *ret = Minimized; - return S_OK; - } - - if(IsZoomed()) - { - *ret = Maximized; - return S_OK; - } - - *ret = Normal; - - return S_OK; - } - } - - virtual HRESULT TakeFocusFromChildren () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window == nil) - return S_OK; - if([Window isKeyWindow]) - [Window makeFirstResponder: View]; - - return S_OK; - } - } - - virtual HRESULT SetExtendClientArea (bool enable) override - { - START_COM_CALL; - - @autoreleasepool - { - _isClientAreaExtended = enable; - - if(enable) - { - Window.titleVisibility = NSWindowTitleHidden; - - [Window setTitlebarAppearsTransparent:true]; - - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - if (wantsTitleBar) - { - [StandardContainer ShowTitleBar:true]; - } - else - { - [StandardContainer ShowTitleBar:false]; - } - - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; - } - else - { - Window.toolbar = nullptr; - } - } - else - { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - - [Window setIsExtended:enable]; - - HideOrShowTrafficLights(); - - UpdateStyle(); - - return S_OK; - } - } - - virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override - { - START_COM_CALL; - - @autoreleasepool - { - _extendClientHints = hints; - - SetExtendClientArea(_isClientAreaExtended); - return S_OK; - } - } - - virtual HRESULT GetExtendTitleBarHeight (double*ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - *ret = [Window getExtendedTitleBarHeight]; - - return S_OK; - } - } - - virtual HRESULT SetExtendTitleBarHeight (double value) override - { - START_COM_CALL; - - @autoreleasepool - { - [StandardContainer SetTitleBarHeightHint:value]; - return S_OK; - } - } - - void EnterFullScreenMode () - { - _fullScreenActive = true; - - [Window setTitle:_lastTitle]; - [Window toggleFullScreen:nullptr]; - } - - void ExitFullScreenMode () - { - [Window toggleFullScreen:nullptr]; - - _fullScreenActive = false; - - SetDecorations(_decorations); - } - - virtual HRESULT SetWindowState (AvnWindowState state) override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window == nullptr) - { - return S_OK; - } - - if(_actualWindowState == state) - { - return S_OK; - } - - _inSetWindowState = true; - - auto currentState = _actualWindowState; - _lastWindowState = state; - - if(currentState == Normal) - { - _preZoomSize = [Window frame]; - } - - if(_shown) - { - switch (state) { - case Maximized: - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - - lastPositionSet.X = 0; - lastPositionSet.Y = 0; - - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - if(!IsZoomed()) - { - DoZoom(); - } - break; - - case Minimized: - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - else - { - [Window miniaturize:Window]; - } - break; - - case FullScreen: - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - EnterFullScreenMode(); - break; - - case Normal: - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - - if(IsZoomed()) - { - if(_decorations == SystemDecorationsFull) - { - DoZoom(); - } - else - { - [Window setFrame:_preZoomSize display:true]; - auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; - - [View setFrameSize:newFrame]; - } - - } - break; - } - - _actualWindowState = _lastWindowState; - WindowEvents->WindowStateChanged(_actualWindowState); - } - - - _inSetWindowState = false; - - return S_OK; - } - } - - virtual void OnResized () override - { - if(_shown && !_inSetWindowState && !_transitioningWindowState) - { - WindowStateChanged(); - } - } - - virtual bool IsDialog() override - { - return _isDialog; - } - -protected: - virtual NSWindowStyleMask GetStyle() override - { - unsigned long s = NSWindowStyleMaskBorderless; - - switch (_decorations) - { - case SystemDecorationsNone: - s = s | NSWindowStyleMaskFullSizeContentView; - break; - - case SystemDecorationsBorderOnly: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; - break; - - case SystemDecorationsFull: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; - - if(_canResize) - { - s = s | NSWindowStyleMaskResizable; - } - break; - } - - if([Window parentWindow] == nullptr) - { - s |= NSWindowStyleMaskMiniaturizable; - } - - if(_isClientAreaExtended) - { - s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; - } - return s; - } -}; - NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; @implementation AutoFitContentView From 947590d453ee9a3490088d09f4cbca3edebee74f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:10:07 +0100 Subject: [PATCH 550/820] fix warnings --- native/Avalonia.Native/src/OSX/WindowImpl.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index b16e72fb32..fae53acc22 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -46,15 +46,15 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetParent (IAvnWindow* parent) override; - void StartStateTransition (); + void StartStateTransition () override ; - void EndStateTransition (); + void EndStateTransition () override ; - SystemDecorations Decorations (); + SystemDecorations Decorations () override ; - AvnWindowState WindowState (); + AvnWindowState WindowState () override ; - void WindowStateChanged (); + void WindowStateChanged () override ; bool UndecoratedIsMaximized (); From 68b4173743353b9901fbca06d4a41add3adbb5ae Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:39:30 +0100 Subject: [PATCH 551/820] remove unused window code. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 7 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 15 ++-- native/Avalonia.Native/src/OSX/WindowImpl.h | 2 - native/Avalonia.Native/src/OSX/WindowImpl.mm | 6 -- native/Avalonia.Native/src/OSX/window.h | 12 ++-- native/Avalonia.Native/src/OSX/window.mm | 68 ++++++------------- 6 files changed, 32 insertions(+), 78 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index ec013657dc..94175b8187 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -35,7 +35,6 @@ BEGIN_INTERFACE_MAP() NSObject *renderTarget; AvnPoint lastPositionSet; NSString *_lastTitle; - IAvnMenu *_mainMenu; bool _shown; bool _inResize; @@ -76,13 +75,13 @@ BEGIN_INTERFACE_MAP() virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override; - virtual HRESULT Invalidate(AvnRect rect) override; + virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override; virtual HRESULT SetMainMenu(IAvnMenu *menu) override; virtual HRESULT BeginMoveDrag() override; - virtual HRESULT BeginResizeDrag(AvnWindowEdge edge) override; + virtual HRESULT BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) override; virtual HRESULT GetPosition(AvnPoint *ret) override; @@ -115,8 +114,6 @@ protected: void UpdateStyle(); -public: - virtual void OnResized(); }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bb40cd2a7e..9959d6a034 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -16,7 +16,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; _inResize = false; - _mainMenu = nullptr; BaseEvents = events; _glContext = gl; renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; @@ -279,7 +278,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso } } -HRESULT WindowBaseImpl::Invalidate(AvnRect rect) { +HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) { START_COM_CALL; @autoreleasepool { @@ -292,8 +291,6 @@ HRESULT WindowBaseImpl::Invalidate(AvnRect rect) { HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { START_COM_CALL; - _mainMenu = menu; - auto nativeMenu = dynamic_cast(menu); auto nsmenu = nativeMenu->GetNative(); @@ -323,7 +320,7 @@ HRESULT WindowBaseImpl::BeginMoveDrag() { } } -HRESULT WindowBaseImpl::BeginResizeDrag(AvnWindowEdge edge) { +HRESULT WindowBaseImpl::BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) { START_COM_CALL; return S_OK; @@ -431,7 +428,7 @@ HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { if (View == NULL) return E_FAIL; *ppv = [renderTarget createSurfaceRenderTarget]; - return *ppv == nil ? E_FAIL : S_OK; + return static_cast(*ppv == nil ? E_FAIL : S_OK); } HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { @@ -505,8 +502,4 @@ NSWindowStyleMask WindowBaseImpl::GetStyle() { void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; -} - -void WindowBaseImpl::OnResized() { - -} +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index fae53acc22..b229921baa 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -88,8 +88,6 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetWindowState (AvnWindowState state) override; - virtual void OnResized () override; - virtual bool IsDialog() override; protected: diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 0b7f9e0a13..9cf5160c97 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -504,12 +504,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) { } } -void WindowImpl::OnResized() { - if (_shown && !_inSetWindowState && !_transitioningWindowState) { - WindowStateChanged(); - } -} - bool WindowImpl::IsDialog() { return _isDialog; } diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 365172622f..271dd2534e 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -2,6 +2,9 @@ #define window_h #import "avalonia-native.h" + +@class AvnMenu; + class WindowBaseImpl; @interface AvnView : NSView @@ -10,7 +13,7 @@ class WindowBaseImpl; -(AvnPoint) translateLocalPoint:(AvnPoint)pt; -(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; -(void) onClosed; --(AvnPixelSize) getPixelSize; + -(AvnPlatformResizeReason) getResizeReason; -(void) setResizeReason:(AvnPlatformResizeReason)reason; + (AvnPoint)toAvnPoint:(CGPoint)p; @@ -20,12 +23,11 @@ class WindowBaseImpl; -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; -(void) ShowTitleBar: (bool) show; -(void) SetTitleBarHeightHint: (double) height; --(void) SetContent: (NSView* _Nonnull) content; + -(void) ShowBlur: (bool) show; @end @interface AvnWindow : NSWindow -+(void) closeAll; -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; @@ -34,8 +36,8 @@ class WindowBaseImpl; -(void) setEnabled: (bool) enable; -(void) showAppMenuOnly; -(void) showWindowMenuWithAppMenu; --(void) applyMenu:(NSMenu* _Nullable)menu; --(double) getScaling; +-(void) applyMenu:(AvnMenu* _Nullable)menu; + -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; -(bool) isDialog; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index a1d231f30a..fe80142f1a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,3 +1,4 @@ +#import #include "common.h" #import "window.h" #include "KeyTransform.h" @@ -6,10 +7,6 @@ #include "automation.h" #import "WindowBaseImpl.h" #include "WindowImpl.h" -#include "IWindowStateChanged.h" - - -NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; @implementation AutoFitContentView { @@ -106,26 +103,13 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _settingSize = false; } - --(void) SetContent: (NSView* _Nonnull) content -{ - if(content != nullptr) - { - [content removeFromSuperview]; - [self addSubview:content]; - _content = content; - } -} @end @implementation AvnView { ComPtr _parent; - ComPtr _swRenderedFrame; - AvnFramebuffer _swRenderedFrameBuffer; - bool _queuedDisplayFromThread; NSTrackingArea* _area; - bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed, _isMouseOver; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; AvnInputModifiers _modifierState; NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; @@ -143,11 +127,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } --(AvnPixelSize) getPixelSize -{ - return _lastPixelSize; -} - - (NSEvent*) lastMouseDownEvent { return _lastMouseDownEvent; @@ -155,7 +134,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void) updateRenderTarget { - [_renderTarget resize:_lastPixelSize withScale: [[self window] backingScaleFactor]]; + [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; [self setNeedsDisplayInRect:[self frame]]; } @@ -345,7 +324,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; auto avnPoint = [AvnView toAvnPoint:localPoint]; auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta; + AvnVector delta = { 0, 0}; if(type == Wheel) { @@ -378,7 +357,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent delta.Y = [event deltaY]; } - auto timestamp = [event timestamp] * 1000; + uint32 timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; if(type != AvnRawMouseEventType::Move || @@ -437,6 +416,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _isXButton2Pressed = true; [self mouseEvent:event withType:XButton2Down]; break; + + default: + break; } } @@ -470,6 +452,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _isXButton2Pressed = false; [self mouseEvent:event withType:XButton2Up]; break; + + default: + break; } } @@ -523,13 +508,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)mouseEntered:(NSEvent *)event { - _isMouseOver = true; [super mouseEntered:event]; } - (void)mouseExited:(NSEvent *)event { - _isMouseOver = false; [self mouseEvent:event withType:LeaveWindow]; [super mouseExited:event]; } @@ -543,7 +526,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto key = s_KeyMap[[event keyCode]]; - auto timestamp = [event timestamp] * 1000; + uint32_t timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; if(_parent != nullptr) @@ -711,7 +694,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - CGRect result; + CGRect result = { 0 }; return result; } @@ -730,10 +713,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent CreateClipboard([info draggingPasteboard], nil), GetAvnDataObjectHandleFromDraggingInfo(info)); - NSDragOperation ret = 0; + NSDragOperation ret = static_cast(0); // Ensure that the managed part didn't add any new effects - reffects = (int)effects & (int)reffects; + reffects = (int)effects & reffects; // OSX requires exactly one operation if((reffects & (int)AvnDragDropEffects::Copy) != 0) @@ -829,9 +812,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent bool _isEnabled; bool _isExtended; AvnMenu* _menu; - double _lastScaling; - IAvnAutomationPeer* _automationPeer; - NSMutableArray* _automationChildren; } -(void) setIsExtended:(bool)value; @@ -844,11 +824,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return _parent->IsDialog(); } --(double) getScaling -{ - return _lastScaling; -} - -(double) getExtendedTitleBarHeight { if(_isExtended) @@ -871,11 +846,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } -+(void)closeAll -{ - [[NSApplication sharedApplication] terminate:self]; -} - - (void)performClose:(id)sender { if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) @@ -983,7 +953,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _closed = false; _isEnabled = true; - _lastScaling = [self backingScaleFactor]; + [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; _isExtended = false; @@ -1004,7 +974,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)windowDidChangeBackingProperties:(NSNotification *)notification { - _lastScaling = [self backingScaleFactor]; + [self backingScaleFactor]; } - (void)windowWillClose:(NSNotification *)notification @@ -1221,9 +1191,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent { auto avnPoint = [AvnView toAvnPoint:windowPoint]; auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta; + AvnVector delta = { 0, 0 }; - _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, [event timestamp] * 1000, AvnInputModifiersNone, point, delta); + _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } } break; From 68af00ef0e7f7eee34ee1f5dfc0e113e04a4128e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:40:08 +0100 Subject: [PATCH 552/820] remove unused code in other classes. --- native/Avalonia.Native/src/OSX/app.mm | 12 ------------ native/Avalonia.Native/src/OSX/common.h | 3 +-- native/Avalonia.Native/src/OSX/cursor.mm | 1 - native/Avalonia.Native/src/OSX/main.mm | 6 +----- native/Avalonia.Native/src/OSX/menu.h | 1 - native/Avalonia.Native/src/OSX/menu.mm | 5 ++--- native/Avalonia.Native/src/OSX/rendertarget.mm | 4 ---- 7 files changed, 4 insertions(+), 28 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 05b129baca..14f1f6888c 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -82,18 +82,6 @@ ComPtr _events; _isHandlingSendEvent = oldHandling; } } - -// This is needed for certain embedded controls -- (BOOL) isHandlingSendEvent -{ - return _isHandlingSendEvent; -} - -- (void)setHandlingSendEvent:(BOOL)handlingSendEvent -{ - _isHandlingSendEvent = handlingSendEvent; -} - @end extern void InitializeAvnApp(IAvnApplicationEvents* events) diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 9186d9e15a..a90a235b9d 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -27,7 +27,7 @@ extern IAvnMenuItem* CreateAppMenuItem(); extern IAvnMenuItem* CreateAppMenuItemSeparator(); extern IAvnApplicationCommands* CreateApplicationCommands(); extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent); -extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); +extern void SetAppMenu(IAvnMenu *menu); extern void SetServicesMenu (IAvnMenu* menu); extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); @@ -38,7 +38,6 @@ extern NSPoint ToNSPoint (AvnPoint p); extern NSRect ToNSRect (AvnRect r); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); -extern CGFloat PrimaryDisplayHeight(); extern NSSize ToNSSize (AvnSize s); #ifdef DEBUG #define NSDebugLog(...) NSLog(__VA_ARGS__) diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm index dc38294a18..8638a03531 100644 --- a/native/Avalonia.Native/src/OSX/cursor.mm +++ b/native/Avalonia.Native/src/OSX/cursor.mm @@ -1,6 +1,5 @@ #include "common.h" #include "cursor.h" -#include class CursorFactory : public ComSingleObject { diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index ea79c494d7..011f881e94 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -343,7 +343,7 @@ public: @autoreleasepool { - ::SetAppMenu(s_appTitle, appMenu); + ::SetAppMenu(appMenu); return S_OK; } } @@ -428,7 +428,3 @@ AvnPoint ConvertPointY (AvnPoint p) return p; } -CGFloat PrimaryDisplayHeight() -{ - return NSMaxY([[[NSScreen screens] firstObject] frame]); -} diff --git a/native/Avalonia.Native/src/OSX/menu.h b/native/Avalonia.Native/src/OSX/menu.h index 186fcf255b..ce46ac11e0 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -31,7 +31,6 @@ private: NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem IAvnActionCallback* _callback; IAvnPredicateCallback* _predicate; - bool _isSeparator; bool _isCheckable; public: diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 726e58478b..fd74edd772 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -74,8 +74,7 @@ AvnAppMenuItem::AvnAppMenuItem(bool isSeparator) { _isCheckable = false; - _isSeparator = isSeparator; - + if(isSeparator) { _native = [NSMenuItem separatorItem]; @@ -460,7 +459,7 @@ extern IAvnMenuItem* CreateAppMenuItemSeparator() static IAvnMenu* s_appMenu = nullptr; static NSMenuItem* s_appMenuItem = nullptr; -extern void SetAppMenu (NSString* appName, IAvnMenu* menu) +extern void SetAppMenu(IAvnMenu *menu) { s_appMenu = menu; diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index dc5c24e41e..266d0345d1 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -1,14 +1,10 @@ #include "common.h" #include "rendertarget.h" -#import #import #import -#include -#include #include #include -#include @interface IOSurfaceHolder : NSObject @end From 849754e424b6ac10cb60a486c7812e28247574ab Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 5 May 2022 16:51:44 +0200 Subject: [PATCH 553/820] Expose CurrentOpacity on ISkiaDrawingContextImpl. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 1 + src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 2548b9f5aa..b45968ec27 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -126,6 +126,7 @@ namespace Avalonia.Skia SKCanvas ISkiaDrawingContextImpl.SkCanvas => Canvas; SKSurface ISkiaDrawingContextImpl.SkSurface => Surface; GRContext ISkiaDrawingContextImpl.GrContext => _grContext; + double ISkiaDrawingContextImpl.CurrentOpacity => _currentOpacity; /// public void Clear(Color color) diff --git a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs index 38fa5a5253..87c89beb7e 100644 --- a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs @@ -8,5 +8,6 @@ namespace Avalonia.Skia SKCanvas SkCanvas { get; } GRContext GrContext { get; } SKSurface SkSurface { get; } + double CurrentOpacity { get; } } } From e0ec6a4ba148c9016d4f438da1de43b0b30604a3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 5 May 2022 12:16:02 -0400 Subject: [PATCH 554/820] Use top level to inform about pointer input --- src/iOS/Avalonia.iOS/TouchHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iOS/Avalonia.iOS/TouchHandler.cs b/src/iOS/Avalonia.iOS/TouchHandler.cs index 43b19c85af..959a660d8a 100644 --- a/src/iOS/Avalonia.iOS/TouchHandler.cs +++ b/src/iOS/Avalonia.iOS/TouchHandler.cs @@ -41,7 +41,7 @@ namespace Avalonia.iOS _ => RawPointerEventType.TouchUpdate }, pt, RawInputModifiers.None, id); - _device.ProcessRawEvent(ev); + _tl.Input?.Invoke(ev); if (t.Phase == UITouchPhase.Cancelled || t.Phase == UITouchPhase.Ended) _knownTouches.Remove(t); @@ -49,4 +49,4 @@ namespace Avalonia.iOS } } -} \ No newline at end of file +} From c029ddfb32d253094478ac07c308a54f1afec67b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 18:13:39 +0100 Subject: [PATCH 555/820] fix project config. --- .../xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme index 5d20a135b9..87a8312c38 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme @@ -56,10 +56,14 @@ + + Date: Thu, 5 May 2022 18:14:06 +0100 Subject: [PATCH 556/820] move avnview to its own file. --- .../project.pbxproj | 8 + native/Avalonia.Native/src/OSX/AvnView.h | 29 + native/Avalonia.Native/src/OSX/AvnView.mm | 706 ++++++++++++++++++ .../Avalonia.Native/src/OSX/INSWindowHolder.h | 2 + native/Avalonia.Native/src/OSX/ResizeScope.h | 2 + native/Avalonia.Native/src/OSX/ResizeScope.mm | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 + native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 + native/Avalonia.Native/src/OSX/automation.mm | 1 + native/Avalonia.Native/src/OSX/window.h | 12 - native/Avalonia.Native/src/OSX/window.mm | 699 +---------------- 11 files changed, 752 insertions(+), 710 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AvnView.h create mode 100644 native/Avalonia.Native/src/OSX/AvnView.mm diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 2a206b0692..2d9dcbf9c8 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; + 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -43,6 +45,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; @@ -50,6 +53,7 @@ 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; + 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -154,6 +158,8 @@ 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */, 18391E45702740FE9DD69695 /* ResizeScope.mm */, 1839171D898F9BFC1373631A /* ResizeScope.h */, + 1839132D0E2454D911F1D1F9 /* AvnView.mm */, + 18391D1669284AD2EC9E866A /* AvnView.h */, ); sourceTree = ""; }; @@ -179,6 +185,7 @@ 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */, 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, + 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,6 +268,7 @@ 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, + 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h new file mode 100644 index 0000000000..c6dd90150f --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -0,0 +1,29 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import + + +#import +#import +#include "window.h" +#import "comimpl.h" +#import "common.h" +#import "WindowImpl.h" +#import "KeyTransform.h" + +@class AvnAccessibilityElement; + +@interface AvnView : NSView +-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; +-(NSEvent* _Nonnull) lastMouseDownEvent; +-(AvnPoint) translateLocalPoint:(AvnPoint)pt; +-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; +-(void) onClosed; + +-(AvnPlatformResizeReason) getResizeReason; +-(void) setResizeReason:(AvnPlatformResizeReason)reason; ++ (AvnPoint)toAvnPoint:(CGPoint)p; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm new file mode 100644 index 0000000000..24cbc25502 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -0,0 +1,706 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "AvnView.h" +#include "automation.h" + +@implementation AvnView +{ + ComPtr _parent; + NSTrackingArea* _area; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; + AvnInputModifiers _modifierState; + NSEvent* _lastMouseDownEvent; + bool _lastKeyHandled; + AvnPixelSize _lastPixelSize; + NSObject* _renderTarget; + AvnPlatformResizeReason _resizeReason; + AvnAccessibilityElement* _accessibilityChild; +} + +- (void)onClosed +{ + @synchronized (self) + { + _parent = nullptr; + } +} + +- (NSEvent*) lastMouseDownEvent +{ + return _lastMouseDownEvent; +} + +- (void) updateRenderTarget +{ + [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; + [self setNeedsDisplayInRect:[self frame]]; +} + +-(AvnView*) initWithParent: (WindowBaseImpl*) parent +{ + self = [super init]; + _renderTarget = parent->renderTarget; + [self setWantsLayer:YES]; + [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; + + _parent = parent; + _area = nullptr; + _lastPixelSize.Height = 100; + _lastPixelSize.Width = 100; + [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]]; + + _modifierState = AvnInputModifiersNone; + return self; +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)setLayer:(CALayer *)layer +{ + [_renderTarget setNewLayer: layer]; + [super setLayer: layer]; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return true; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return true; +} + +- (BOOL)canBecomeKeyView +{ + return true; +} + +-(void)setFrameSize:(NSSize)newSize +{ + [super setFrameSize:newSize]; + + if(_area != nullptr) + { + [self removeTrackingArea:_area]; + _area = nullptr; + } + + if (_parent == nullptr) + { + return; + } + + NSRect rect = NSZeroRect; + rect.size = newSize; + + NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag; + _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; + [self addTrackingArea:_area]; + + _parent->UpdateCursor(); + + auto fsize = [self convertSizeToBacking: [self frame].size]; + + if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height) + { + _lastPixelSize.Width = (int)fsize.width; + _lastPixelSize.Height = (int)fsize.height; + [self updateRenderTarget]; + + auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; + _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); + } +} + +- (void)updateLayer +{ + AvnInsidePotentialDeadlock deadlock; + if (_parent == nullptr) + { + return; + } + + _parent->BaseEvents->RunRenderPriorityJobs(); + + if (_parent == nullptr) + { + return; + } + + _parent->BaseEvents->Paint(); +} + +- (void)drawRect:(NSRect)dirtyRect +{ + return; +} + +-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose +{ + @autoreleasepool { + [_renderTarget setSwFrame:fb]; + dispose->Release(); + } +} + +- (AvnPoint) translateLocalPoint:(AvnPoint)pt +{ + pt.Y = [self bounds].size.height - pt.Y; + return pt; +} + ++ (AvnPoint)toAvnPoint:(CGPoint)p +{ + AvnPoint result; + + result.X = p.x; + result.Y = p.y; + + return result; +} + +- (void) viewDidChangeBackingProperties +{ + auto fsize = [self convertSizeToBacking: [self frame].size]; + _lastPixelSize.Width = (int)fsize.width; + _lastPixelSize.Height = (int)fsize.height; + [self updateRenderTarget]; + + if(_parent != nullptr) + { + _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); + } + + [super viewDidChangeBackingProperties]; +} + +- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled +{ + auto parentWindow = objc_cast([self window]); + + if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) + { + if(trigerInputWhenDisabled) + { + auto window = dynamic_cast(_parent.getRaw()); + + if(window != nullptr) + { + window->WindowEvents->GotInputWhenDisabled(); + } + } + + return TRUE; + } + + return FALSE; +} + +- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type +{ + bool triggerInputWhenDisabled = type != Move; + + if([self ignoreUserInput: triggerInputWhenDisabled]) + { + return; + } + + auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; + auto avnPoint = [AvnView toAvnPoint:localPoint]; + auto point = [self translateLocalPoint:avnPoint]; + AvnVector delta = { 0, 0}; + + if(type == Wheel) + { + auto speed = 5; + + if([event hasPreciseScrollingDeltas]) + { + speed = 50; + } + + delta.X = [event scrollingDeltaX] / speed; + delta.Y = [event scrollingDeltaY] / speed; + + if(delta.X == 0 && delta.Y == 0) + { + return; + } + } + else if (type == Magnify) + { + delta.X = delta.Y = [event magnification]; + } + else if (type == Rotate) + { + delta.X = delta.Y = [event rotation]; + } + else if (type == Swipe) + { + delta.X = [event deltaX]; + delta.Y = [event deltaY]; + } + + uint32 timestamp = static_cast([event timestamp] * 1000); + auto modifiers = [self getModifiers:[event modifierFlags]]; + + if(type != Move || + ( + [self window] != nil && + ( + [[self window] firstResponder] == nil + || ![[[self window] firstResponder] isKindOfClass: [NSView class]] + ) + ) + ) + [self becomeFirstResponder]; + + if(_parent != nullptr) + { + _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); + } + + [super mouseMoved:event]; +} + +- (BOOL) resignFirstResponder +{ + _parent->BaseEvents->LostFocus(); + return YES; +} + +- (void)mouseMoved:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; +} + +- (void)mouseDown:(NSEvent *)event +{ + _isLeftPressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:LeftButtonDown]; +} + +- (void)otherMouseDown:(NSEvent *)event +{ + _lastMouseDownEvent = event; + + switch(event.buttonNumber) + { + case 2: + case 3: + _isMiddlePressed = true; + [self mouseEvent:event withType:MiddleButtonDown]; + break; + case 4: + _isXButton1Pressed = true; + [self mouseEvent:event withType:XButton1Down]; + break; + case 5: + _isXButton2Pressed = true; + [self mouseEvent:event withType:XButton2Down]; + break; + + default: + break; + } +} + +- (void)rightMouseDown:(NSEvent *)event +{ + _isRightPressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:RightButtonDown]; +} + +- (void)mouseUp:(NSEvent *)event +{ + _isLeftPressed = false; + [self mouseEvent:event withType:LeftButtonUp]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + switch(event.buttonNumber) + { + case 2: + case 3: + _isMiddlePressed = false; + [self mouseEvent:event withType:MiddleButtonUp]; + break; + case 4: + _isXButton1Pressed = false; + [self mouseEvent:event withType:XButton1Up]; + break; + case 5: + _isXButton2Pressed = false; + [self mouseEvent:event withType:XButton2Up]; + break; + + default: + break; + } +} + +- (void)rightMouseUp:(NSEvent *)event +{ + _isRightPressed = false; + [self mouseEvent:event withType:RightButtonUp]; +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super mouseDragged:event]; +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super otherMouseDragged:event]; +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super rightMouseDragged:event]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + [self mouseEvent:event withType:Wheel]; + [super scrollWheel:event]; +} + +- (void)magnifyWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Magnify]; + [super magnifyWithEvent:event]; +} + +- (void)rotateWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Rotate]; + [super rotateWithEvent:event]; +} + +- (void)swipeWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Swipe]; + [super swipeWithEvent:event]; +} + +- (void)mouseEntered:(NSEvent *)event +{ + [super mouseEntered:event]; +} + +- (void)mouseExited:(NSEvent *)event +{ + [self mouseEvent:event withType:LeaveWindow]; + [super mouseExited:event]; +} + +- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type +{ + if([self ignoreUserInput: false]) + { + return; + } + + auto key = s_KeyMap[[event keyCode]]; + + uint32_t timestamp = static_cast([event timestamp] * 1000); + auto modifiers = [self getModifiers:[event modifierFlags]]; + + if(_parent != nullptr) + { + _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); + } +} + +- (BOOL)performKeyEquivalent:(NSEvent *)event +{ + bool result = _lastKeyHandled; + + _lastKeyHandled = false; + + return result; +} + +- (void)flagsChanged:(NSEvent *)event +{ + auto newModifierState = [self getModifiers:[event modifierFlags]]; + + bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt; + bool isControlCurrentlyPressed = (_modifierState & Control) == Control; + bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift; + bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows; + + bool isAltPressed = (newModifierState & Alt) == Alt; + bool isControlPressed = (newModifierState & Control) == Control; + bool isShiftPressed = (newModifierState & Shift) == Shift; + bool isCommandPressed = (newModifierState & Windows) == Windows; + + + if (isAltPressed && !isAltCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if (isAltCurrentlyPressed && !isAltPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + if (isControlPressed && !isControlCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if (isControlCurrentlyPressed && !isControlPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + if (isShiftPressed && !isShiftCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if(isShiftCurrentlyPressed && !isShiftPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + if(isCommandPressed && !isCommandCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if(isCommandCurrentlyPressed && ! isCommandPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + _modifierState = newModifierState; + + [[self inputContext] handleEvent:event]; + [super flagsChanged:event]; +} + +- (void)keyDown:(NSEvent *)event +{ + [self keyboardEvent:event withType:KeyDown]; + [[self inputContext] handleEvent:event]; + [super keyDown:event]; +} + +- (void)keyUp:(NSEvent *)event +{ + [self keyboardEvent:event withType:KeyUp]; + [super keyUp:event]; +} + +- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod +{ + unsigned int rv = 0; + + if (mod & NSEventModifierFlagControl) + rv |= Control; + if (mod & NSEventModifierFlagShift) + rv |= Shift; + if (mod & NSEventModifierFlagOption) + rv |= Alt; + if (mod & NSEventModifierFlagCommand) + rv |= Windows; + + if (_isLeftPressed) + rv |= LeftMouseButton; + if (_isMiddlePressed) + rv |= MiddleMouseButton; + if (_isRightPressed) + rv |= RightMouseButton; + if (_isXButton1Pressed) + rv |= XButton1MouseButton; + if (_isXButton2Pressed) + rv |= XButton2MouseButton; + + return (AvnInputModifiers)rv; +} + +- (BOOL)hasMarkedText +{ + return _lastKeyHandled; +} + +- (NSRange)markedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)selectedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ + +} + +- (void)unmarkText +{ + +} + +- (NSArray *)validAttributesForMarkedText +{ + return [NSArray new]; +} + +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange +{ + return [NSAttributedString new]; +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + if(!_lastKeyHandled) + { + if(_parent != nullptr) + { + _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); + } + } +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange +{ + CGRect result = { 0 }; + + return result; +} + +- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info +{ + auto localPoint = [self convertPoint:[info draggingLocation] toView:self]; + auto avnPoint = [AvnView toAvnPoint:localPoint]; + auto point = [self translateLocalPoint:avnPoint]; + auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]]; + NSDragOperation nsop = [info draggingSourceOperationMask]; + + auto effects = ConvertDragDropEffects(nsop); + int reffects = (int)_parent->BaseEvents + ->DragEvent(type, point, modifiers, effects, + CreateClipboard([info draggingPasteboard], nil), + GetAvnDataObjectHandleFromDraggingInfo(info)); + + NSDragOperation ret = static_cast(0); + + // Ensure that the managed part didn't add any new effects + reffects = (int)effects & reffects; + + // OSX requires exactly one operation + if((reffects & (int)AvnDragDropEffects::Copy) != 0) + ret = NSDragOperationCopy; + else if((reffects & (int)AvnDragDropEffects::Move) != 0) + ret = NSDragOperationMove; + else if((reffects & (int)AvnDragDropEffects::Link) != 0) + ret = NSDragOperationLink; + if(ret == 0) + ret = NSDragOperationNone; + return ret; +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender]; +} + +- (NSDragOperation)draggingUpdated:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender]; +} + +- (void)draggingExited:(id )sender +{ + [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender]; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone; +} + +- (BOOL)performDragOperation:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone; +} + +- (void)concludeDragOperation:(nullable id )sender +{ + +} + +- (AvnPlatformResizeReason)getResizeReason +{ + return _resizeReason; +} + +- (void)setResizeReason:(AvnPlatformResizeReason)reason +{ + _resizeReason = reason; +} + +- (AvnAccessibilityElement *) accessibilityChild +{ + if (_accessibilityChild == nil) + { + auto peer = _parent->BaseEvents->GetAutomationPeer(); + + if (peer == nil) + return nil; + + _accessibilityChild = [AvnAccessibilityElement acquire:peer]; + } + + return _accessibilityChild; +} + +- (NSArray *)accessibilityChildren +{ + auto child = [self accessibilityChild]; + return NSAccessibilityUnignoredChildrenForOnlyChild(child); +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + return [[self accessibilityChild] accessibilityHitTest:point]; +} + +- (id)accessibilityFocusedUIElement +{ + return [[self accessibilityChild] accessibilityFocusedUIElement]; +} + +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index aa8c34ef00..adc0dd1990 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -6,6 +6,8 @@ #ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H #define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H +@class AvnView; + struct INSWindowHolder { virtual AvnWindow* _Nonnull GetNSWindow () = 0; diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h index c57dc96690..7509f93c01 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.h +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -9,6 +9,8 @@ #include "window.h" #include "avalonia-native.h" +@class AvnView; + class ResizeScope { public: diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm index 8644b41fba..90e7f5cf15 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.mm +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -5,6 +5,7 @@ #import #include "ResizeScope.h" +#import "AvnView.h" ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { _view = view; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 9959d6a034..8f409fa84b 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -6,6 +6,7 @@ #import #include "common.h" #import "window.h" +#import "AvnView.h" #include "menu.h" #include "rendertarget.h" #include "automation.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 9cf5160c97..ef8be4be9c 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -5,6 +5,7 @@ #import #import "window.h" +#import "AvnView.h" #include "automation.h" #include "menu.h" #import "WindowImpl.h" diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 087d15a248..7a0b3b8127 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -3,6 +3,7 @@ #import "window.h" #include "AvnString.h" #import "INSWindowHolder.h" +#import "AvnView.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 271dd2534e..8caceb5bc1 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -7,18 +7,6 @@ class WindowBaseImpl; -@interface AvnView : NSView --(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; --(NSEvent* _Nonnull) lastMouseDownEvent; --(AvnPoint) translateLocalPoint:(AvnPoint)pt; --(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; --(void) onClosed; - --(AvnPlatformResizeReason) getResizeReason; --(void) setResizeReason:(AvnPlatformResizeReason)reason; -+ (AvnPoint)toAvnPoint:(CGPoint)p; -@end - @interface AutoFitContentView : NSView -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; -(void) ShowTitleBar: (bool) show; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index fe80142f1a..a66ff94c8a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -7,6 +7,7 @@ #include "automation.h" #import "WindowBaseImpl.h" #include "WindowImpl.h" +#include "AvnView.h" @implementation AutoFitContentView { @@ -105,704 +106,6 @@ } @end -@implementation AvnView -{ - ComPtr _parent; - NSTrackingArea* _area; - bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; - AvnInputModifiers _modifierState; - NSEvent* _lastMouseDownEvent; - bool _lastKeyHandled; - AvnPixelSize _lastPixelSize; - NSObject* _renderTarget; - AvnPlatformResizeReason _resizeReason; - AvnAccessibilityElement* _accessibilityChild; -} - -- (void)onClosed -{ - @synchronized (self) - { - _parent = nullptr; - } -} - -- (NSEvent*) lastMouseDownEvent -{ - return _lastMouseDownEvent; -} - -- (void) updateRenderTarget -{ - [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; - [self setNeedsDisplayInRect:[self frame]]; -} - --(AvnView*) initWithParent: (WindowBaseImpl*) parent -{ - self = [super init]; - _renderTarget = parent->renderTarget; - [self setWantsLayer:YES]; - [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; - - _parent = parent; - _area = nullptr; - _lastPixelSize.Height = 100; - _lastPixelSize.Width = 100; - [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]]; - - _modifierState = AvnInputModifiersNone; - return self; -} - -- (BOOL)isFlipped -{ - return YES; -} - -- (BOOL)wantsUpdateLayer -{ - return YES; -} - -- (void)setLayer:(CALayer *)layer -{ - [_renderTarget setNewLayer: layer]; - [super setLayer: layer]; -} - -- (BOOL)isOpaque -{ - return YES; -} - -- (BOOL)acceptsFirstResponder -{ - return true; -} - -- (BOOL)acceptsFirstMouse:(NSEvent *)event -{ - return true; -} - -- (BOOL)canBecomeKeyView -{ - return true; -} - --(void)setFrameSize:(NSSize)newSize -{ - [super setFrameSize:newSize]; - - if(_area != nullptr) - { - [self removeTrackingArea:_area]; - _area = nullptr; - } - - if (_parent == nullptr) - { - return; - } - - NSRect rect = NSZeroRect; - rect.size = newSize; - - NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag; - _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; - [self addTrackingArea:_area]; - - _parent->UpdateCursor(); - - auto fsize = [self convertSizeToBacking: [self frame].size]; - - if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height) - { - _lastPixelSize.Width = (int)fsize.width; - _lastPixelSize.Height = (int)fsize.height; - [self updateRenderTarget]; - - auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; - _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); - } -} - -- (void)updateLayer -{ - AvnInsidePotentialDeadlock deadlock; - if (_parent == nullptr) - { - return; - } - - _parent->BaseEvents->RunRenderPriorityJobs(); - - if (_parent == nullptr) - { - return; - } - - _parent->BaseEvents->Paint(); -} - -- (void)drawRect:(NSRect)dirtyRect -{ - return; -} - --(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose -{ - @autoreleasepool { - [_renderTarget setSwFrame:fb]; - dispose->Release(); - } -} - -- (AvnPoint) translateLocalPoint:(AvnPoint)pt -{ - pt.Y = [self bounds].size.height - pt.Y; - return pt; -} - -+ (AvnPoint)toAvnPoint:(CGPoint)p -{ - AvnPoint result; - - result.X = p.x; - result.Y = p.y; - - return result; -} - -- (void) viewDidChangeBackingProperties -{ - auto fsize = [self convertSizeToBacking: [self frame].size]; - _lastPixelSize.Width = (int)fsize.width; - _lastPixelSize.Height = (int)fsize.height; - [self updateRenderTarget]; - - if(_parent != nullptr) - { - _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); - } - - [super viewDidChangeBackingProperties]; -} - -- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled -{ - auto parentWindow = objc_cast([self window]); - - if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) - { - if(trigerInputWhenDisabled) - { - auto window = dynamic_cast(_parent.getRaw()); - - if(window != nullptr) - { - window->WindowEvents->GotInputWhenDisabled(); - } - } - - return TRUE; - } - - return FALSE; -} - -- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type -{ - bool triggerInputWhenDisabled = type != Move; - - if([self ignoreUserInput: triggerInputWhenDisabled]) - { - return; - } - - auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; - auto avnPoint = [AvnView toAvnPoint:localPoint]; - auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta = { 0, 0}; - - if(type == Wheel) - { - auto speed = 5; - - if([event hasPreciseScrollingDeltas]) - { - speed = 50; - } - - delta.X = [event scrollingDeltaX] / speed; - delta.Y = [event scrollingDeltaY] / speed; - - if(delta.X == 0 && delta.Y == 0) - { - return; - } - } - else if (type == Magnify) - { - delta.X = delta.Y = [event magnification]; - } - else if (type == Rotate) - { - delta.X = delta.Y = [event rotation]; - } - else if (type == Swipe) - { - delta.X = [event deltaX]; - delta.Y = [event deltaY]; - } - - uint32 timestamp = static_cast([event timestamp] * 1000); - auto modifiers = [self getModifiers:[event modifierFlags]]; - - if(type != AvnRawMouseEventType::Move || - ( - [self window] != nil && - ( - [[self window] firstResponder] == nil - || ![[[self window] firstResponder] isKindOfClass: [NSView class]] - ) - ) - ) - [self becomeFirstResponder]; - - if(_parent != nullptr) - { - _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); - } - - [super mouseMoved:event]; -} - -- (BOOL) resignFirstResponder -{ - _parent->BaseEvents->LostFocus(); - return YES; -} - -- (void)mouseMoved:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; -} - -- (void)mouseDown:(NSEvent *)event -{ - _isLeftPressed = true; - _lastMouseDownEvent = event; - [self mouseEvent:event withType:LeftButtonDown]; -} - -- (void)otherMouseDown:(NSEvent *)event -{ - _lastMouseDownEvent = event; - - switch(event.buttonNumber) - { - case 2: - case 3: - _isMiddlePressed = true; - [self mouseEvent:event withType:MiddleButtonDown]; - break; - case 4: - _isXButton1Pressed = true; - [self mouseEvent:event withType:XButton1Down]; - break; - case 5: - _isXButton2Pressed = true; - [self mouseEvent:event withType:XButton2Down]; - break; - - default: - break; - } -} - -- (void)rightMouseDown:(NSEvent *)event -{ - _isRightPressed = true; - _lastMouseDownEvent = event; - [self mouseEvent:event withType:RightButtonDown]; -} - -- (void)mouseUp:(NSEvent *)event -{ - _isLeftPressed = false; - [self mouseEvent:event withType:LeftButtonUp]; -} - -- (void)otherMouseUp:(NSEvent *)event -{ - switch(event.buttonNumber) - { - case 2: - case 3: - _isMiddlePressed = false; - [self mouseEvent:event withType:MiddleButtonUp]; - break; - case 4: - _isXButton1Pressed = false; - [self mouseEvent:event withType:XButton1Up]; - break; - case 5: - _isXButton2Pressed = false; - [self mouseEvent:event withType:XButton2Up]; - break; - - default: - break; - } -} - -- (void)rightMouseUp:(NSEvent *)event -{ - _isRightPressed = false; - [self mouseEvent:event withType:RightButtonUp]; -} - -- (void)mouseDragged:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; - [super mouseDragged:event]; -} - -- (void)otherMouseDragged:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; - [super otherMouseDragged:event]; -} - -- (void)rightMouseDragged:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; - [super rightMouseDragged:event]; -} - -- (void)scrollWheel:(NSEvent *)event -{ - [self mouseEvent:event withType:Wheel]; - [super scrollWheel:event]; -} - -- (void)magnifyWithEvent:(NSEvent *)event -{ - [self mouseEvent:event withType:Magnify]; - [super magnifyWithEvent:event]; -} - -- (void)rotateWithEvent:(NSEvent *)event -{ - [self mouseEvent:event withType:Rotate]; - [super rotateWithEvent:event]; -} - -- (void)swipeWithEvent:(NSEvent *)event -{ - [self mouseEvent:event withType:Swipe]; - [super swipeWithEvent:event]; -} - -- (void)mouseEntered:(NSEvent *)event -{ - [super mouseEntered:event]; -} - -- (void)mouseExited:(NSEvent *)event -{ - [self mouseEvent:event withType:LeaveWindow]; - [super mouseExited:event]; -} - -- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type -{ - if([self ignoreUserInput: false]) - { - return; - } - - auto key = s_KeyMap[[event keyCode]]; - - uint32_t timestamp = static_cast([event timestamp] * 1000); - auto modifiers = [self getModifiers:[event modifierFlags]]; - - if(_parent != nullptr) - { - _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); - } -} - -- (BOOL)performKeyEquivalent:(NSEvent *)event -{ - bool result = _lastKeyHandled; - - _lastKeyHandled = false; - - return result; -} - -- (void)flagsChanged:(NSEvent *)event -{ - auto newModifierState = [self getModifiers:[event modifierFlags]]; - - bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt; - bool isControlCurrentlyPressed = (_modifierState & Control) == Control; - bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift; - bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows; - - bool isAltPressed = (newModifierState & Alt) == Alt; - bool isControlPressed = (newModifierState & Control) == Control; - bool isShiftPressed = (newModifierState & Shift) == Shift; - bool isCommandPressed = (newModifierState & Windows) == Windows; - - - if (isAltPressed && !isAltCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if (isAltCurrentlyPressed && !isAltPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - if (isControlPressed && !isControlCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if (isControlCurrentlyPressed && !isControlPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - if (isShiftPressed && !isShiftCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if(isShiftCurrentlyPressed && !isShiftPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - if(isCommandPressed && !isCommandCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if(isCommandCurrentlyPressed && ! isCommandPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - _modifierState = newModifierState; - - [[self inputContext] handleEvent:event]; - [super flagsChanged:event]; -} - -- (void)keyDown:(NSEvent *)event -{ - [self keyboardEvent:event withType:KeyDown]; - [[self inputContext] handleEvent:event]; - [super keyDown:event]; -} - -- (void)keyUp:(NSEvent *)event -{ - [self keyboardEvent:event withType:KeyUp]; - [super keyUp:event]; -} - -- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod -{ - unsigned int rv = 0; - - if (mod & NSEventModifierFlagControl) - rv |= Control; - if (mod & NSEventModifierFlagShift) - rv |= Shift; - if (mod & NSEventModifierFlagOption) - rv |= Alt; - if (mod & NSEventModifierFlagCommand) - rv |= Windows; - - if (_isLeftPressed) - rv |= LeftMouseButton; - if (_isMiddlePressed) - rv |= MiddleMouseButton; - if (_isRightPressed) - rv |= RightMouseButton; - if (_isXButton1Pressed) - rv |= XButton1MouseButton; - if (_isXButton2Pressed) - rv |= XButton2MouseButton; - - return (AvnInputModifiers)rv; -} - -- (BOOL)hasMarkedText -{ - return _lastKeyHandled; -} - -- (NSRange)markedRange -{ - return NSMakeRange(NSNotFound, 0); -} - -- (NSRange)selectedRange -{ - return NSMakeRange(NSNotFound, 0); -} - -- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange -{ - -} - -- (void)unmarkText -{ - -} - -- (NSArray *)validAttributesForMarkedText -{ - return [NSArray new]; -} - -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange -{ - return [NSAttributedString new]; -} - -- (void)insertText:(id)string replacementRange:(NSRange)replacementRange -{ - if(!_lastKeyHandled) - { - if(_parent != nullptr) - { - _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); - } - } -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)point -{ - return 0; -} - -- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange -{ - CGRect result = { 0 }; - - return result; -} - -- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info -{ - auto localPoint = [self convertPoint:[info draggingLocation] toView:self]; - auto avnPoint = [AvnView toAvnPoint:localPoint]; - auto point = [self translateLocalPoint:avnPoint]; - auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]]; - NSDragOperation nsop = [info draggingSourceOperationMask]; - - auto effects = ConvertDragDropEffects(nsop); - int reffects = (int)_parent->BaseEvents - ->DragEvent(type, point, modifiers, effects, - CreateClipboard([info draggingPasteboard], nil), - GetAvnDataObjectHandleFromDraggingInfo(info)); - - NSDragOperation ret = static_cast(0); - - // Ensure that the managed part didn't add any new effects - reffects = (int)effects & reffects; - - // OSX requires exactly one operation - if((reffects & (int)AvnDragDropEffects::Copy) != 0) - ret = NSDragOperationCopy; - else if((reffects & (int)AvnDragDropEffects::Move) != 0) - ret = NSDragOperationMove; - else if((reffects & (int)AvnDragDropEffects::Link) != 0) - ret = NSDragOperationLink; - if(ret == 0) - ret = NSDragOperationNone; - return ret; -} - -- (NSDragOperation)draggingEntered:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender]; -} - -- (NSDragOperation)draggingUpdated:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender]; -} - -- (void)draggingExited:(id )sender -{ - [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender]; -} - -- (BOOL)prepareForDragOperation:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone; -} - -- (BOOL)performDragOperation:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone; -} - -- (void)concludeDragOperation:(nullable id )sender -{ - -} - -- (AvnPlatformResizeReason)getResizeReason -{ - return _resizeReason; -} - -- (void)setResizeReason:(AvnPlatformResizeReason)reason -{ - _resizeReason = reason; -} - -- (AvnAccessibilityElement *) accessibilityChild -{ - if (_accessibilityChild == nil) - { - auto peer = _parent->BaseEvents->GetAutomationPeer(); - - if (peer == nil) - return nil; - - _accessibilityChild = [AvnAccessibilityElement acquire:peer]; - } - - return _accessibilityChild; -} - -- (NSArray *)accessibilityChildren -{ - auto child = [self accessibilityChild]; - return NSAccessibilityUnignoredChildrenForOnlyChild(child); -} - -- (id)accessibilityHitTest:(NSPoint)point -{ - return [[self accessibilityChild] accessibilityHitTest:point]; -} - -- (id)accessibilityFocusedUIElement -{ - return [[self accessibilityChild] accessibilityFocusedUIElement]; -} - -@end - @implementation AvnWindow { From f19639684d4fd7ac1b497a27f874475c6888447d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 18:17:58 +0100 Subject: [PATCH 557/820] move AutoFitContentView to its own file. --- .../src/OSX/AutoFitContentView.h | 15 +++ .../src/OSX/AutoFitContentView.mm | 104 ++++++++++++++++++ .../project.pbxproj | 8 ++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 + native/Avalonia.Native/src/OSX/WindowImpl.mm | 3 +- native/Avalonia.Native/src/OSX/window.h | 8 -- native/Avalonia.Native/src/OSX/window.mm | 98 +---------------- 8 files changed, 132 insertions(+), 107 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AutoFitContentView.h create mode 100644 native/Avalonia.Native/src/OSX/AutoFitContentView.mm diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h new file mode 100644 index 0000000000..68c9fb8dc8 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h @@ -0,0 +1,15 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "avalonia-native.h" + +@interface AutoFitContentView : NSView +-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; +-(void) ShowTitleBar: (bool) show; +-(void) SetTitleBarHeightHint: (double) height; + +-(void) ShowBlur: (bool) show; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm new file mode 100644 index 0000000000..92d6f67a91 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -0,0 +1,104 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#include "AvnView.h" +#import "AutoFitContentView.h" + +@implementation AutoFitContentView +{ + NSVisualEffectView* _titleBarMaterial; + NSBox* _titleBarUnderline; + NSView* _content; + NSVisualEffectView* _blurBehind; + double _titleBarHeightHint; + bool _settingSize; +} + +-(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content +{ + _titleBarHeightHint = -1; + _content = content; + _settingSize = false; + + [self setAutoresizesSubviews:true]; + [self setWantsLayer:true]; + + _titleBarMaterial = [NSVisualEffectView new]; + [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow]; + [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar]; + [_titleBarMaterial setWantsLayer:true]; + _titleBarMaterial.hidden = true; + + _titleBarUnderline = [NSBox new]; + _titleBarUnderline.boxType = NSBoxSeparator; + _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor]; + _titleBarUnderline.hidden = true; + + [self addSubview:_titleBarMaterial]; + [self addSubview:_titleBarUnderline]; + + _blurBehind = [NSVisualEffectView new]; + [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; + [_blurBehind setMaterial:NSVisualEffectMaterialLight]; + [_blurBehind setWantsLayer:true]; + _blurBehind.hidden = true; + + [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + [self addSubview:_blurBehind]; + [self addSubview:_content]; + + [self setWantsLayer:true]; + return self; +} + +-(void) ShowBlur:(bool)show +{ + _blurBehind.hidden = !show; +} + +-(void) ShowTitleBar: (bool) show +{ + _titleBarMaterial.hidden = !show; + _titleBarUnderline.hidden = !show; +} + +-(void) SetTitleBarHeightHint: (double) height +{ + _titleBarHeightHint = height; + + [self setFrameSize:self.frame.size]; +} + +-(void)setFrameSize:(NSSize)newSize +{ + if(_settingSize) + { + return; + } + + _settingSize = true; + [super setFrameSize:newSize]; + + auto window = objc_cast([self window]); + + // TODO get actual titlebar size + + double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint; + + NSRect tbar; + tbar.origin.x = 0; + tbar.origin.y = newSize.height - height; + tbar.size.width = newSize.width; + tbar.size.height = height; + + [_titleBarMaterial setFrame:tbar]; + tbar.size.height = height < 1 ? 0 : 1; + [_titleBarUnderline setFrame:tbar]; + + _settingSize = false; +} +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 2d9dcbf9c8..88e4c0c682 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -13,9 +13,11 @@ 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; + 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; @@ -48,6 +50,8 @@ 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; + 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; }; + 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; @@ -160,6 +164,8 @@ 1839171D898F9BFC1373631A /* ResizeScope.h */, 1839132D0E2454D911F1D1F9 /* AvnView.mm */, 18391D1669284AD2EC9E866A /* AvnView.h */, + 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, + 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, ); sourceTree = ""; }; @@ -186,6 +192,7 @@ 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, + 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -269,6 +276,7 @@ 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, + 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 94175b8187..d2690cee41 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -9,6 +9,8 @@ #import "rendertarget.h" #include "INSWindowHolder.h" +@class AutoFitContentView; + class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, public INSWindowHolder { diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 8f409fa84b..ca610d3ce6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -13,6 +13,7 @@ #import "WindowBaseImpl.h" #import "cursor.h" #include "ResizeScope.h" +#import "AutoFitContentView.h" WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index ef8be4be9c..954d27ab98 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -5,10 +5,9 @@ #import #import "window.h" +#import "AutoFitContentView.h" #import "AvnView.h" #include "automation.h" -#include "menu.h" -#import "WindowImpl.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 8caceb5bc1..e9b98ce9ae 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -7,14 +7,6 @@ class WindowBaseImpl; -@interface AutoFitContentView : NSView --(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; --(void) ShowTitleBar: (bool) show; --(void) SetTitleBarHeightHint: (double) height; - --(void) ShowBlur: (bool) show; -@end - @interface AvnWindow : NSWindow -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(void) setCanBecomeKeyAndMain; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index a66ff94c8a..5bd3d8ddac 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -8,103 +8,7 @@ #import "WindowBaseImpl.h" #include "WindowImpl.h" #include "AvnView.h" - -@implementation AutoFitContentView -{ - NSVisualEffectView* _titleBarMaterial; - NSBox* _titleBarUnderline; - NSView* _content; - NSVisualEffectView* _blurBehind; - double _titleBarHeightHint; - bool _settingSize; -} - --(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content -{ - _titleBarHeightHint = -1; - _content = content; - _settingSize = false; - - [self setAutoresizesSubviews:true]; - [self setWantsLayer:true]; - - _titleBarMaterial = [NSVisualEffectView new]; - [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow]; - [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar]; - [_titleBarMaterial setWantsLayer:true]; - _titleBarMaterial.hidden = true; - - _titleBarUnderline = [NSBox new]; - _titleBarUnderline.boxType = NSBoxSeparator; - _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor]; - _titleBarUnderline.hidden = true; - - [self addSubview:_titleBarMaterial]; - [self addSubview:_titleBarUnderline]; - - _blurBehind = [NSVisualEffectView new]; - [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; - [_blurBehind setMaterial:NSVisualEffectMaterialLight]; - [_blurBehind setWantsLayer:true]; - _blurBehind.hidden = true; - - [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - [self addSubview:_blurBehind]; - [self addSubview:_content]; - - [self setWantsLayer:true]; - return self; -} - --(void) ShowBlur:(bool)show -{ - _blurBehind.hidden = !show; -} - --(void) ShowTitleBar: (bool) show -{ - _titleBarMaterial.hidden = !show; - _titleBarUnderline.hidden = !show; -} - --(void) SetTitleBarHeightHint: (double) height -{ - _titleBarHeightHint = height; - - [self setFrameSize:self.frame.size]; -} - --(void)setFrameSize:(NSSize)newSize -{ - if(_settingSize) - { - return; - } - - _settingSize = true; - [super setFrameSize:newSize]; - - auto window = objc_cast([self window]); - - // TODO get actual titlebar size - - double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint; - - NSRect tbar; - tbar.origin.x = 0; - tbar.origin.y = newSize.height - height; - tbar.size.width = newSize.width; - tbar.size.height = height; - - [_titleBarMaterial setFrame:tbar]; - tbar.size.height = height < 1 ? 0 : 1; - [_titleBarUnderline setFrame:tbar]; - - _settingSize = false; -} -@end +#include "AutoFitContentView.h" @implementation AvnWindow From 3aab84d8daca2575897ca435af49f073f0a8aeb2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 18:21:58 +0100 Subject: [PATCH 558/820] remove some imports no longer needed. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 -- native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 - native/Avalonia.Native/src/OSX/window.mm | 4 ---- 3 files changed, 7 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index ca610d3ce6..fb67addce1 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -8,9 +8,7 @@ #import "window.h" #import "AvnView.h" #include "menu.h" -#include "rendertarget.h" #include "automation.h" -#import "WindowBaseImpl.h" #import "cursor.h" #include "ResizeScope.h" #import "AutoFitContentView.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 954d27ab98..0d325c4490 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -9,7 +9,6 @@ #import "AvnView.h" #include "automation.h" - WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { _isClientAreaExtended = false; _extendClientHints = AvnDefaultChrome; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index bf2cbdbf19..68a459d088 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,15 +1,11 @@ #import #include "common.h" #import "window.h" -#include "KeyTransform.h" #include "menu.h" -#include "rendertarget.h" #include "automation.h" #import "WindowBaseImpl.h" #include "WindowImpl.h" #include "AvnView.h" -#include "AutoFitContentView.h" - @implementation AvnWindow { From 174b94f3b4c822018dcc7daaccc9f1420ef8c172 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 6 May 2022 09:28:45 +0200 Subject: [PATCH 559/820] Replace ImageMagick with ImageSharp for render test comparisons. --- Avalonia.sln | 2 +- ....NET-Q16-AnyCPU.props => ImageSharp.props} | 2 +- .../Avalonia.Direct2D1.RenderTests.csproj | 2 +- tests/Avalonia.RenderTests/TestBase.cs | 68 ++++++++++++++++--- .../Avalonia.Skia.RenderTests.csproj | 2 +- 5 files changed, 62 insertions(+), 14 deletions(-) rename build/{Magick.NET-Q16-AnyCPU.props => ImageSharp.props} (63%) diff --git a/Avalonia.sln b/Avalonia.sln index ea30514c3e..c8e513f94c 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -99,7 +99,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\HarfBuzzSharp.props = build\HarfBuzzSharp.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props - build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props build\Microsoft.CSharp.props = build\Microsoft.CSharp.props build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props build\Moq.props = build\Moq.props @@ -118,6 +117,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\System.Memory.props = build\System.Memory.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\XUnit.props = build\XUnit.props + build\ImageSharp.props = build\ImageSharp.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" diff --git a/build/Magick.NET-Q16-AnyCPU.props b/build/ImageSharp.props similarity index 63% rename from build/Magick.NET-Q16-AnyCPU.props rename to build/ImageSharp.props index 21d9cdcb1f..178c274ac9 100644 --- a/build/Magick.NET-Q16-AnyCPU.props +++ b/build/ImageSharp.props @@ -1,5 +1,5 @@ - + diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj index e7f1552370..52acc78db1 100644 --- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj +++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj @@ -19,6 +19,6 @@ - + diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index b70c721085..523876500f 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -1,10 +1,9 @@ using System.IO; using System.Runtime.CompilerServices; -using ImageMagick; using Avalonia.Controls; using Avalonia.Media.Imaging; using Avalonia.Rendering; - +using SixLabors.ImageSharp; using Xunit; using Avalonia.Platform; using System.Threading.Tasks; @@ -12,6 +11,8 @@ using System; using System.Threading; using Avalonia.Media; using Avalonia.Threading; +using SixLabors.ImageSharp.PixelFormats; +using Image = SixLabors.ImageSharp.Image; #if AVALONIA_SKIA using Avalonia.Skia; #else @@ -119,12 +120,12 @@ namespace Avalonia.Direct2D1.RenderTests var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png"); var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png"); - using (var expected = new MagickImage(expectedPath)) - using (var immediate = new MagickImage(immediatePath)) - using (var deferred = new MagickImage(deferredPath)) + using (var expected = Image.Load(expectedPath)) + using (var immediate = Image.Load(immediatePath)) + using (var deferred = Image.Load(deferredPath)) { - double immediateError = expected.Compare(immediate, ErrorMetric.RootMeanSquared); - double deferredError = expected.Compare(deferred, ErrorMetric.RootMeanSquared); + var immediateError = CompareImages(immediate, expected); + var deferredError = CompareImages(deferred, expected); if (immediateError > 0.022) { @@ -143,10 +144,10 @@ namespace Avalonia.Direct2D1.RenderTests var expectedPath = Path.Combine(OutputPath, testName + ".expected.png"); var actualPath = Path.Combine(OutputPath, testName + ".out.png"); - using (var expected = new MagickImage(expectedPath)) - using (var actual = new MagickImage(actualPath)) + using (var expected = Image.Load(expectedPath)) + using (var actual = Image.Load(actualPath)) { - double immediateError = expected.Compare(actual, ErrorMetric.RootMeanSquared); + double immediateError = CompareImages(actual, expected); if (immediateError > 0.022) { @@ -154,6 +155,53 @@ namespace Avalonia.Direct2D1.RenderTests } } } + + /// + /// Calculates root mean square error for given two images. + /// Based roughly on ImageMagick implementation to ensure consistency. + /// + private static double CompareImages(Image actual, Image expected) + { + if (actual.Width != expected.Width || actual.Height != expected.Height) + { + throw new ArgumentException("Images have different resolutions"); + } + + var quantity = actual.Width * actual.Height; + double squaresError = 0; + + const double scale = 1 / 255d; + + for (var x = 0; x < actual.Width; x++) + { + double localError = 0; + + for (var y = 0; y < actual.Height; y++) + { + var expectedAlpha = expected[x, y].A * scale; + var actualAlpha = actual[x, y].A * scale; + + var r = scale * (expectedAlpha * expected[x, y].R - actualAlpha * actual[x, y].R); + var g = scale * (expectedAlpha * expected[x, y].G - actualAlpha * actual[x, y].G); + var b = scale * (expectedAlpha * expected[x, y].B - actualAlpha * actual[x, y].B); + var a = expectedAlpha - actualAlpha; + + var error = r * r + g * g + b * b + a * a; + + localError += error; + } + + squaresError += localError; + } + + var meanSquaresError = squaresError / quantity; + + const int channelCount = 4; + + meanSquaresError = meanSquaresError / channelCount; + + return Math.Sqrt(meanSquaresError); + } private string GetTestsDirectory() { diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj index ffbcdd9e7c..d3f2b44968 100644 --- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -20,7 +20,7 @@ - + From 66d7ffe2e99d8809bf1801bd7df20397db4260e0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:23:52 +0100 Subject: [PATCH 560/820] use content min/max size for minsize. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fb67addce1..43a27a34b2 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -225,8 +225,8 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { START_COM_CALL; @autoreleasepool { - [Window setMinSize:ToNSSize(minSize)]; - [Window setMaxSize:ToNSSize(maxSize)]; + [Window setContentMinSize:ToNSSize(minSize)]; + [Window setContentMaxSize:ToNSSize(maxSize)]; return S_OK; } @@ -243,8 +243,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso auto resizeBlock = ResizeScope(View, reason); @autoreleasepool { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; + auto maxSize = [Window contentMaxSize]; + auto minSize = [Window contentMinSize]; if (x < minSize.width) { x = minSize.width; From 90f6143c58d9641fca2a19734fbeb9462c30639c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 6 May 2022 12:40:33 +0200 Subject: [PATCH 561/820] Don't use managed dialogs by default in ControlCatalog. It means that system dialogs never get properly tested. --- samples/ControlCatalog.NetCore/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 4b81935452..4464413e63 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -117,7 +117,6 @@ namespace ControlCatalog.NetCore EnableMultitouch = true }) .UseSkia() - .UseManagedSystemDialogs() .AfterSetup(builder => { builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() From 4cba4519f3e76d368384a40a9e655a76eedcf7f9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:45:47 +0100 Subject: [PATCH 562/820] only create the NSWindow when show is called. Cache sizes until needed. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 11 ++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 53 ++++++++++++++----- native/Avalonia.Native/src/OSX/window.h | 2 +- native/Avalonia.Native/src/OSX/window.mm | 6 ++- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index d2690cee41..19c443806c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -24,10 +24,7 @@ BEGIN_INTERFACE_MAP() INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) END_INTERFACE_MAP() - virtual ~WindowBaseImpl() { - View = NULL; - Window = NULL; - } + virtual ~WindowBaseImpl(); AutoFitContentView *StandardContainer; AvnView *View; @@ -36,6 +33,9 @@ BEGIN_INTERFACE_MAP() ComPtr _glContext; NSObject *renderTarget; AvnPoint lastPositionSet; + NSSize lastSize; + NSSize lastMinSize; + NSSize lastMaxSize; NSString *_lastTitle; bool _shown; @@ -116,6 +116,9 @@ protected: void UpdateStyle(); +private: + void InitialiseNSWindow (); + }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fb67addce1..fe3a209d3e 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -12,6 +12,13 @@ #import "cursor.h" #include "ResizeScope.h" #import "AutoFitContentView.h" +#include "WindowBaseImpl.h" + + +WindowBaseImpl::~WindowBaseImpl() { + View = nullptr; + Window = nullptr; +} WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; @@ -22,17 +29,11 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) View = [[AvnView alloc] initWithParent:this]; StandardContainer = [[AutoFitContentView new] initWithContent:View]; - Window = [[AvnWindow alloc] initWithParent:this]; - [Window setContentView:StandardContainer]; - lastPositionSet.X = 100; lastPositionSet.Y = 100; _lastTitle = @""; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setOpaque:false]; + Window = nullptr; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -83,6 +84,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + InitialiseNSWindow(); + SetPosition(lastPositionSet); UpdateStyle(); @@ -97,6 +100,10 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window orderFront:Window]; } + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + [Window setContentSize: lastSize]; + _shown = true; return S_OK; @@ -225,8 +232,13 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { START_COM_CALL; @autoreleasepool { - [Window setMinSize:ToNSSize(minSize)]; - [Window setMaxSize:ToNSSize(maxSize)]; + lastMinSize = ToNSSize(minSize); + lastMaxSize = ToNSSize(maxSize); + + if(Window != nullptr) { + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + } return S_OK; } @@ -243,8 +255,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso auto resizeBlock = ResizeScope(View, reason); @autoreleasepool { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; + auto maxSize = lastMaxSize; + auto minSize = lastMinSize; if (x < minSize.width) { x = minSize.width; @@ -267,8 +279,12 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso BaseEvents->Resized(AvnSize{x, y}, reason); } - [Window setContentSize:NSSize{x, y}]; - [Window invalidateShadow]; + lastSize = NSSize {x, y}; + + if(Window != nullptr) { + [Window setContentSize:lastSize]; + [Window invalidateShadow]; + } } @finally { _inResize = false; @@ -502,4 +518,13 @@ NSWindowStyleMask WindowBaseImpl::GetStyle() { void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; -} \ No newline at end of file +} + +void WindowBaseImpl::InitialiseNSWindow() { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{ 0, 0, lastSize } styleMask:GetStyle()]; + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setOpaque:false]; +} diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index e9b98ce9ae..a8c31e7b60 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -8,7 +8,7 @@ class WindowBaseImpl; @interface AvnWindow : NSWindow --(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; +-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; -(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 68a459d088..965b4a849b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -143,9 +143,10 @@ _canBecomeKeyAndMain = true; } --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent +-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { - self = [super init]; + self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; + [self setReleasedWhenClosed:false]; _parent = parent; [self setDelegate:self]; @@ -155,6 +156,7 @@ [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; + _isExtended = false; return self; } From 0ee1a7e3910525a069230d713c93ed47d3528b4d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:50:42 +0100 Subject: [PATCH 563/820] add explanation for init with content size. --- native/Avalonia.Native/src/OSX/window.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 965b4a849b..68f8f76d75 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -145,6 +145,9 @@ -(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { + // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ + // create nswindow with specific contentRect, otherwise we wont be able to resize the window + // until several ms after the window is physically on the screen. self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; [self setReleasedWhenClosed:false]; From 2372155995ec6680c5ec27570da95a23b9e09cc4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 13:05:40 +0100 Subject: [PATCH 564/820] only create the window if reference is null --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fe3a209d3e..0c85dcc303 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -521,10 +521,12 @@ void WindowBaseImpl::UpdateStyle() { } void WindowBaseImpl::InitialiseNSWindow() { - Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{ 0, 0, lastSize } styleMask:GetStyle()]; - [Window setContentView:StandardContainer]; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; + if(Window == nullptr) { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; - [Window setOpaque:false]; + [Window setOpaque:false]; + } } From e76887fe25830404a0d6afd97782e0f8629c22b0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:14:08 +0100 Subject: [PATCH 565/820] INSWindowHolder uses NSView and NSWindow types. --- native/Avalonia.Native/src/OSX/INSWindowHolder.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index adc0dd1990..ae64a53e7d 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -10,8 +10,8 @@ struct INSWindowHolder { - virtual AvnWindow* _Nonnull GetNSWindow () = 0; - virtual AvnView* _Nonnull GetNSView () = 0; + virtual NSWindow* _Nonnull GetNSWindow () = 0; + virtual NSView* _Nonnull GetNSView () = 0; }; #endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 19c443806c..fd079b1427 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -51,9 +51,9 @@ BEGIN_INTERFACE_MAP() virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; - virtual AvnWindow *GetNSWindow() override; + virtual NSWindow *GetNSWindow() override; - virtual AvnView *GetNSView() override; + virtual NSView *GetNSView() override; virtual HRESULT Show(bool activate, bool isDialog) override; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 0c85dcc303..6ec026ed10 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -60,11 +60,11 @@ HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { return S_OK; } -AvnWindow *WindowBaseImpl::GetNSWindow() { +NSWindow *WindowBaseImpl::GetNSWindow() { return Window; } -AvnView *WindowBaseImpl::GetNSView() { +NSView *WindowBaseImpl::GetNSView() { return View; } From 0ac5a1c808f192b17afcbb9acc8a9d387432fab8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:16:11 +0100 Subject: [PATCH 566/820] set min and max when creating window. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 6ec026ed10..826486ae31 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -527,6 +527,10 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; + [Window setContentSize: lastSize]; + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + [Window setOpaque:false]; } } From 8be332ab1d1504ff12950821ce8136e1a8647603 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:20:49 +0100 Subject: [PATCH 567/820] make AvnWindowProtocol --- .../project.pbxproj | 12 ++++++++++ .../Avalonia.Native/src/OSX/AvnPanelWindow.h | 9 ++++++++ .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 8 +++++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 6 +++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 18 +++++++++------ native/Avalonia.Native/src/OSX/WindowImpl.mm | 8 +++---- .../Avalonia.Native/src/OSX/WindowProtocol.h | 23 +++++++++++++++++++ native/Avalonia.Native/src/OSX/window.h | 15 ++---------- native/Avalonia.Native/src/OSX/window.mm | 10 ++++---- 9 files changed, 78 insertions(+), 31 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.h create mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowProtocol.h diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 88e4c0c682..8ee2a61741 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,9 +9,11 @@ /* Begin PBXBuildFile section */ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; }; 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; + 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; @@ -19,6 +21,7 @@ 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; + 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -47,6 +50,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; }; 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; @@ -54,10 +58,12 @@ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; + 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; + 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnPanelWindow.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -166,6 +172,9 @@ 18391D1669284AD2EC9E866A /* AvnView.h */, 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, + 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */, + 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */, + 1839122E037567BDD1D09DEB /* WindowProtocol.h */, ); sourceTree = ""; }; @@ -193,6 +202,8 @@ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, + 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */, + 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -277,6 +288,7 @@ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, + 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h new file mode 100644 index 0000000000..0fe95d044d --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h @@ -0,0 +1,9 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#define NSWindow NSPanel +#define AvnWindow AvnPanelWindow + +//#include "window.h" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm new file mode 100644 index 0000000000..8b79e28ca1 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -0,0 +1,8 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#define AvnWindow AvnPanelWindow + +//#include "window.mm" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index fd079b1427..19065097fb 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -7,6 +7,7 @@ #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #import "rendertarget.h" +#import "WindowProtocol.h" #include "INSWindowHolder.h" @class AutoFitContentView; @@ -28,7 +29,7 @@ BEGIN_INTERFACE_MAP() AutoFitContentView *StandardContainer; AvnView *View; - AvnWindow *Window; + NSWindow * Window; ComPtr BaseEvents; ComPtr _glContext; NSObject *renderTarget; @@ -116,9 +117,10 @@ protected: void UpdateStyle(); + id GetWindowProtocol (); + private: void InitialiseNSWindow (); - }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 826486ae31..82c634dc4f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -100,10 +100,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window orderFront:Window]; } - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - [Window setContentSize: lastSize]; - _shown = true; return S_OK; @@ -132,7 +128,8 @@ HRESULT WindowBaseImpl::Hide() { @autoreleasepool { if (Window != nullptr) { [Window orderOut:Window]; - [Window restoreParentWindow]; + + [GetWindowProtocol() restoreParentWindow]; } return S_OK; @@ -311,10 +308,10 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { auto nsmenu = nativeMenu->GetNative(); - [Window applyMenu:nsmenu]; + [GetWindowProtocol() applyMenu:nsmenu]; if ([Window isKeyWindow]) { - [Window showWindowMenuWithAppMenu]; + [GetWindowProtocol() showWindowMenuWithAppMenu]; } return S_OK; @@ -532,5 +529,12 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; + + [Window setContentMinSize: lastMinSize]; + [Window setContentMaxSize: lastMaxSize]; } } + +id WindowBaseImpl::GetWindowProtocol() { + return static_cast>(Window); +} diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 0d325c4490..3de4f5d5a8 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,7 +20,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; WindowEvents = events; - [Window setCanBecomeKeyAndMain]; + [GetWindowProtocol() setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; @@ -68,7 +68,7 @@ HRESULT WindowImpl::SetEnabled(bool enable) { START_COM_CALL; @autoreleasepool { - [Window setEnabled:enable]; + [GetWindowProtocol() setEnabled:enable]; return S_OK; } } @@ -354,7 +354,7 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) { View.layer.zPosition = 0; } - [Window setIsExtended:enable]; + [GetWindowProtocol() setIsExtended:enable]; HideOrShowTrafficLights(); @@ -383,7 +383,7 @@ HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { return E_POINTER; } - *ret = [Window getExtendedTitleBarHeight]; + *ret = [GetWindowProtocol() getExtendedTitleBarHeight]; return S_OK; } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h new file mode 100644 index 0000000000..ac20fe8eb1 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -0,0 +1,23 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import + +@class AvnMenu; + +@protocol AvnWindowProtocol +-(void) setCanBecomeKeyAndMain; +-(void) pollModalSession: (NSModalSession _Nonnull) session; +-(void) restoreParentWindow; +-(bool) shouldTryToHandleEvents; +-(void) setEnabled: (bool) enable; +-(void) showAppMenuOnly; +-(void) showWindowMenuWithAppMenu; +-(void) applyMenu:(AvnMenu* _Nullable)menu; + +-(double) getExtendedTitleBarHeight; +-(void) setIsExtended:(bool)value; +-(bool) isDialog; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index a8c31e7b60..1a001dd6e4 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -2,25 +2,14 @@ #define window_h #import "avalonia-native.h" +#import "WindowProtocol.h" @class AvnMenu; class WindowBaseImpl; -@interface AvnWindow : NSWindow +@interface AvnWindow : NSWindow -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; --(void) setCanBecomeKeyAndMain; --(void) pollModalSession: (NSModalSession _Nonnull) session; --(void) restoreParentWindow; --(bool) shouldTryToHandleEvents; --(void) setEnabled: (bool) enable; --(void) showAppMenuOnly; --(void) showWindowMenuWithAppMenu; --(void) applyMenu:(AvnMenu* _Nullable)menu; - --(double) getExtendedTitleBarHeight; --(void) setIsExtended:(bool)value; --(bool) isDialog; @end #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 68f8f76d75..e4bfedaba4 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,11 +1,11 @@ #import -#include "common.h" +#import "common.h" #import "window.h" -#include "menu.h" -#include "automation.h" +#import "menu.h" +#import "automation.h" #import "WindowBaseImpl.h" -#include "WindowImpl.h" -#include "AvnView.h" +#import "WindowImpl.h" +#import "AvnView.h" @implementation AvnWindow { From cd9be07ced12909309b68a68934ba4371855a3c3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:30:36 +0100 Subject: [PATCH 568/820] ensure menu gets applied. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 36 ++++++++++++++----- native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 - 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 19065097fb..d52518820c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -37,6 +37,7 @@ BEGIN_INTERFACE_MAP() NSSize lastSize; NSSize lastMinSize; NSSize lastMaxSize; + AvnMenu* lastMenu; NSString *_lastTitle; bool _shown; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 82c634dc4f..bd763ce2fd 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -34,6 +34,7 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) _lastTitle = @""; Window = nullptr; + lastMenu = nullptr; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -91,6 +92,10 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window setTitle:_lastTitle]; + if(!isDialog) { + [GetWindowProtocol() setCanBecomeKeyAndMain]; + } + if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; @@ -306,12 +311,14 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { auto nativeMenu = dynamic_cast(menu); - auto nsmenu = nativeMenu->GetNative(); + lastMenu = nativeMenu->GetNative(); - [GetWindowProtocol() applyMenu:nsmenu]; + if(Window != nullptr) { + [GetWindowProtocol() applyMenu:lastMenu]; - if ([Window isKeyWindow]) { - [GetWindowProtocol() showWindowMenuWithAppMenu]; + if ([Window isKeyWindow]) { + [GetWindowProtocol() showWindowMenuWithAppMenu]; + } } return S_OK; @@ -524,17 +531,30 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; - [Window setContentSize: lastSize]; + [Window setContentSize:lastSize]; [Window setContentMinSize:lastMinSize]; [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; - [Window setContentMinSize: lastMinSize]; - [Window setContentMaxSize: lastMaxSize]; + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + + if (lastMenu != nullptr) { + [GetWindowProtocol() applyMenu:lastMenu]; + + if ([Window isKeyWindow]) { + [GetWindowProtocol() showWindowMenuWithAppMenu]; + } + } } } id WindowBaseImpl::GetWindowProtocol() { - return static_cast>(Window); + id instance; + if ([Window conformsToProtocol:@protocol(AvnWindowProtocol)]) { + instance = Window; + } + + return instance; } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 3de4f5d5a8..2a25cc69da 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,7 +20,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; WindowEvents = events; - [GetWindowProtocol() setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; From f008e403cf1e1ef18fa1d6fba631eb3048a18059 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 16:02:50 +0100 Subject: [PATCH 569/820] make it compile 2 versions is AvnWindow (NSWindow / NSPanel version) fix include mess, and pragma once. --- native/Avalonia.Native/inc/rendertarget.h | 5 + .../src/OSX/AutoFitContentView.h | 4 +- .../src/OSX/AutoFitContentView.mm | 3 +- .../project.pbxproj | 26 ++- .../Avalonia.Native/src/OSX/AvnPanelWindow.h | 9 - .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 7 +- native/Avalonia.Native/src/OSX/AvnView.h | 10 +- native/Avalonia.Native/src/OSX/AvnView.mm | 3 +- .../src/OSX/{window.mm => AvnWindow.mm} | 189 +++++++----------- native/Avalonia.Native/src/OSX/PopupImpl.h | 9 + native/Avalonia.Native/src/OSX/PopupImpl.mm | 68 +++++++ native/Avalonia.Native/src/OSX/ResizeScope.h | 1 - native/Avalonia.Native/src/OSX/ResizeScope.mm | 2 +- .../Avalonia.Native/src/OSX/SystemDialogs.mm | 1 - .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 5 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 19 +- native/Avalonia.Native/src/OSX/WindowImpl.h | 1 - native/Avalonia.Native/src/OSX/WindowImpl.mm | 6 +- .../src/OSX/WindowInterfaces.h | 17 ++ .../Avalonia.Native/src/OSX/WindowProtocol.h | 2 + native/Avalonia.Native/src/OSX/automation.h | 3 +- native/Avalonia.Native/src/OSX/automation.mm | 7 +- native/Avalonia.Native/src/OSX/main.mm | 1 - native/Avalonia.Native/src/OSX/menu.mm | 1 - native/Avalonia.Native/src/OSX/window.h | 15 -- src/Avalonia.Native/avn.idl | 1 + 26 files changed, 235 insertions(+), 180 deletions(-) delete mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.h rename native/Avalonia.Native/src/OSX/{window.mm => AvnWindow.mm} (79%) create mode 100644 native/Avalonia.Native/src/OSX/PopupImpl.h create mode 100644 native/Avalonia.Native/src/OSX/PopupImpl.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowInterfaces.h delete mode 100644 native/Avalonia.Native/src/OSX/window.h diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h index 2b0338d099..a59915777f 100644 --- a/native/Avalonia.Native/inc/rendertarget.h +++ b/native/Avalonia.Native/inc/rendertarget.h @@ -1,3 +1,8 @@ +#pragma once + +#include "com.h" +#include "comimpl.h" +#include "avalonia-native.h" @protocol IRenderTarget -(void) setNewLayer: (CALayer*) layer; diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h index 68c9fb8dc8..7f1f764141 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.h +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h @@ -3,8 +3,10 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // +#pragma once + #import -#import "avalonia-native.h" +#include "avalonia-native.h" @interface AutoFitContentView : NSView -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 92d6f67a91..4eaa08cbe2 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -4,7 +4,8 @@ // #include "AvnView.h" -#import "AutoFitContentView.h" +#include "AutoFitContentView.h" +#import "WindowInterfaces.h" @implementation AutoFitContentView { diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 8ee2a61741..6fc3977d4e 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,16 +9,19 @@ /* Begin PBXBuildFile section */ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DB45C7D892E61BF388C /* WindowInterfaces.h */; }; 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; }; 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; - 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; + 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839155B28B20FFB672D29C6 /* AvnWindow.mm */; }; + 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183910513F396141938832B5 /* PopupImpl.h */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391BB698579F40F1783F31 /* PopupImpl.mm */; }; 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; }; @@ -43,16 +46,17 @@ AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; }; AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; }; - AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; }; AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; }; BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; }; BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 183910513F396141938832B5 /* PopupImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupImpl.h; sourceTree = ""; }; 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; }; 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; + 1839155B28B20FFB672D29C6 /* AvnWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnWindow.mm; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; }; 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; @@ -60,10 +64,11 @@ 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; + 18391BB698579F40F1783F31 /* PopupImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopupImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; - 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnPanelWindow.h; sourceTree = ""; }; + 18391DB45C7D892E61BF388C /* WindowInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowInterfaces.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -78,7 +83,6 @@ 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; }; 37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; }; - 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; }; 37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; }; 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; }; @@ -92,7 +96,6 @@ AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = ""; }; AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = ""; }; @@ -148,8 +151,6 @@ AB661C212148288600291242 /* common.h */, 379860FE214DA0C000CD0246 /* KeyTransform.h */, 37E2330E21583241000CB7E2 /* KeyTransform.mm */, - AB661C1F2148286E00291242 /* window.mm */, - 37C09D8A21581EF2006A6758 /* window.h */, AB00E4F62147CA920032A60A /* main.mm */, 37155CE3233C00EB0034DCE9 /* menu.h */, 520624B222973F4100C4DCEF /* menu.mm */, @@ -173,8 +174,11 @@ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */, - 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */, 1839122E037567BDD1D09DEB /* WindowProtocol.h */, + 1839155B28B20FFB672D29C6 /* AvnWindow.mm */, + 18391DB45C7D892E61BF388C /* WindowInterfaces.h */, + 18391BB698579F40F1783F31 /* PopupImpl.mm */, + 183910513F396141938832B5 /* PopupImpl.h */, ); sourceTree = ""; }; @@ -202,8 +206,9 @@ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, - 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */, 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, + 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */, + 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -282,13 +287,14 @@ 1A465D10246AB61600C5858B /* dnd.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, - AB661C202148286E00291242 /* window.mm in Sources */, 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */, + 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */, + 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h deleted file mode 100644 index 0fe95d044d..0000000000 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Created by Dan Walmsley on 06/05/2022. -// Copyright (c) 2022 Avalonia. All rights reserved. -// - -#define NSWindow NSPanel -#define AvnWindow AvnPanelWindow - -//#include "window.h" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm index 8b79e28ca1..2365189010 100644 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -3,6 +3,9 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // -#define AvnWindow AvnPanelWindow +#pragma once + +#define IS_NSPANEL + +#include "AvnWindow.mm" -//#include "window.mm" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h index c6dd90150f..86a68d34c5 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.h +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -2,17 +2,15 @@ // Created by Dan Walmsley on 05/05/2022. // Copyright (c) 2022 Avalonia. All rights reserved. // - +#pragma once #import #import #import -#include "window.h" -#import "comimpl.h" -#import "common.h" -#import "WindowImpl.h" -#import "KeyTransform.h" +#include "common.h" +#include "WindowImpl.h" +#include "KeyTransform.h" @class AvnAccessibilityElement; diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 24cbc25502..e6cf73755b 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -4,8 +4,9 @@ // #import -#import "AvnView.h" +#include "AvnView.h" #include "automation.h" +#import "WindowInterfaces.h" @implementation AvnView { diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm similarity index 79% rename from native/Avalonia.Native/src/OSX/window.mm rename to native/Avalonia.Native/src/OSX/AvnWindow.mm index e4bfedaba4..09534a0a4b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -1,13 +1,32 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + + #import -#import "common.h" -#import "window.h" -#import "menu.h" -#import "automation.h" +#import "WindowProtocol.h" #import "WindowBaseImpl.h" -#import "WindowImpl.h" -#import "AvnView.h" -@implementation AvnWindow +#ifdef IS_NSPANEL +#define BASE_CLASS NSPanel +#define CLASS_NAME AvnPanel +#else +#define BASE_CLASS NSWindow +#define CLASS_NAME AvnWindow +#endif + +#import +#include "common.h" +#include "menu.h" +#include "automation.h" +#include "WindowBaseImpl.h" +#include "WindowImpl.h" +#include "AvnView.h" +#include "WindowInterfaces.h" +#include "PopupImpl.h" + +@implementation CLASS_NAME { ComPtr _parent; bool _canBecomeKeyAndMain; @@ -66,7 +85,7 @@ - (void)pollModalSession:(nonnull NSModalSession)session { auto response = [NSApp runModalSession:session]; - + if(response == NSModalResponseContinue) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -85,18 +104,18 @@ if(_menu != nullptr) { auto appMenuItem = ::GetAppMenuItem(); - + if(appMenuItem != nullptr) { auto appMenu = [appMenuItem menu]; - + [appMenu removeItem:appMenuItem]; - + [_menu insertItem:appMenuItem atIndex:0]; - + [_menu setHasGlobalMenuItem:true]; } - + [NSApp setMenu:_menu]; } else @@ -108,22 +127,22 @@ -(void) showAppMenuOnly { auto appMenuItem = ::GetAppMenuItem(); - + if(appMenuItem != nullptr) { auto appMenu = ::GetAppMenu(); - + auto nativeAppMenu = dynamic_cast(appMenu); - + [[appMenuItem menu] removeItem:appMenuItem]; - + if(_menu != nullptr) { [_menu setHasGlobalMenuItem:false]; } - + [nativeAppMenu->GetNative() addItem:appMenuItem]; - + [NSApp setMenu:nativeAppMenu->GetNative()]; } } @@ -134,7 +153,7 @@ { menu = [AvnMenu new]; } - + _menu = menu; } @@ -143,19 +162,19 @@ _canBecomeKeyAndMain = true; } --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ // create nswindow with specific contentRect, otherwise we wont be able to resize the window // until several ms after the window is physically on the screen. self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; - + [self setReleasedWhenClosed:false]; _parent = parent; [self setDelegate:self]; _closed = false; _isEnabled = true; - + [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; @@ -167,12 +186,12 @@ - (BOOL)windowShouldClose:(NSWindow *)sender { auto window = dynamic_cast(_parent.getRaw()); - + if(window != nullptr) { return !window->WindowEvents->Closing(); } - + return true; } @@ -201,16 +220,17 @@ // If the window has a child window being shown as a dialog then don't allow it to become the key window. for(NSWindow* uch in [self childWindows]) { - auto ch = objc_cast(uch); + // TODO protocol + auto ch = objc_cast(uch); if(ch == nil) continue; if (ch.isDialog) return false; } - + return true; } - + return false; } @@ -232,7 +252,7 @@ -(void)becomeKeyWindow { [self showWindowMenuWithAppMenu]; - + if(_parent != nullptr) { _parent->BaseEvents->Activated(); @@ -243,7 +263,8 @@ -(void) restoreParentWindow; { - auto parent = objc_cast([self parentWindow]); + // TODO protocol + auto parent = objc_cast([self parentWindow]); if(parent != nil) { [parent removeChildWindow:self]; @@ -253,7 +274,7 @@ - (void)windowDidMiniaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -263,7 +284,7 @@ - (void)windowDidDeminiaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -273,7 +294,7 @@ - (void)windowDidResize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -283,7 +304,7 @@ - (void)windowWillExitFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->StartStateTransition(); @@ -293,22 +314,22 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->EndStateTransition(); - + if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized) { NSRect screenRect = [[self screen] visibleFrame]; [self setFrame:screenRect display:YES]; } - + if(parent->WindowState() == Minimized) { [self miniaturize:nullptr]; } - + parent->WindowStateChanged(); } } @@ -316,7 +337,7 @@ - (void)windowWillEnterFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->StartStateTransition(); @@ -326,7 +347,7 @@ - (void)windowDidEnterFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->EndStateTransition(); @@ -343,20 +364,20 @@ { if(_parent) _parent->BaseEvents->Deactivated(); - + [self showAppMenuOnly]; - + [super resignKeyWindow]; } - (void)windowDidMove:(NSNotification *)notification { AvnPoint position; - + if(_parent != nullptr) { auto cparent = dynamic_cast(_parent.getRaw()); - + if(cparent != nullptr) { if(cparent->WindowState() == Maximized) @@ -364,7 +385,7 @@ cparent->SetWindowState(Normal); } } - + _parent->GetPosition(&position); _parent->BaseEvents->PositionChanged(position); } @@ -379,7 +400,7 @@ - (void)sendEvent:(NSEvent *)event { [super sendEvent:event]; - + /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast. if(_parent != nullptr && dynamic_cast(_parent.getRaw()) != nullptr) { @@ -390,30 +411,30 @@ AvnView* view = _parent->View; NSPoint windowPoint = [event locationInWindow]; NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; - + if (!NSPointInRect(viewPoint, view.bounds)) { auto avnPoint = [AvnView toAvnPoint:windowPoint]; auto point = [self translateLocalPoint:avnPoint]; AvnVector delta = { 0, 0 }; - + _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } } - break; - + break; + case NSEventTypeMouseEntered: { _parent->UpdateCursor(); } - break; - + break; + case NSEventTypeMouseExited: { [[NSCursor arrowCursor] set]; } - break; - + break; + default: break; } @@ -422,63 +443,3 @@ @end -class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup -{ -private: - BEGIN_INTERFACE_MAP() - INHERIT_INTERFACE_MAP(WindowBaseImpl) - INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) - END_INTERFACE_MAP() - virtual ~PopupImpl(){} - ComPtr WindowEvents; - PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) - { - WindowEvents = events; - [Window setLevel:NSPopUpMenuWindowLevel]; - } -protected: - virtual NSWindowStyleMask GetStyle() override - { - return NSWindowStyleMaskBorderless; - } - - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - [Window setContentSize:NSSize{x, y}]; - - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; - } - - return S_OK; - } - } -public: - virtual bool ShouldTakeFocusOnShow() override - { - return false; - } -}; - -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) -{ - @autoreleasepool - { - IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); - return ptr; - } -} - -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) -{ - @autoreleasepool - { - IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); - return ptr; - } -} diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.h b/native/Avalonia.Native/src/OSX/PopupImpl.h new file mode 100644 index 0000000000..451019a6a4 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PopupImpl.h @@ -0,0 +1,9 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H +#define AVALONIA_NATIVE_OSX_POPUPIMPL_H + +#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm new file mode 100644 index 0000000000..64a8780158 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -0,0 +1,68 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#include "WindowInterfaces.h" +#include "AvnView.h" +#include "WindowImpl.h" +#include "automation.h" +#include "menu.h" +#include "common.h" +#import "WindowBaseImpl.h" +#import "WindowProtocol.h" +#import +#include "PopupImpl.h" + +class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup +{ +private: + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) + END_INTERFACE_MAP() + virtual ~PopupImpl(){} + ComPtr WindowEvents; + PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) + { + WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; + } +protected: + virtual NSWindowStyleMask GetStyle() override + { + return NSWindowStyleMaskBorderless; + } + + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override + { + START_COM_CALL; + + @autoreleasepool + { + if (Window != nullptr) + { + [Window setContentSize:NSSize{x, y}]; + + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; + } + + return S_OK; + } + } +public: + virtual bool ShouldTakeFocusOnShow() override + { + return false; + } +}; + + +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) +{ + @autoreleasepool + { + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); + return ptr; + } +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h index 7509f93c01..9a43c158fe 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.h +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -6,7 +6,6 @@ #ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H #define AVALONIA_NATIVE_OSX_RESIZESCOPE_H -#include "window.h" #include "avalonia-native.h" @class AvnView; diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm index 90e7f5cf15..9f1177af8b 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.mm +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -5,7 +5,7 @@ #import #include "ResizeScope.h" -#import "AvnView.h" +#include "AvnView.h" ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { _view = view; diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index 21ad9cfa7c..535b6c3b66 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,4 @@ #include "common.h" -#include "window.h" #include "INSWindowHolder.h" class SystemDialogs : public ComSingleObject diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index d52518820c..a8f549b3c6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -6,11 +6,12 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H -#import "rendertarget.h" -#import "WindowProtocol.h" +#include "rendertarget.h" #include "INSWindowHolder.h" @class AutoFitContentView; +@class AvnMenu; +@protocol AvnWindowProtocol; class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bd763ce2fd..a58d0bb8be 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -5,14 +5,14 @@ #import #include "common.h" -#import "window.h" -#import "AvnView.h" +#include "AvnView.h" #include "menu.h" #include "automation.h" -#import "cursor.h" +#include "cursor.h" #include "ResizeScope.h" -#import "AutoFitContentView.h" -#include "WindowBaseImpl.h" +#include "AutoFitContentView.h" +#import "WindowProtocol.h" +#import "WindowInterfaces.h" WindowBaseImpl::~WindowBaseImpl() { @@ -558,3 +558,12 @@ id WindowBaseImpl::GetWindowProtocol() { return instance; } + +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) +{ + @autoreleasepool + { + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); + return ptr; + } +} diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index b229921baa..a4ee4f447c 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -6,7 +6,6 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWIMPL_H - #import "WindowBaseImpl.h" #include "IWindowStateChanged.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 2a25cc69da..9992d64b47 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -4,10 +4,10 @@ // #import -#import "window.h" -#import "AutoFitContentView.h" -#import "AvnView.h" +#include "AutoFitContentView.h" +#include "AvnView.h" #include "automation.h" +#include "WindowProtocol.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { _isClientAreaExtended = false; diff --git a/native/Avalonia.Native/src/OSX/WindowInterfaces.h b/native/Avalonia.Native/src/OSX/WindowInterfaces.h new file mode 100644 index 0000000000..6e6d62e85e --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowInterfaces.h @@ -0,0 +1,17 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import +#include "WindowProtocol.h" +#include "WindowBaseImpl.h" + +@interface AvnWindow : NSWindow +-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +@end + +@interface AvnPanel : NSPanel +-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index ac20fe8eb1..d81d2f1ed1 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -3,6 +3,8 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // +#pragma once + #import @class AvnMenu; diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 1727d21f27..367df3619d 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,6 @@ -#import +#pragma once +#import NS_ASSUME_NONNULL_BEGIN class IAvnAutomationPeer; diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7a0b3b8127..d0c8d7a9db 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,9 +1,8 @@ #include "common.h" -#import "automation.h" -#import "window.h" +#include "automation.h" #include "AvnString.h" -#import "INSWindowHolder.h" -#import "AvnView.h" +#include "INSWindowHolder.h" +#include "AvnView.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 011f881e94..6ee86b21ae 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -1,7 +1,6 @@ //This file will contain actual IID structures #define COM_GUIDS_MATERIALIZE #include "common.h" -#include "window.h" static NSString* s_appTitle = @"Avalonia"; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index fd74edd772..b05588a441 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -1,7 +1,6 @@ #include "common.h" #include "menu.h" -#import "window.h" #include "KeyTransform.h" #include #include /* For kVK_ constants, and TIS functions. */ diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h deleted file mode 100644 index 1a001dd6e4..0000000000 --- a/native/Avalonia.Native/src/OSX/window.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef window_h -#define window_h - -#import "avalonia-native.h" -#import "WindowProtocol.h" - -@class AvnMenu; - -class WindowBaseImpl; - -@interface AvnWindow : NSWindow --(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; -@end - -#endif /* window_h */ diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 8092004989..d6ef0f8918 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -2,6 +2,7 @@ @clr-access internal @clr-map bool int @cpp-preamble @@ +#pragma once #include "com.h" #include "stddef.h" @@ From 1cca34f56ede6b1c4b2169b64a74a9832331e536 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 17:30:03 +0100 Subject: [PATCH 570/820] actually create nspanels for dialogs. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 35 ++++++++----------- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 28 ++++++++++----- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 - 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 09534a0a4b..d0b23540f9 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -29,7 +29,6 @@ @implementation CLASS_NAME { ComPtr _parent; - bool _canBecomeKeyAndMain; bool _closed; bool _isEnabled; bool _isExtended; @@ -157,11 +156,6 @@ _menu = menu; } --(void) setCanBecomeKeyAndMain -{ - _canBecomeKeyAndMain = true; -} - -(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ @@ -215,28 +209,27 @@ -(BOOL)canBecomeKeyWindow { - if (_canBecomeKeyAndMain) + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + for(NSWindow* uch in [self childWindows]) { - // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) - { - // TODO protocol - auto ch = objc_cast(uch); - if(ch == nil) - continue; - if (ch.isDialog) - return false; - } - - return true; + // TODO protocol + auto ch = objc_cast(uch); + if(ch == nil) + continue; + if (ch.isDialog) + return false; } - return false; + return true; } -(BOOL)canBecomeMainWindow { - return _canBecomeKeyAndMain; +#ifdef IS_NSPANEL + return false; +#else + return true; +#endif } -(bool)shouldTryToHandleEvents diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index a8f549b3c6..ae1e6a7016 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -122,6 +122,7 @@ protected: id GetWindowProtocol (); private: + void CreateNSWindow (bool isDialog); void InitialiseNSWindow (); }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index a58d0bb8be..414632770f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -13,6 +13,7 @@ #include "AutoFitContentView.h" #import "WindowProtocol.h" #import "WindowInterfaces.h" +#include "WindowBaseImpl.h" WindowBaseImpl::~WindowBaseImpl() { @@ -31,6 +32,9 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastPositionSet.X = 100; lastPositionSet.Y = 100; + lastSize = NSSize { 100, 100 }; + lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; + lastMinSize = NSSize { 0, 0 }; _lastTitle = @""; Window = nullptr; @@ -85,6 +89,7 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + CreateNSWindow(isDialog); InitialiseNSWindow(); SetPosition(lastPositionSet); @@ -92,10 +97,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window setTitle:_lastTitle]; - if(!isDialog) { - [GetWindowProtocol() setCanBecomeKeyAndMain]; - } - if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; @@ -524,9 +525,21 @@ void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; } -void WindowBaseImpl::InitialiseNSWindow() { +void WindowBaseImpl::CreateNSWindow(bool isDialog) { if(Window == nullptr) { - Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + if(isDialog) + { + Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + } + else + { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + } + } +} + +void WindowBaseImpl::InitialiseNSWindow() { + if(Window != nullptr) { [Window setContentView:StandardContainer]; [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; @@ -537,9 +550,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setOpaque:false]; - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu]; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 9992d64b47..63a38f0c22 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -507,7 +507,7 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = NSWindowStyleMaskBorderless; + unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless; switch (_decorations) { case SystemDecorationsNone: diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index d81d2f1ed1..1c97d89f39 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -10,7 +10,6 @@ @class AvnMenu; @protocol AvnWindowProtocol --(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; -(bool) shouldTryToHandleEvents; From d6a4a6c901fec82f9675c3f70e72a7e01c008fb5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 18:04:57 +0100 Subject: [PATCH 571/820] ensure cast to protocol instead of concrete types. --- native/Avalonia.Native/src/OSX/AutoFitContentView.mm | 5 +++-- native/Avalonia.Native/src/OSX/AvnView.mm | 7 ++++++- native/Avalonia.Native/src/OSX/AvnWindow.mm | 6 ++---- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++---- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 4eaa08cbe2..314c579b76 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -5,7 +5,8 @@ #include "AvnView.h" #include "AutoFitContentView.h" -#import "WindowInterfaces.h" +#include "WindowInterfaces.h" +#include "WindowProtocol.h" @implementation AutoFitContentView { @@ -84,7 +85,7 @@ _settingSize = true; [super setFrameSize:newSize]; - auto window = objc_cast([self window]); + auto window = static_cast>([self window]); // TODO get actual titlebar size diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index e6cf73755b..02526afbcb 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -195,7 +195,12 @@ - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { - auto parentWindow = objc_cast([self window]); + if(_parent == nullptr) + { + return TRUE; + } + + auto parentWindow = _parent->GetWindowProtocol(); if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) { diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index d0b23540f9..ef4dcc3df4 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -212,8 +212,7 @@ // If the window has a child window being shown as a dialog then don't allow it to become the key window. for(NSWindow* uch in [self childWindows]) { - // TODO protocol - auto ch = objc_cast(uch); + auto ch = static_cast>(uch); if(ch == nil) continue; if (ch.isDialog) @@ -256,8 +255,7 @@ -(void) restoreParentWindow; { - // TODO protocol - auto parent = objc_cast([self parentWindow]); + auto parent = static_cast>([self parentWindow]); if(parent != nil) { [parent removeChildWindow:self]; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index ae1e6a7016..8c82bba98c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -114,13 +114,13 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog(); + id GetWindowProtocol (); + protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); - id GetWindowProtocol (); - private: void CreateNSWindow (bool isDialog); void InitialiseNSWindow (); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 414632770f..227f348333 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -561,12 +561,12 @@ void WindowBaseImpl::InitialiseNSWindow() { } id WindowBaseImpl::GetWindowProtocol() { - id instance; - if ([Window conformsToProtocol:@protocol(AvnWindowProtocol)]) { - instance = Window; + if(Window == nullptr) + { + return nullptr; } - return instance; + return static_cast>(Window); } extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) From 99e07e68d592771273aa4b795682603d2507d973 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 18:07:01 +0100 Subject: [PATCH 572/820] remove unnecessary cast. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index ef4dcc3df4..33f7f75314 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -255,7 +255,8 @@ -(void) restoreParentWindow; { - auto parent = static_cast>([self parentWindow]); + auto parent = [self parentWindow]; + if(parent != nil) { [parent removeChildWindow:self]; From 17b9f246f3a2ed510a560584b589e2a170dadce2 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 6 May 2022 21:01:07 +0200 Subject: [PATCH 573/820] Text hit testing fixes --- .../Media/TextFormatting/TextLineImpl.cs | 15 ++++-- .../Media/TextFormatting/TextShaperOptions.cs | 4 +- .../Presenters/TextPresenter.cs | 5 -- src/Avalonia.Controls/TextBlock.cs | 6 ++- .../HeadlessPlatformStubs.cs | 2 +- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 2 +- .../Media/TextShaperImpl.cs | 2 +- .../Media/TextFormatting/TextLineTests.cs | 50 ++++++++++++------- .../HarfBuzzTextShaperImpl.cs | 2 +- .../Avalonia.UnitTests/MockTextShaperImpl.cs | 2 +- 10 files changed, 55 insertions(+), 35 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 73ec055bbe..26e73cdf3b 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -407,6 +407,7 @@ namespace Avalonia.Media.TextFormatting var currentPosition = FirstTextSourceIndex; var currentRect = Rect.Empty; var startX = Start; + var runStart = startX; //A portion of the line is covered. for (var index = 0; index < TextRuns.Count; index++) @@ -431,7 +432,7 @@ namespace Avalonia.Media.TextFormatting { case ShapedTextCharacters when currentRun is ShapedTextCharacters: { - if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) + if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End && _flowDirection == FlowDirection.LeftToRight) { goto skip; } @@ -480,7 +481,7 @@ namespace Avalonia.Media.TextFormatting case ShapedTextCharacters shapedRun: { endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( - shapedRun.ShapedBuffer.IsLeftToRight ? + shapedRun.ShapedBuffer.IsLeftToRight ? new CharacterHit(firstTextSourceCharacterIndex + textLength) : new CharacterHit(firstTextSourceCharacterIndex)); @@ -493,7 +494,7 @@ namespace Avalonia.Media.TextFormatting startX += startOffset; - var characterHit = shapedRun.GlyphRun.IsLeftToRight ? + var characterHit = _flowDirection == FlowDirection.LeftToRight ? shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) : shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); @@ -580,6 +581,11 @@ namespace Avalonia.Media.TextFormatting { break; } + + if (_flowDirection == FlowDirection.RightToLeft) + { + endX += currentRun.Size.Width - endOffset; + } } else { @@ -591,8 +597,9 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width - endOffset; } - lastDirection = currentDirection; startX = endX; + lastDirection = currentDirection; + runStart += currentRun.Size.Width; } return result; diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs index a7fe92dc9a..4e75bb921e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs @@ -16,7 +16,7 @@ namespace Avalonia.Media.TextFormatting { Typeface = typeface; FontRenderingEmSize = fontRenderingEmSize; - BidLevel = bidiLevel; + BidiLevel = bidiLevel; Culture = culture; IncrementalTabWidth = incrementalTabWidth; } @@ -33,7 +33,7 @@ namespace Avalonia.Media.TextFormatting /// /// Get the bidi level of the text. /// - public sbyte BidLevel { get; } + public sbyte BidiLevel { get; } /// /// Get the culture. diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 0785149a73..62ea05c68c 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -515,11 +515,6 @@ namespace Avalonia.Controls.Presenters protected override Size MeasureOverride(Size availableSize) { - if (string.IsNullOrEmpty(Text)) - { - return new Size(); - } - _constraint = availableSize; _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index bbe6aeb7ee..1a69d1218c 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -631,7 +631,11 @@ namespace Avalonia.Controls return finalSize; } - _constraint = new Size(finalSize.Width, double.PositiveInfinity); + var scale = LayoutHelper.GetLayoutScale(this); + + var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); + + _constraint = new Size(finalSize.Deflate(padding).Width, double.PositiveInfinity); _textLayout = null; diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 083b16c107..22ff8e8f97 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -137,7 +137,7 @@ namespace Avalonia.Headless { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; - var bidiLevel = options.BidLevel; + var bidiLevel = options.BidiLevel; return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); } diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index 908b0ffa47..777e907617 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -16,7 +16,7 @@ namespace Avalonia.Skia { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; - var bidiLevel = options.BidLevel; + var bidiLevel = options.BidiLevel; var culture = options.Culture; using (var buffer = new Buffer()) diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index f4e4b00147..6e32d32913 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -16,7 +16,7 @@ namespace Avalonia.Direct2D1.Media { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; - var bidiLevel = options.BidLevel; + var bidiLevel = options.BidiLevel; var culture = options.Culture; using (var buffer = new Buffer()) diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index a47638d2ec..f29dddf86b 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -718,31 +718,45 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); - var text = "0123".AsMemory(); - var ltrOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); - var rtlOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 1, CultureInfo.CurrentCulture); - - var textRuns = new List - { - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text), ltrOptions), defaultProperties), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length, text.Length), ltrOptions), defaultProperties), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2, text.Length), rtlOptions), defaultProperties), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 3, text.Length), ltrOptions), defaultProperties) - }; - - - var textSource = new FixedRunsTextSource(textRuns); + var text = "אאא AAA"; + var textSource = new SingleBufferTextSource(text, defaultProperties); var formatter = new TextFormatterImpl(); var textLine = - formatter.FormatLine(textSource, 0, double.PositiveInfinity, - new GenericTextParagraphProperties(defaultProperties)); + formatter.FormatLine(textSource, 0, 200, + new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left, true, true, defaultProperties, TextWrapping.NoWrap, 0, 0)); - var textBounds = textLine.GetTextBounds(0, text.Length * 4); + var textBounds = textLine.GetTextBounds(0, text.Length); - Assert.Equal(3, textBounds.Count); + Assert.Equal(2, textBounds.Count); Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + + textBounds = textLine.GetTextBounds(0, 4); + + var secondRun = textLine.TextRuns[1] as ShapedTextCharacters; + + Assert.Equal(1, textBounds.Count); + Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); + + textBounds = textLine.GetTextBounds(4, 3); + + var firstRun = textLine.TextRuns[0] as ShapedTextCharacters; + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); + + textBounds = textLine.GetTextBounds(0, 5); + + Assert.Equal(2, textBounds.Count); + + Assert.Equal(7.201171875, textBounds[0].Rectangle.Width); + + Assert.Equal(textLine.Start, textBounds[0].Rectangle.Left); + + Assert.Equal(secondRun.Size.Width, textBounds[1].Rectangle.Width); + + Assert.Equal(textLine.Start + firstRun.Size.Width, textBounds[1].Rectangle.Left); } } diff --git a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs index 5f8854b3ab..4bc30484e9 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs @@ -15,7 +15,7 @@ namespace Avalonia.UnitTests { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; - var bidiLevel = options.BidLevel; + var bidiLevel = options.BidiLevel; var culture = options.Culture; using (var buffer = new Buffer()) diff --git a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs index c4b1e6c154..7c34bd192e 100644 --- a/tests/Avalonia.UnitTests/MockTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/MockTextShaperImpl.cs @@ -11,7 +11,7 @@ namespace Avalonia.UnitTests { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; - var bidiLevel = options.BidLevel; + var bidiLevel = options.BidiLevel; var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); From 45ed0194254fa28b7a82b25de5f381da6be147a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sat, 7 May 2022 13:54:55 +0100 Subject: [PATCH 574/820] Respect Window MaxWidth and MaxHeight when using any SizeToContent to Auto --- src/Avalonia.Controls/Window.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index a5f99918b2..d0162bb0de 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -917,6 +917,15 @@ namespace Avalonia.Controls var constraint = clientSize; var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; + if (MaxWidth > 0 && MaxWidth < maxAutoSize.Width) + { + maxAutoSize = maxAutoSize.WithWidth(MaxWidth); + } + if (MaxHeight > 0 && MaxHeight < maxAutoSize.Height) + { + maxAutoSize = maxAutoSize.WithHeight(MaxHeight); + } + if (sizeToContent.HasAllFlags(SizeToContent.Width)) { constraint = constraint.WithWidth(maxAutoSize.Width); From 3600639d39482ef60efbb573f4586d8019da745a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 8 May 2022 19:17:08 +0100 Subject: [PATCH 575/820] re-create NSWindow if we call show and need to swap out for a dialog. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 4 ++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 21 ++++++++++++++----- .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 33f7f75314..6ff19ead68 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -433,5 +433,9 @@ } } +- (void)disconnectParent { + _parent = nullptr; +} + @end diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 8c82bba98c..eff13bcb23 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -123,6 +123,7 @@ protected: private: void CreateNSWindow (bool isDialog); + void CleanNSWindow (); void InitialiseNSWindow (); }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 227f348333..a7c38bf652 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -525,14 +525,25 @@ void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; } +void WindowBaseImpl::CleanNSWindow() { + if(Window != nullptr) { + [GetWindowProtocol() disconnectParent]; + [Window close]; + Window = nullptr; + } +} + void WindowBaseImpl::CreateNSWindow(bool isDialog) { - if(Window == nullptr) { - if(isDialog) - { + if (isDialog) { + if (![Window isKindOfClass:[AvnPanel class]]) { + CleanNSWindow(); + Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; } - else - { + } else { + if (![Window isKindOfClass:[AvnWindow class]]) { + CleanNSWindow(); + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; } } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 1c97d89f39..92194706de 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -20,5 +20,6 @@ -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; +-(void) disconnectParent; -(bool) isDialog; @end \ No newline at end of file From c756f812ec887cbdf77fd2d3d420a7164f25ee0f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 9 May 2022 11:19:20 +0100 Subject: [PATCH 576/820] remove shadow invalidation hack. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index a7c38bf652..db5eb54e3f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -286,7 +286,6 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if(Window != nullptr) { [Window setContentSize:lastSize]; - [Window invalidateShadow]; } } @finally { From 5a027c585ab9f827bea077cc0a23f9f0579f9bce Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 9 May 2022 12:44:07 +0100 Subject: [PATCH 577/820] Merge pull request #8103 from AvaloniaUI/fixes/deterministic-builds Fixes/deterministic builds --- .../CompilerExtensions/XamlIlClrPropertyInfoHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs index 871a2a2045..e76e2cd46e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs @@ -55,7 +55,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return cached.get; } - var name = lst.Count == 0 ? key : key + "_" + Guid.NewGuid().ToString("N"); + var name = lst.Count == 0 ? key : key + "_" + context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); var field = _builder.DefineField(types.IPropertyInfo, name + "!Field", false, true); From a92a49b04fb44103c0ec5506564ee7f0ab87dbfa Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 9 May 2022 22:14:35 +0300 Subject: [PATCH 578/820] Updated Rotate3DTransform to the new OnPropertyChanged --- src/Avalonia.Base/Rotate3DTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Rotate3DTransform.cs b/src/Avalonia.Base/Rotate3DTransform.cs index 306363ec39..2c4e515861 100644 --- a/src/Avalonia.Base/Rotate3DTransform.cs +++ b/src/Avalonia.Base/Rotate3DTransform.cs @@ -203,7 +203,7 @@ public class Rotate3DTransform : Transform } } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { if (!_isInitializing) RaiseChanged(); } From 2f1ffbd81e6d573a6d5dfa6b06647a0d071d3903 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 9 May 2022 23:52:15 +0300 Subject: [PATCH 579/820] Make ThreadSafeObjectPool actually thread safe (#8106) * Make ThreadSafeObjectPool actually thread safe --- src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs index c6845485dc..827a02334a 100644 --- a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs +++ b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs @@ -5,12 +5,11 @@ namespace Avalonia.Threading public class ThreadSafeObjectPool where T : class, new() { private Stack _stack = new Stack(); - private object _lock = new object(); public static ThreadSafeObjectPool Default { get; } = new ThreadSafeObjectPool(); public T Get() { - lock (_lock) + lock (_stack) { if(_stack.Count == 0) return new T(); From c175ef318ea68a5a9e84ea7ff6140119f66627c0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 00:26:27 -0400 Subject: [PATCH 580/820] Restructure native embed sample project --- Avalonia.sln | 27 ++++ .../NativeEmbedSample.Desktop.csproj | 15 +++ .../NativeEmbedSample.Desktop/Program.cs | 17 +++ .../Android/EmbedSample.Android.cs | 50 ++++++++ .../NativeEmbedSample/{App.xaml => App.axaml} | 3 +- .../interop/NativeEmbedSample/App.axaml.cs | 23 ++++ samples/interop/NativeEmbedSample/App.xaml.cs | 22 ---- .../interop/NativeEmbedSample/EmbedSample.cs | 115 ++++-------------- .../NativeEmbedSample/Gtk/EmbedSample.Gtk.cs | 41 +++++++ .../NativeEmbedSample/Gtk/GtkHelper.cs | 62 ++++++++++ .../{ => Gtk}/nodes-license.md | 0 .../NativeEmbedSample/{ => Gtk}/nodes.mp4 | Bin .../interop/NativeEmbedSample/GtkHelper.cs | 58 --------- .../NativeEmbedSample/Mac/EmbedSample.Mac.cs | 32 +++++ .../NativeEmbedSample/Mac/MacHelper.cs | 39 ++++++ .../interop/NativeEmbedSample/MacHelper.cs | 39 ------ .../interop/NativeEmbedSample/MainView.axaml | 64 ++++++++++ .../NativeEmbedSample/MainView.axaml.cs | 45 +++++++ .../NativeEmbedSample/MainWindow.axaml | 10 ++ .../NativeEmbedSample/MainWindow.axaml.cs | 17 +++ .../interop/NativeEmbedSample/MainWindow.xaml | 52 -------- .../NativeEmbedSample/MainWindow.xaml.cs | 36 ------ .../NativeEmbedSample.csproj | 24 ++-- samples/interop/NativeEmbedSample/Program.cs | 17 --- .../NativeEmbedSample/Win/EmbedSample.Win.cs | 37 ++++++ .../interop/NativeEmbedSample/Win/WinApi.cs | 75 ++++++++++++ samples/interop/NativeEmbedSample/WinApi.cs | 74 ----------- .../NativeEmbedSample/iOS/EmbedSample.iOS.cs | 65 ++++++++++ 28 files changed, 657 insertions(+), 402 deletions(-) create mode 100644 samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj create mode 100644 samples/interop/NativeEmbedSample.Desktop/Program.cs create mode 100644 samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs rename samples/interop/NativeEmbedSample/{App.xaml => App.axaml} (57%) create mode 100644 samples/interop/NativeEmbedSample/App.axaml.cs delete mode 100644 samples/interop/NativeEmbedSample/App.xaml.cs create mode 100644 samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs create mode 100644 samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs rename samples/interop/NativeEmbedSample/{ => Gtk}/nodes-license.md (100%) rename samples/interop/NativeEmbedSample/{ => Gtk}/nodes.mp4 (100%) delete mode 100644 samples/interop/NativeEmbedSample/GtkHelper.cs create mode 100644 samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs create mode 100644 samples/interop/NativeEmbedSample/Mac/MacHelper.cs delete mode 100644 samples/interop/NativeEmbedSample/MacHelper.cs create mode 100644 samples/interop/NativeEmbedSample/MainView.axaml create mode 100644 samples/interop/NativeEmbedSample/MainView.axaml.cs create mode 100644 samples/interop/NativeEmbedSample/MainWindow.axaml create mode 100644 samples/interop/NativeEmbedSample/MainWindow.axaml.cs delete mode 100644 samples/interop/NativeEmbedSample/MainWindow.xaml delete mode 100644 samples/interop/NativeEmbedSample/MainWindow.xaml.cs delete mode 100644 samples/interop/NativeEmbedSample/Program.cs create mode 100644 samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs create mode 100644 samples/interop/NativeEmbedSample/Win/WinApi.cs delete mode 100644 samples/interop/NativeEmbedSample/WinApi.cs create mode 100644 samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs diff --git a/Avalonia.sln b/Avalonia.sln index c8e513f94c..0adc29ffe8 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -219,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1989,6 +1991,30 @@ Global {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhone.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhone.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|Any CPU.Build.0 = Release|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.ActiveCfg = Release|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.Build.0 = Release|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2048,6 +2074,7 @@ Global {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} + {F2389463-DDB4-4317-B894-D4DF9FF6B763} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj b/samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj new file mode 100644 index 0000000000..1ec852ab6d --- /dev/null +++ b/samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + + + + + + + + + + diff --git a/samples/interop/NativeEmbedSample.Desktop/Program.cs b/samples/interop/NativeEmbedSample.Desktop/Program.cs new file mode 100644 index 0000000000..01684d0301 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Desktop/Program.cs @@ -0,0 +1,17 @@ +using Avalonia; +using NativeEmbedSample; + +namespace NativeEmbedSample.Desktop; + +public class Program +{ + static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .With(new AvaloniaNativePlatformOptions() + { + }) + .UsePlatformDetect(); + +} diff --git a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs b/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs new file mode 100644 index 0000000000..ed3b9aeeb0 --- /dev/null +++ b/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs @@ -0,0 +1,50 @@ +#if __ANDROID__ || ANDROID +using System; +using System.IO; +using System.Diagnostics; +using Android.Views; +using Android.Webkit; +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +namespace NativeEmbedSample; + +public partial class EmbedSample +{ + private IPlatformHandle CreateAndroid(IPlatformHandle parent) + { + var button = new Android.Widget.Button(Android.App.Application.Context) { Text = "Android button" }; + + return new AndroidViewHandle(button); + } + + private void DestroyAndroid(IPlatformHandle control) + { + base.DestroyNativeControlCore(control); + } +} + +internal sealed class AndroidViewHandle : INativeControlHostDestroyableControlHandle +{ + private View _view; + + public AndroidViewHandle(View view) + { + _view = view; + } + + public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; + public string HandleDescriptor => "JavaHandle"; + + public void Destroy() + { + _view?.Dispose(); + _view = null; + } + + ~AndroidViewHandle() + { + Destroy(); + } +} +#endif diff --git a/samples/interop/NativeEmbedSample/App.xaml b/samples/interop/NativeEmbedSample/App.axaml similarity index 57% rename from samples/interop/NativeEmbedSample/App.xaml rename to samples/interop/NativeEmbedSample/App.axaml index e35ade4087..d6f182ed3f 100644 --- a/samples/interop/NativeEmbedSample/App.xaml +++ b/samples/interop/NativeEmbedSample/App.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="NativeEmbedSample.App"> - - + diff --git a/samples/interop/NativeEmbedSample/App.axaml.cs b/samples/interop/NativeEmbedSample/App.axaml.cs new file mode 100644 index 0000000000..0a89ea441b --- /dev/null +++ b/samples/interop/NativeEmbedSample/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace NativeEmbedSample; + +public class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + desktopLifetime.MainWindow = new MainWindow(); + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + singleViewLifetime.MainView = new MainView(); + + base.OnFrameworkInitializationCompleted(); + } +} diff --git a/samples/interop/NativeEmbedSample/App.xaml.cs b/samples/interop/NativeEmbedSample/App.xaml.cs deleted file mode 100644 index cb17cfc35d..0000000000 --- a/samples/interop/NativeEmbedSample/App.xaml.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace NativeEmbedSample -{ - public class App : Application - { - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) - desktopLifetime.MainWindow = new MainWindow(); - - base.OnFrameworkInitializationCompleted(); - } - } -} diff --git a/samples/interop/NativeEmbedSample/EmbedSample.cs b/samples/interop/NativeEmbedSample/EmbedSample.cs index ab9df11e19..340068058f 100644 --- a/samples/interop/NativeEmbedSample/EmbedSample.cs +++ b/samples/interop/NativeEmbedSample/EmbedSample.cs @@ -6,116 +6,49 @@ using System.Text; using Avalonia.Controls; using Avalonia.Platform; using Avalonia.Threading; -using MonoMac.AppKit; -using MonoMac.Foundation; -using MonoMac.WebKit; -using Encoding = SharpDX.Text.Encoding; namespace NativeEmbedSample { - public class EmbedSample : NativeControlHost + public partial class EmbedSample : NativeControlHost { public bool IsSecond { get; set; } - private Process _mplayer; - IPlatformHandle CreateLinux(IPlatformHandle parent) - { - if (IsSecond) - { - var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle); - if (chooser != null) - return chooser; - } - - var control = base.CreateNativeControlCore(parent); - var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, - "..", - "nodes.mp4")); - _mplayer = Process.Start(new ProcessStartInfo("mplayer", - $"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"") - { - UseShellExecute = false, - - }); - return control; - } - - void DestroyLinux(IPlatformHandle handle) - { - _mplayer?.Kill(); - _mplayer = null; - base.DestroyNativeControlCore(handle); - } - - private const string RichText = - @"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}} -{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;} -{\*\generator Riched20 6.3.9600}\viewkind4\uc1 -\pard\sa200\sl276\slmult1\f0\fs22\lang9 I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par -}"; - - IPlatformHandle CreateWin32(IPlatformHandle parent) - { - WinApi.LoadLibrary("Msftedit.dll"); - var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W", - @"Rich Edit", - 0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle, - IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero); - var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 }; - var text = RichText.Replace("", IsSecond ? "\\qr " : ""); - var bytes = Encoding.UTF8.GetBytes(text); - WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes); - return new PlatformHandle(handle, "HWND"); - - } - - void DestroyWin32(IPlatformHandle handle) - { - WinApi.DestroyWindow(handle.Handle); - } - - IPlatformHandle CreateOSX(IPlatformHandle parent) - { - // Note: We are using MonoMac for example purposes - // It shouldn't be used in production apps - MacHelper.EnsureInitialized(); - - var webView = new WebView(); - Dispatcher.UIThread.Post(() => - { - webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl( - IsSecond ? "https://bing.com": "https://google.com/"))); - }); - return new MacOSViewHandle(webView); - - } - - void DestroyOSX(IPlatformHandle handle) - { - ((MacOSViewHandle)handle).Dispose(); - } - protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +#if DESKTOP + if (OperatingSystem.IsLinux()) return CreateLinux(parent); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (OperatingSystem.IsWindows()) return CreateWin32(parent); - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (OperatingSystem.IsMacOS()) return CreateOSX(parent); +#elif __ANDROID__ || ANDROID + if (OperatingSystem.IsAndroid()) + return CreateAndroid(parent); +#elif IOS + if (OperatingSystem.IsIOS()) + return CreateIOS(parent); +#endif return base.CreateNativeControlCore(parent); } protected override void DestroyNativeControlCore(IPlatformHandle control) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +#if DESKTOP + if (OperatingSystem.IsLinux()) DestroyLinux(control); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + else if (OperatingSystem.IsWindows()) DestroyWin32(control); - else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (OperatingSystem.IsMacOS()) DestroyOSX(control); - else - base.DestroyNativeControlCore(control); +#elif __ANDROID__ || ANDROID + if (OperatingSystem.IsAndroid()) + DestroyAndroid(control); +#elif IOS + if (OperatingSystem.IsIOS()) + DestroyIOS(control); +#endif + else base.DestroyNativeControlCore(control); } } } diff --git a/samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs b/samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs new file mode 100644 index 0000000000..24c9deca36 --- /dev/null +++ b/samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs @@ -0,0 +1,41 @@ +#if DESKTOP +using System.IO; +using System.Diagnostics; +using Avalonia.Platform; + +namespace NativeEmbedSample; + +public partial class EmbedSample +{ + private Process _mplayer; + + IPlatformHandle CreateLinux(IPlatformHandle parent) + { + if (IsSecond) + { + var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle); + if (chooser != null) + return chooser; + } + + var control = base.CreateNativeControlCore(parent); + var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, + "..", + "nodes.mp4")); + _mplayer = Process.Start(new ProcessStartInfo("mplayer", + $"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"") + { + UseShellExecute = false, + + }); + return control; + } + + void DestroyLinux(IPlatformHandle handle) + { + _mplayer?.Kill(); + _mplayer = null; + base.DestroyNativeControlCore(handle); + } +} +#endif diff --git a/samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs b/samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs new file mode 100644 index 0000000000..567bc25acb --- /dev/null +++ b/samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs @@ -0,0 +1,62 @@ +#if DESKTOP + +using System; +using System.Threading.Tasks; +using Avalonia.Controls.Platform; +using Avalonia.Platform; +using Avalonia.Platform.Interop; +using Avalonia.X11.NativeDialogs; +using static Avalonia.X11.NativeDialogs.Gtk; +using static Avalonia.X11.NativeDialogs.Glib; + +namespace NativeEmbedSample; + +internal class GtkHelper +{ + private static Task s_gtkTask; + + class FileChooser : INativeControlHostDestroyableControlHandle + { + private readonly IntPtr _widget; + + public FileChooser(IntPtr widget, IntPtr xid) + { + _widget = widget; + Handle = xid; + } + + public IntPtr Handle { get; } + public string HandleDescriptor => "XID"; + + public void Destroy() + { + RunOnGlibThread(() => + { + gtk_widget_destroy(_widget); + return 0; + }).Wait(); + } + } + + + public static IPlatformHandle CreateGtkFileChooser(IntPtr parentXid) + { + if (s_gtkTask == null) + s_gtkTask = StartGtk(); + if (!s_gtkTask.Result) + return null; + return RunOnGlibThread(() => + { + using (var title = new Utf8Buffer("Embedded")) + { + var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder, + IntPtr.Zero); + gtk_widget_realize(widget); + var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget)); + gtk_window_present(widget); + return new FileChooser(widget, xid); + } + }).Result; + } +} +#endif diff --git a/samples/interop/NativeEmbedSample/nodes-license.md b/samples/interop/NativeEmbedSample/Gtk/nodes-license.md similarity index 100% rename from samples/interop/NativeEmbedSample/nodes-license.md rename to samples/interop/NativeEmbedSample/Gtk/nodes-license.md diff --git a/samples/interop/NativeEmbedSample/nodes.mp4 b/samples/interop/NativeEmbedSample/Gtk/nodes.mp4 similarity index 100% rename from samples/interop/NativeEmbedSample/nodes.mp4 rename to samples/interop/NativeEmbedSample/Gtk/nodes.mp4 diff --git a/samples/interop/NativeEmbedSample/GtkHelper.cs b/samples/interop/NativeEmbedSample/GtkHelper.cs deleted file mode 100644 index e389a51ef5..0000000000 --- a/samples/interop/NativeEmbedSample/GtkHelper.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Threading.Tasks; -using Avalonia.Controls.Platform; -using Avalonia.Platform; -using Avalonia.Platform.Interop; -using Avalonia.X11.NativeDialogs; -using static Avalonia.X11.NativeDialogs.Gtk; -using static Avalonia.X11.NativeDialogs.Glib; -namespace NativeEmbedSample -{ - public class GtkHelper - { - private static Task s_gtkTask; - class FileChooser : INativeControlHostDestroyableControlHandle - { - private readonly IntPtr _widget; - - public FileChooser(IntPtr widget, IntPtr xid) - { - _widget = widget; - Handle = xid; - } - - public IntPtr Handle { get; } - public string HandleDescriptor => "XID"; - public void Destroy() - { - RunOnGlibThread(() => - { - gtk_widget_destroy(_widget); - return 0; - }).Wait(); - } - } - - - - public static IPlatformHandle CreateGtkFileChooser(IntPtr parentXid) - { - if (s_gtkTask == null) - s_gtkTask = StartGtk(); - if (!s_gtkTask.Result) - return null; - return RunOnGlibThread(() => - { - using (var title = new Utf8Buffer("Embedded")) - { - var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder, - IntPtr.Zero); - gtk_widget_realize(widget); - var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget)); - gtk_window_present(widget); - return new FileChooser(widget, xid); - } - }).Result; - } - } -} diff --git a/samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs b/samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs new file mode 100644 index 0000000000..911a874c27 --- /dev/null +++ b/samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs @@ -0,0 +1,32 @@ +#if DESKTOP +using Avalonia.Platform; +using Avalonia.Threading; +using MonoMac.Foundation; +using MonoMac.WebKit; + +namespace NativeEmbedSample; + +public partial class EmbedSample +{ + IPlatformHandle CreateOSX(IPlatformHandle parent) + { + // Note: We are using MonoMac for example purposes + // It shouldn't be used in production apps + MacHelper.EnsureInitialized(); + + var webView = new WebView(); + Dispatcher.UIThread.Post(() => + { + webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl( + IsSecond ? "https://bing.com": "https://google.com/"))); + }); + return new MacOSViewHandle(webView); + + } + + void DestroyOSX(IPlatformHandle handle) + { + ((MacOSViewHandle)handle).Dispose(); + } +} +#endif diff --git a/samples/interop/NativeEmbedSample/Mac/MacHelper.cs b/samples/interop/NativeEmbedSample/Mac/MacHelper.cs new file mode 100644 index 0000000000..d72ef5479c --- /dev/null +++ b/samples/interop/NativeEmbedSample/Mac/MacHelper.cs @@ -0,0 +1,39 @@ +#if DESKTOP +using System; +using Avalonia.Platform; +using MonoMac.AppKit; + +namespace NativeEmbedSample; + +internal class MacHelper +{ + private static bool _isInitialized; + + public static void EnsureInitialized() + { + if (_isInitialized) + return; + _isInitialized = true; + NSApplication.Init(); + } +} + +internal class MacOSViewHandle : IPlatformHandle, IDisposable +{ + private NSView _view; + + public MacOSViewHandle(NSView view) + { + _view = view; + } + + public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; + public string HandleDescriptor => "NSView"; + + public void Dispose() + { + _view.Dispose(); + _view = null; + } +} +#endif diff --git a/samples/interop/NativeEmbedSample/MacHelper.cs b/samples/interop/NativeEmbedSample/MacHelper.cs deleted file mode 100644 index 74a06a0a0c..0000000000 --- a/samples/interop/NativeEmbedSample/MacHelper.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Avalonia.Platform; -using MonoMac.AppKit; - -namespace NativeEmbedSample -{ - public class MacHelper - { - private static bool _isInitialized; - - public static void EnsureInitialized() - { - if (_isInitialized) - return; - _isInitialized = true; - NSApplication.Init(); - } - } - - class MacOSViewHandle : IPlatformHandle, IDisposable - { - private NSView _view; - - public MacOSViewHandle(NSView view) - { - _view = view; - } - - public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; - public string HandleDescriptor => "NSView"; - - public void Dispose() - { - _view.Dispose(); - _view = null; - } - } - -} diff --git a/samples/interop/NativeEmbedSample/MainView.axaml b/samples/interop/NativeEmbedSample/MainView.axaml new file mode 100644 index 0000000000..4bfb14b2f9 --- /dev/null +++ b/samples/interop/NativeEmbedSample/MainView.axaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + Text + + + Tooltip + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/interop/NativeEmbedSample/MainView.axaml.cs b/samples/interop/NativeEmbedSample/MainView.axaml.cs new file mode 100644 index 0000000000..976de7a97c --- /dev/null +++ b/samples/interop/NativeEmbedSample/MainView.axaml.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; + +namespace NativeEmbedSample; + +public class MainView : UserControl +{ + public MainView() + { + AvaloniaXamlLoader.Load(this); + } + + public async void ShowPopupDelay(object sender, RoutedEventArgs args) + { + await Task.Delay(3000); + ShowPopup(sender, args); + } + + public void ShowPopup(object sender, RoutedEventArgs args) + { + new ContextMenu() + { + Items = new List + { + new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } + } + }.Open((Control)sender); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == BoundsProperty) + { + var isMobile = change.GetNewValue().Width < 1200; + this.Find("FirstPanel")!.Classes.Set("mobile", isMobile); + this.Find("SecondPanel")!.Classes.Set("mobile", isMobile); + } + } +} diff --git a/samples/interop/NativeEmbedSample/MainWindow.axaml b/samples/interop/NativeEmbedSample/MainWindow.axaml new file mode 100644 index 0000000000..a615428778 --- /dev/null +++ b/samples/interop/NativeEmbedSample/MainWindow.axaml @@ -0,0 +1,10 @@ + + + + diff --git a/samples/interop/NativeEmbedSample/MainWindow.axaml.cs b/samples/interop/NativeEmbedSample/MainWindow.axaml.cs new file mode 100644 index 0000000000..a261dad5ed --- /dev/null +++ b/samples/interop/NativeEmbedSample/MainWindow.axaml.cs @@ -0,0 +1,17 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace NativeEmbedSample; + +public class MainWindow : Window +{ + public MainWindow() + { + AvaloniaXamlLoader.Load(this); +#if DEBUG && DESKTOP + this.AttachDevTools(); +#endif + } +} + diff --git a/samples/interop/NativeEmbedSample/MainWindow.xaml b/samples/interop/NativeEmbedSample/MainWindow.xaml deleted file mode 100644 index f2161a1bea..0000000000 --- a/samples/interop/NativeEmbedSample/MainWindow.xaml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - Text - - - Tooltip - - - - - - - - - Visible - - - - - - - - Visible - - - - - - diff --git a/samples/interop/NativeEmbedSample/MainWindow.xaml.cs b/samples/interop/NativeEmbedSample/MainWindow.xaml.cs deleted file mode 100644 index 4324aa2762..0000000000 --- a/samples/interop/NativeEmbedSample/MainWindow.xaml.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; - -namespace NativeEmbedSample -{ - public class MainWindow : Window - { - public MainWindow() - { - AvaloniaXamlLoader.Load(this); - this.AttachDevTools(); - } - - public async void ShowPopupDelay(object sender, RoutedEventArgs args) - { - await Task.Delay(3000); - ShowPopup(sender, args); - } - - public void ShowPopup(object sender, RoutedEventArgs args) - { - - new ContextMenu() - { - Items = new List - { - new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } - } - }.Open((Control)sender); - } - } -} diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj index c25442b52c..34206c2b63 100644 --- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj +++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj @@ -1,30 +1,32 @@  - Exe - netcoreapp2.0 - true + net6.0;net6.0-android;net6.0-ios true + + $(DefineConstants);DESKTOP + + + + - - - Designer - - + + + Gtk\Gtk.cs + + + PreserveNewest - - - diff --git a/samples/interop/NativeEmbedSample/Program.cs b/samples/interop/NativeEmbedSample/Program.cs deleted file mode 100644 index baa7837667..0000000000 --- a/samples/interop/NativeEmbedSample/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia; - -namespace NativeEmbedSample -{ - class Program - { - static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .With(new AvaloniaNativePlatformOptions() - { - }) - .UsePlatformDetect(); - - } -} diff --git a/samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs b/samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs new file mode 100644 index 0000000000..2e14e7d766 --- /dev/null +++ b/samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs @@ -0,0 +1,37 @@ +#if DESKTOP +using System; +using System.Text; +using Avalonia.Platform; + +namespace NativeEmbedSample; + +public partial class EmbedSample +{ + private const string RichText = + @"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}} +{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;} +{\*\generator Riched20 6.3.9600}\viewkind4\uc1 +\pard\sa200\sl276\slmult1\f0\fs22\lang9 I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par +}"; + + IPlatformHandle CreateWin32(IPlatformHandle parent) + { + WinApi.LoadLibrary("Msftedit.dll"); + var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W", + @"Rich Edit", + 0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle, + IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero); + var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 }; + var text = RichText.Replace("", IsSecond ? "\\qr " : ""); + var bytes = Encoding.UTF8.GetBytes(text); + WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes); + return new PlatformHandle(handle, "HWND"); + + } + + void DestroyWin32(IPlatformHandle handle) + { + WinApi.DestroyWindow(handle.Handle); + } +} +#endif diff --git a/samples/interop/NativeEmbedSample/Win/WinApi.cs b/samples/interop/NativeEmbedSample/Win/WinApi.cs new file mode 100644 index 0000000000..5ec6e6bbeb --- /dev/null +++ b/samples/interop/NativeEmbedSample/Win/WinApi.cs @@ -0,0 +1,75 @@ +#if DESKTOP +using System; +using System.Runtime.InteropServices; + +namespace NativeEmbedSample; + +internal unsafe class WinApi +{ + public enum CommonControls : uint + { + ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header + ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips + ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips + ICC_TAB_CLASSES = 0x00000008, // tab, tooltips + ICC_UPDOWN_CLASS = 0x00000010, // updown + ICC_PROGRESS_CLASS = 0x00000020, // progress + ICC_HOTKEY_CLASS = 0x00000040, // hotkey + ICC_ANIMATE_CLASS = 0x00000080, // animate + ICC_WIN95_CLASSES = 0x000000FF, + ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown + ICC_USEREX_CLASSES = 0x00000200, // comboex + ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control + ICC_INTERNET_CLASSES = 0x00000800, + ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller + ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control + ICC_STANDARD_CLASSES = 0x00004000, + ICC_LINK_CLASS = 0x00008000 + } + + [StructLayout(LayoutKind.Sequential)] + public struct INITCOMMONCONTROLSEX + { + public int dwSize; + public uint dwICC; + } + + [DllImport("Comctl32.dll")] + public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool DestroyWindow(IntPtr hwnd); + + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string lib); + + + [DllImport("kernel32.dll")] + public static extern IntPtr GetModuleHandle(string lpModuleName); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr CreateWindowEx( + int dwExStyle, + string lpClassName, + string lpWindowName, + uint dwStyle, + int x, + int y, + int nWidth, + int nHeight, + IntPtr hWndParent, + IntPtr hMenu, + IntPtr hInstance, + IntPtr lpParam); + + [StructLayout(LayoutKind.Sequential)] + public struct SETTEXTEX + { + public uint Flags; + public uint Codepage; + } + + [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); +} +#endif diff --git a/samples/interop/NativeEmbedSample/WinApi.cs b/samples/interop/NativeEmbedSample/WinApi.cs deleted file mode 100644 index 8e5bcdf49e..0000000000 --- a/samples/interop/NativeEmbedSample/WinApi.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace NativeEmbedSample -{ - public unsafe class WinApi - { - public enum CommonControls : uint - { - ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header - ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips - ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips - ICC_TAB_CLASSES = 0x00000008, // tab, tooltips - ICC_UPDOWN_CLASS = 0x00000010, // updown - ICC_PROGRESS_CLASS = 0x00000020, // progress - ICC_HOTKEY_CLASS = 0x00000040, // hotkey - ICC_ANIMATE_CLASS = 0x00000080, // animate - ICC_WIN95_CLASSES = 0x000000FF, - ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown - ICC_USEREX_CLASSES = 0x00000200, // comboex - ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control - ICC_INTERNET_CLASSES = 0x00000800, - ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller - ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control - ICC_STANDARD_CLASSES = 0x00004000, - ICC_LINK_CLASS = 0x00008000 - } - - [StructLayout(LayoutKind.Sequential)] - public struct INITCOMMONCONTROLSEX - { - public int dwSize; - public uint dwICC; - } - - [DllImport("Comctl32.dll")] - public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init); - - [DllImport("user32.dll", SetLastError = true)] - public static extern bool DestroyWindow(IntPtr hwnd); - - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibrary(string lib); - - - [DllImport("kernel32.dll")] - public static extern IntPtr GetModuleHandle(string lpModuleName); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr CreateWindowEx( - int dwExStyle, - string lpClassName, - string lpWindowName, - uint dwStyle, - int x, - int y, - int nWidth, - int nHeight, - IntPtr hWndParent, - IntPtr hMenu, - IntPtr hInstance, - IntPtr lpParam); - - [StructLayout(LayoutKind.Sequential)] - public struct SETTEXTEX - { - public uint Flags; - public uint Codepage; - } - - [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] - public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); - } -} diff --git a/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs b/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs new file mode 100644 index 0000000000..185c2e6b9f --- /dev/null +++ b/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs @@ -0,0 +1,65 @@ +#if IOS +using System; +using System.IO; +using System.Diagnostics; +using Avalonia.Controls.Platform; +using Avalonia.Platform; +using CoreGraphics; +using Foundation; +using UIKit; +using WebKit; + +namespace NativeEmbedSample; + +public partial class EmbedSample +{ + private IPlatformHandle CreateIOS(IPlatformHandle parent) + { + if (IsSecond) + { + var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration()); + webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/"))); + + return new UIViewHandle(webView); + } + else + { + var button = new UIButton(); + var clickCount = 0; + button.SetTitle("Hello world", UIControlState.Normal); + button.BackgroundColor = UIColor.Blue; + button.AddTarget((_, _) => + { + clickCount++; + button.SetTitle($"Click count {clickCount}", UIControlState.Normal); + }, UIControlEvent.TouchDown); + + return new UIViewHandle(button); + } + } + + private void DestroyIOS(IPlatformHandle control) + { + base.DestroyNativeControlCore(control); + } +} + +internal class UIViewHandle : INativeControlHostDestroyableControlHandle +{ + private UIView _view; + + public UIViewHandle(UIView view) + { + _view = view; + } + + public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; + public string HandleDescriptor => "UIView"; + + public void Destroy() + { + _view?.Dispose(); + _view = null; + } +} +#endif From d8d6fa5097c13acbeef998015bfe6295286f2735 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 00:28:09 -0400 Subject: [PATCH 581/820] Add iOS implementation --- Avalonia.sln | 27 +++ .../NativeEmbedSample.iOS/AppDelegate.cs | 9 + .../AppIcon.appiconset/Contents.json | 117 +++++++++++ .../AppIcon.appiconset/Icon1024.png | Bin 0 -> 70429 bytes .../AppIcon.appiconset/Icon120.png | Bin 0 -> 3773 bytes .../AppIcon.appiconset/Icon152.png | Bin 0 -> 4750 bytes .../AppIcon.appiconset/Icon167.png | Bin 0 -> 4692 bytes .../AppIcon.appiconset/Icon180.png | Bin 0 -> 5192 bytes .../AppIcon.appiconset/Icon20.png | Bin 0 -> 1313 bytes .../AppIcon.appiconset/Icon29.png | Bin 0 -> 845 bytes .../AppIcon.appiconset/Icon40.png | Bin 0 -> 1101 bytes .../AppIcon.appiconset/Icon58.png | Bin 0 -> 1761 bytes .../AppIcon.appiconset/Icon60.png | Bin 0 -> 2537 bytes .../AppIcon.appiconset/Icon76.png | Bin 0 -> 2332 bytes .../AppIcon.appiconset/Icon80.png | Bin 0 -> 2454 bytes .../AppIcon.appiconset/Icon87.png | Bin 0 -> 2758 bytes .../NativeEmbedSample.iOS/Entitlements.plist | 6 + .../interop/NativeEmbedSample.iOS/Info.plist | 42 ++++ samples/interop/NativeEmbedSample.iOS/Main.cs | 6 + .../NativeEmbedSample.iOS.csproj | 16 ++ .../Resources/LaunchScreen.xib | 43 ++++ src/iOS/Avalonia.iOS/AvaloniaView.cs | 4 +- src/iOS/Avalonia.iOS/NativeControlHostImpl.cs | 183 ++++++++++++++++++ 23 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 samples/interop/NativeEmbedSample.iOS/AppDelegate.cs create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png create mode 100644 samples/interop/NativeEmbedSample.iOS/Entitlements.plist create mode 100644 samples/interop/NativeEmbedSample.iOS/Info.plist create mode 100644 samples/interop/NativeEmbedSample.iOS/Main.cs create mode 100644 samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj create mode 100644 samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib create mode 100644 src/iOS/Avalonia.iOS/NativeControlHostImpl.cs diff --git a/Avalonia.sln b/Avalonia.sln index 0adc29ffe8..0a33bc4150 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -221,6 +221,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\D EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.iOS", "samples\interop\NativeEmbedSample.iOS\NativeEmbedSample.iOS.csproj", "{28DB5AD1-656D-4619-BE0B-5B475E138DF8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -2015,6 +2017,30 @@ Global {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.Build.0 = Release|Any CPU {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhone.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhone.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|Any CPU.Build.0 = Release|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhone.ActiveCfg = Release|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhone.Build.0 = Release|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2075,6 +2101,7 @@ Global {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {F2389463-DDB4-4317-B894-D4DF9FF6B763} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} + {28DB5AD1-656D-4619-BE0B-5B475E138DF8} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/interop/NativeEmbedSample.iOS/AppDelegate.cs b/samples/interop/NativeEmbedSample.iOS/AppDelegate.cs new file mode 100644 index 0000000000..9ac8ebab2e --- /dev/null +++ b/samples/interop/NativeEmbedSample.iOS/AppDelegate.cs @@ -0,0 +1,9 @@ +using Avalonia.iOS; + +namespace NativeEmbedSample.iOS; + +[Register("AppDelegate")] +public partial class AppDelegate : AvaloniaAppDelegate +{ + +} diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..98f4d035c8 --- /dev/null +++ b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,117 @@ +{ + "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/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png new file mode 100644 index 0000000000000000000000000000000000000000..9174c989a9c8b8a5ca133228f4ed7c173fffd2ee GIT binary patch literal 70429 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 z zCn)b8&=oMu6ygwwJfdasJX|@L6?m1Dv0X9t>JAWO^UIj0#&(3UrHx;vP^3g= zL{(XT!?`D*pP8)WoGHYEZZc$!odTzb8n)q0|88*>6P z`?6&CSv_W7r2yF0beQ2*?V^_%pKktVAo`)T^26X@NpK_*-ni{D7{Sp{C0A<|16l(; zOL*xGW|*sKsiwHvE!h3QXe@^a#6W3}8!DQu-h?A_4gkeRYkt4NC~GR5P8eyp;9kVQ8$QG$5ad7Fo23Z~ak1jY~RXG{v?3G$RarFe`XePu3X{R+=mBOw&X zks)|Sc$RcG-jhn!`~-x|vg!&DA&@}QH^RNdyy9nq56yrU$^qAaS+F_NOaeFb)CVaH z?!UvPajgrK&zqdAs>&Def#wkcG_UhmYOVw^M`VZz@+4IWAVzK%`+za9rm2SD9={u@ zlx5D6UDL;lc7#9`+%vnlP3PescU=N`DHQPt_N55GNBMkVCRMR4?fvp zAFsvcHN4c9rb>J@{*IH>RTr9de%9i4Gd(cbFa9SP4anhoP;TA0!oZyB8?lNMDHPHK zCaOaFU9?x2A!o>p>mCF9r+hKs9Czu_P1l$LWU%}q#)=T3p`ZnYyeHmsewqw`}L^4LuHqfo+CG6<2n7#l^3;H^^!1 zsaieYFnN)Kc7Mv}^xE)4kXUw8<9I+jMB@QV9T9I8haLDt1Ne#exWUfGYG$4uMoEu& zo81#2up18Y40h%tIsOZglp(ltVsE*j1~$lVd|;rN)&${~o~-%KZnJp&3|OFR{^8E9 zJ;fCu53Ysw%}@VYWE*z7r)&4P=^B-SF%a@>*9g84<4aFUZT7x)qdsS+#2tu5NbpU@ zg;EwV)l-#sK>#r9>(0Figx{9lKm>KvRj;y<8 zc8SxMW4<11(s@QMV_}n9MRzA*62->vzxmHh1)GVASEJY7LVtRw`Rv{v`(Fuc00(&o z%m>gS2aJekmdNQ4p<{pD3HqZ-%4hdU1__xYhLi9mTJXD|E zE`t6SX)}l_DY5vO0Xrs#O6_DKtPKn0f+e~SprDYmJL_`<053iA5P`zn z4<5etc%aF58sHFr#M;U-9|=;l)J#Q2vS!Q9(d(EX6fubL%uA_lqa2%!cpNIv78QZ}Ayo(>C(ZpsRtKhzD--fpuoCch87cX-Bna9_{z%$b*dHM0?+T&Hk!+^UM`r|vq z2Id$??bX^|tfYaE+h#Nik(ZcN+wt)28q^gWe!y8jDCXrD<2qV#49x@5$8&Zrd5NTs zNYcix;9fe#PQQ;T?!6hG>9K{K+RCPqiGc9z%t{=`QaX>7O{l(+#7mJ1>Rae^J?82e z6cLqLypskTCyu>uc~$0-XZ^1Qvhwr+pKQ#CKImhGu*MGM*ZrROuAHWuT*yM$ieEy8*KLFMMdLZL|D+yDmy@3_PELTEVMI6nwfcYA3ZQ9wwKdtkT z;`;z7fU{U6>CS7kr3=A-()_G*G(Mjf2wXKe

Fpy)y!S(AQHSG#udd_8#b4sQu!R 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 PgFh2POM|ilu9yD@)d*=D literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png new file mode 100644 index 0000000000000000000000000000000000000000..9c60a1761dbf62cc2a45ff98b9fdb63ade16e4d9 GIT binary patch literal 3773 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 Dh-f%Y literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png new file mode 100644 index 0000000000000000000000000000000000000000..448d6efb577d07e227a5c62545173ddf6bd86b55 GIT binary patch literal 4750 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 literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png new file mode 100644 index 0000000000000000000000000000000000000000..8524768f8d764da7e9c452a444208708f2d18ff1 GIT binary patch literal 4692 zcmdT|XIB&4(xpTN1*K>xQbX^8^b({KIw~rHNC|`@2%(dJ5D-F#1ZfEYiGV0lq(}=W zgc7Psl_H9vHvuJ7z1-)%p)6vnfLQD;Bp4zg1 zAEvXXcM#BG{nP+pdX{>0bT#Q0j$O{s(Q#aW80y^)qu+Solk&js%GX`#>--*?1>hBn zylj2Bl~|w=hswPyL69*gD{tKnqopZQY+Ok0Wi&``_+IL55R?xKc>smnzEfS9yo`Q{=^|^0;fo;{d{hqBCglz?TcMBUE zv9qCXytz?uTg*u4#tlljAzN}Z=2nHzZAGy%_zhVGGpm|P+pa8pAAJpzq()b>@s(R} z>2qXI5%uyKubl;@obSI8@VZc*jSs8>75IYaJwEbpU(ry69>yD|l$U2d20L+%sS>{i zsSICRml49T7GzA*+lM?CZ_~6^^)!No`QYzJ%-}6)O^+lfdl+G z1O?m!ckdDA}b>}*SY^H-eW-!oJ#MwHFg>6&At;9qxdriX`yY1d+lkmMg! zbjZjbS%^n()6yjKE)&;ur^F2bxwkn6FFoM^gqLnWZxS>f|4wJlH=b2o4-Lxfd^<0e zz^_NU*zzAI3jcRGyyy5GjU?&q(WPND9kUGKLz@7}2snY4M}FIf$QH*ghL-*jzPb2$ zfZPGTkTrFubtmHyXOA5Bry1XzDL+p)hmFSY)mk4*gqwlmmF>S zS+6Vi7>oBhNb6~6tX}0;A^WbCa9MbjjVhSa{Lce7miezenM|Mu)0JhdR@?mUvSbZU zq$p{l5F@Ky=t|-zHlfycS;Id~J{+F*3z7_-4P;x;#PucfvxDC!H?r#%l4aoVTO0RK zICSXmLZz1U?=@vc;C3jXDNGe41M&r-BJK&U)ieK&C}}?qHsi?pi^e_1VMxMD55KBE zB4|ats({#-#(#7n`cGza(VjkBI%y5xz`P~Gw7t*%UhwsuXZT$l^}I4|ezRXla$6*= z4b4T>R@8RgoS|5fnHBgyxLA{}I}-vb&NwMmjX5^?-|^eI9q*$!4%Mj`79UNBh{Ebb3Wc!z1tI(1vUyP1+*7^(4&1yM?CgM^mSAh?2hHosE$M}P*C_29}omMN5 z12_~tF)$?J`Pfb7S7Ol;OIJ@M1|NS#swII$?TS%{PGGR-pI^#;tU6fVx1KN#M&@MvKk4-Jp&tj7w$N( zUkNq6ocd|jckZa+JEtTLx!aNEOs^Bx;U<&Y0+esu1>>q8Gzf+)WjZzB%o>4Pa%hEs zY-v}@!TU|d#Z;_FA~>%`Bj(etxw`!TE z-H%3zyd5F`pvUxzP1g=4fBqrm7E#4@pCy5w-?u&S+@c*t46db7I>wgduD$k9F`h-- z8|En#lIX8#wVV`~w(NA8w`dhhGKKqnaE>hM!=Yn0FMfh@Gkd%P`u{M)#cORv1DCHaJUhdI>IC>z+d12<41E>}{%v^kX2{^jY$+)k{d3|iIYJS_{^L+_5#=E11KJ{FDFv1W&0AY z?_TrXK{$m%K3YAMh&%{l+HhC8HZN~!n2Dvl4B5M2+HnTe=D(hG;PCF`n3nVfhI`E= zqU6et<>1JAvWswf$Gis9`hIWZPDAm;X=QS4#pVIEzad@vP>m}p?#Aek% z_oE<(AwZ)LoKljNMO=Ww$VAFkGh#5xWG|&k*1@^banyC+i*vm5P#-}Id8B5y%X|DY z#f|69{Z+KklHPM`$qr8?G)4Uq`pXLeTiA5Z9qy>9xZl-aW2pf0fK=2sz#R(!nxEn= zg|4{|6qU()T5{}Zm{D7MAe%YE0vxST9%ah%YxPXD>yg-N_i1pe=(ffkvz-zQtrLT7 zr&*;O*K(zPbX9?R!@nT$ag3)GY@2TiVN?dlwf9SsC)|KuYe0t8@gphVIGL2MR&-S0LZOfu zz1pW@U*WUq8i7;ht%)tl>?T8(MC|%=G^d7UMC|3L*T#=o zZgwNH`W=8xf=m5JawZUNo$!K%M;#%PPK^?ycT_1pq8>u0la@2o3zUWjc#brSm7Yns z@>;{5shEk+&a{tPfC{A04V<^#jWA@t+n0;TeE#O6TdSxfQKJ8JBm>I*UVU@`baL&PzJInq zmEHH~@Xn9?d+^Wu)}cd+cV*w-;BVhCJ5THdQ9VPAGVf;i?r%LVh@#nk(2Obi-_In; z#Cp=)F|i8DZfV6p`w{%$?4R>|K%=HOwp5eMRQ3CxsHQxDYVZqJaC=&40{Z`OX1{?k zBq8x_(aO(8+8Q|xLo63l>>j<1miKe_As)PSJEw&e1n_LZtz(lyWH*1DR6kIVS^U@EfkZD6pvdN%6MsTLSwv6i5>hgZ=tqX=5=EW7u>)5%{#%5ASh88%@$m94oJE(Rn_ z5@A~q6cEJ!{=%5$(Z~fj#|s7dg2(b+){7cJ%N0WI1NUk2ctkAp(gI0VSU@NCkdH9O zLJ}`)4w!LmPZ0$DqbJm;qDAkVT7x=VmI=j*x64gC?FGFat8!`H?AG2}%!CHki9{$Z zY5iNo6h|!>4}VKwYBdd-U&4kN4UKKcg<(DmXjI6eP@*~#@fCR~2b0@FfMO3*^l8;e zCbDH#c`J>$GNFEMGsFFF38pjXLhJe2WczfNoMDN-(X&P7J+ zwIW5tefQGvw<8!YIzO01{U8I{4Vhae^>xi3dGt-6_q{Hw<}UUW$^1X+R8*qY`#8>8 zUAh{$OyrbULuz`bomFpon_e&@{q<*w@^wBeJxc@~-2?j*?BMSXDjnot?}G(I;+1J049jExcd zo~6IaL@XT@b$mMcO&SYc`8Tot&%9jy5#kg`KMLw>XR(EeyPi}Y zi!B09N~kd3RcxTj;OyZ_8e@xNO`JG?=p^eRV@JZ4!BtZWE0ky9DeY;}?BN`E*4~!3 z=RQN^Hfznx9GdF;o!GzR;ERcn7SD&-T`kuQOVoepQDJjQGyp5;`JFIlS?wrWv&gYF z2_ey|T?4J`Rjyy^UUfRYV^Ba1Hds2^UcQ=>5> zshQcP%=BU~v-du=et;~zUrL>!+37mr7K0NmSfq#=>qAimUWuWmiSy zGC3H`hO(k3JZ4V=XSux+v)F9lrGQq|HRBtUm2Ok>7je;;>tf&P?bS|~6l%uzL1L%O qQuI}W&FnVtX2s7O|6Nb``GoL3$B3jnW^%eFqJtP&8CL2$qy7ci8tmx+ literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png new file mode 100644 index 0000000000000000000000000000000000000000..60a64703c0f11d08705cd607f7751338707f5919 GIT binary patch literal 5192 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 nB>N_)Q7iwV%v7MwAoJ}E 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 T3-0p800000NkvXXu0mjfSlal| literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png new file mode 100644 index 0000000000000000000000000000000000000000..1ad04f004b638bf781012290d78e4138f97bbe5e GIT binary patch literal 1761 zcmV<71|Io|P)4P%ubY|S^%$zf~ zmwTOa@12BA$oV-Y9!V&U%c=j==#_}M2ylE}1m>yyDoGsZ#Yy zNX}RO*f(MzmKS&u`qiajIyW{Y_LC%m2NqT@Ic|QpvYqwNgBK7n5X%c(3k^?2>EOA` zqGaXjE7H9BiJ55fh0iJRW}@=&(@R^E1hLB>kE%PS6eP@VZVdtn(fh;5DPKg!j;fJZ%)wH{Wn#~V&#n(o1URS zsyS`0Tu2m;-H}z9O^h`!UZAFr@?0a7Z;pYOi0uZhgzh=rOEDi`FIkKtVu*gEcSM!h zmb#_XR$akjlg$JI75MXgWkG7IUnYJ+X=1J!qJ%jXVj{L1I2QU%?=?DgV^U?)92DZm zV?>``xT1#kZdgUt!2n?|0>*6ae4tikA9FAlJ}kjmMQm_z3LB5sZYHBKdbex9_Hv@K z%Y?q@9-)b7vJ6X$3h0B4tH__=#*`9^efY@IQfghn*=E2Nb8sR8lrQlu`Ca_Rmm6>Z z7bkEe^w8M>x;hoNUvWu_GZJPVpI;bMTsBpf(@U$Ch(-gk0T#WpsaB1{7ISQ~Y48mW z;Nk?@LjM_?q{BV-D=veoOmJoncVDC1GwGGz(O5@o7ZkGCIJMPO(7K9b6M_wF?Xqd< zo4J6KF0_U2<1=T3x0qc6G6#g+^=N{QTpChC!GZuCY*|eU{Rw)LMN7a2wwbaCdn_dE zzy+Lip(XU4-+SoTFyEcnH3?HRV^%-;Ylx;|>8v&^Dy459ZJ_1zio`68!6s8SO(6sq zaN75WUKiF9+8MruR3=w5)hzA^Z1clVBuXc)+8@e);xX7bfygR&FsIIt-gQ+==(c;S z#J*CO1qZBF&M&6TRmskZXaMOU6&?jn_(BqY5 z>Y|^?uOh;yp6w0QR1`>tiEz_-{Zu!N#(nhRndJV$7;LOgPyZQ*J2yCin+~*u!qKj# zItsG2IDi{ZH+E6j=D|ht=qWxKNxaA6E>3Wna>=z1gy?*#>|g_1^BGspGro&OWRc(k zPP)(*y0WZ7Z-kICr3#g7($P*LCjE>7S`Xh~s!b~bPTo0XSkRm2T(M`QZbv-`Imx*Ulqk*aI* zaqa*=>61?nFb8w3wuiA&zyVyGz>!cm-pY3xEsMEiY)Th4FVrtqWp%V~gxW82)>4^N z*H{>GR?m$^s6BN^M=4^iEjT7(gM(8z7K#9Sn(M_`)oSH2zHDCla&cmK)bb|4nAMIO ze4S3gI9L8AD+T9c#C8K8JF)I68NBWK`5pE`q^OR#h~-u!e7P2i$UDq7^*uZr< zbp8sR<$nYxb8EKH|BltapZ^j3+PZt_u^PR*lT6;TCNxu^yFH$j(!JXvbmHfQ>a0>O z+2k{tOWD%ln$M`tD&>+*KBKvmEgi1;jOwgXF4_DG_&r&PcxYTT00000NkvXXu0mjf DptxI> literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png new file mode 100644 index 0000000000000000000000000000000000000000..2dd52620a8f15577e56ec7fe8e671988dd17ab0f GIT binary patch literal 2537 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 literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png new file mode 100644 index 0000000000000000000000000000000000000000..b058cae2f440e5a5875e45c036c99f1fb6356046 GIT binary patch literal 2332 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 Kc&(xHv;P1B$EH01 literal 0 HcmV?d00001 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png new file mode 100644 index 0000000000000000000000000000000000000000..4954a4bd33f613d45f74dc0b12beb516e3b38661 GIT binary patch literal 2758 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 z + + + + + diff --git a/samples/interop/NativeEmbedSample.iOS/Info.plist b/samples/interop/NativeEmbedSample.iOS/Info.plist new file mode 100644 index 0000000000..b9656e1c20 --- /dev/null +++ b/samples/interop/NativeEmbedSample.iOS/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDisplayName + AvaloniaNative + CFBundleIdentifier + Avalonia.Native.Sample + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/AppIcon.appiconset + + diff --git a/samples/interop/NativeEmbedSample.iOS/Main.cs b/samples/interop/NativeEmbedSample.iOS/Main.cs new file mode 100644 index 0000000000..a1d1502084 --- /dev/null +++ b/samples/interop/NativeEmbedSample.iOS/Main.cs @@ -0,0 +1,6 @@ +using NativeEmbedSample.iOS; + +// This is the main entry point of the application. +// If you want to use a different Application Delegate class from "AppDelegate" +// you can specify it here. +UIApplication.Main(args, null, typeof(AppDelegate)); diff --git a/samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj b/samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj new file mode 100644 index 0000000000..83611c90a1 --- /dev/null +++ b/samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj @@ -0,0 +1,16 @@ + + + net6.0-ios + manual + Exe + enable + true + 11.2 + iossimulator-x64 + + + + + + + diff --git a/samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib b/samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib new file mode 100644 index 0000000000..8190201742 --- /dev/null +++ b/samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index e8108dd3de..0a47b152ed 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -43,7 +43,7 @@ namespace Avalonia.iOS MultipleTouchEnabled = true; } - internal class TopLevelImpl : ITopLevelImplWithTextInputMethod + internal class TopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost { private readonly AvaloniaView _view; public AvaloniaView View => _view; @@ -51,6 +51,7 @@ namespace Avalonia.iOS public TopLevelImpl(AvaloniaView view) { _view = view; + NativeControlHost = new NativeControlHostImpl(_view); } public void Dispose() @@ -112,6 +113,7 @@ namespace Avalonia.iOS new AcrylicPlatformCompensationLevels(); public ITextInputMethodImpl? TextInputMethod => _view; + public INativeControlHostImpl NativeControlHost { get; } } [Export("layerClass")] diff --git a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs new file mode 100644 index 0000000000..c5c632e7f7 --- /dev/null +++ b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs @@ -0,0 +1,183 @@ +#nullable enable + +using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Controls.Platform; +using Avalonia.Platform; +using CoreGraphics; +using ObjCRuntime; +using UIKit; + +namespace Avalonia.iOS +{ + internal class NativeControlHostImpl : INativeControlHostImpl + { + private readonly AvaloniaView _avaloniaView; + + public NativeControlHostImpl(AvaloniaView avaloniaView) + { + _avaloniaView = avaloniaView; + } + + public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) + { + return new UIViewControlHandle(new UIView()); + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) + { + var holder = new UIViewControlHandle(_avaloniaView); + NativeControlAttachment? attachment = null; + try + { + var child = create(holder); + // It has to be assigned to the variable before property setter is called so we dispose it on exception +#pragma warning disable IDE0017 // Simplify object initialization + attachment = new NativeControlAttachment(holder, child); +#pragma warning restore IDE0017 // Simplify object initialization + attachment.AttachedTo = this; + return attachment; + } + catch + { + attachment?.Dispose(); + holder?.Destroy(); + throw; + } + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) + { + return new NativeControlAttachment(new UIViewControlHandle(_avaloniaView), handle) + { + AttachedTo = this + }; + } + + public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == UIViewControlHandle.UIViewDescriptor; + + private class ViewHolder : UIView + { + public ViewHolder(IntPtr handle) : base(new NativeHandle(handle)) + { + + } + } + + private class NativeControlAttachment : INativeControlHostControlTopLevelAttachment + { + // ReSharper disable once NotAccessedField.Local (keep GC reference) + private IPlatformHandle? _child; + private UIViewControlHandle? _holder; + private UIView? _view; + private NativeControlHostImpl? _attachedTo; + + public NativeControlAttachment(UIViewControlHandle holder, IPlatformHandle child) + { + _holder = holder; + _child = child; + + _view = (child as UIViewControlHandle)?.View ?? new ViewHolder(child.Handle); + _holder.View.AddSubview(_view); + } + + [MemberNotNull(nameof(_view))] + private void CheckDisposed() + { + if (_view == null) + throw new ObjectDisposedException(nameof(NativeControlAttachment)); + } + + public void Dispose() + { + _view?.RemoveFromSuperview(); + _holder = null; + _child = null; + _attachedTo = null; + _view?.Dispose(); + _view = null; + } + + public INativeControlHostImpl? AttachedTo + { + get => _attachedTo; + set + { + CheckDisposed(); + + _attachedTo = (NativeControlHostImpl?)value; + if (_attachedTo == null) + { + _view.RemoveFromSuperview(); + } + else + { + _holder!.View.AddSubview(_view); + } + } + } + + public bool IsCompatibleWith(INativeControlHostImpl host) => host is NativeControlHostImpl; + + public void HideWithSize(Size size) + { + CheckDisposed(); + _view.Hidden = true; + } + + public void ShowInBounds(Rect bounds) + { + CheckDisposed(); + if (_attachedTo == null) + throw new InvalidOperationException("The control isn't currently attached to a toplevel"); + + //bounds = _attachedTo._avaloniaView.ContentScaleFactor; + _view.Frame = new CGRect(bounds.X, bounds.Y, bounds.Width, bounds.Height); + _view.Hidden = false; + } + } + } + + public class UIViewControlHandle : INativeControlHostDestroyableControlHandle, IDisposable + { + internal const string UIViewDescriptor = "UIView"; + + private UIView? _view; + + public UIViewControlHandle(UIView view) + { + _view = view; + } + + public UIView View => _view ?? throw new ObjectDisposedException(nameof(UIViewControlHandle)); + + public string HandleDescriptor => UIViewDescriptor; + + IntPtr IPlatformHandle.Handle => _view?.Handle.Handle ?? default; + + public void Destroy() + { + Dispose(true); + } + + void IDisposable.Dispose() + { + Dispose(true); + } + + ~UIViewControlHandle() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + _view?.Dispose(); + _view = null; + if (disposing) + { + GC.SuppressFinalize(this); + } + } + } +} From fbbd93f4cd19b6b6fa6281ff2a3dad7776324c33 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 00:50:49 -0400 Subject: [PATCH 582/820] Add android implementation --- Avalonia.sln | 27 +++ .../NativeEmbedSample.Android/MainActivity.cs | 12 ++ .../NativeEmbedSample.Android.csproj | 50 +++++ .../Properties/AndroidManifest.xml | 4 + .../Resources/AboutResources.txt | 44 +++++ .../Resources/drawable/splash_screen.xml | 13 ++ .../Resources/values/colors.xml | 4 + .../Resources/values/styles.xml | 17 ++ .../SplashActivity.cs | 16 ++ .../Android/EmbedSample.Android.cs | 20 +- src/Android/Avalonia.Android/AvaloniaView.cs | 7 +- .../Platform/AndroidNativeControlHostImpl.cs | 187 ++++++++++++++++++ .../Platform/SkiaPlatform/TopLevelImpl.cs | 10 +- 13 files changed, 402 insertions(+), 9 deletions(-) create mode 100644 samples/interop/NativeEmbedSample.Android/MainActivity.cs create mode 100644 samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj create mode 100644 samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml create mode 100644 samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt create mode 100644 samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml create mode 100644 samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml create mode 100644 samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml create mode 100644 samples/interop/NativeEmbedSample.Android/SplashActivity.cs create mode 100644 src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs diff --git a/Avalonia.sln b/Avalonia.sln index 0a33bc4150..4728d25bfa 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -219,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Android", "samples\interop\NativeEmbedSample.Android\NativeEmbedSample.Android.csproj", "{7D287579-7DB4-4415-A52A-46A5CD6FE30F}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.iOS", "samples\interop\NativeEmbedSample.iOS\NativeEmbedSample.iOS.csproj", "{28DB5AD1-656D-4619-BE0B-5B475E138DF8}" @@ -1993,6 +1995,30 @@ Global {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhone.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhone.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|Any CPU.Build.0 = Release|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhone.ActiveCfg = Release|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhone.Build.0 = Release|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -2100,6 +2126,7 @@ Global {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} + {7D287579-7DB4-4415-A52A-46A5CD6FE30F} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {F2389463-DDB4-4317-B894-D4DF9FF6B763} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {28DB5AD1-656D-4619-BE0B-5B475E138DF8} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} EndGlobalSection diff --git a/samples/interop/NativeEmbedSample.Android/MainActivity.cs b/samples/interop/NativeEmbedSample.Android/MainActivity.cs new file mode 100644 index 0000000000..5d2e06a330 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/MainActivity.cs @@ -0,0 +1,12 @@ +using Android.App; +using Android.Content.PM; + +using Avalonia; +using Avalonia.Android; + +namespace NativeEmbedSample.Android; + +[Activity(Label = "NativeEmbedSample", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] +public class MainActivity : AvaloniaActivity +{ +} diff --git a/samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj b/samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj new file mode 100644 index 0000000000..adfd74b9d5 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj @@ -0,0 +1,50 @@ + + + net6.0-android + 21 + Exe + enable + com.Avalonia.NativeEmbedSample + 1 + 1.0 + apk + true + + + + + + + Resources\drawable\Icon.png + + + + + True + True + True + True + + + + False + False + + + + True + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml b/samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..aa570ec504 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt b/samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt new file mode 100644 index 0000000000..c2bca974c4 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml b/samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml new file mode 100644 index 0000000000..2e920b4b3b --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml b/samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..59279d5d32 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + diff --git a/samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml b/samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml new file mode 100644 index 0000000000..2759d2904a --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/samples/interop/NativeEmbedSample.Android/SplashActivity.cs b/samples/interop/NativeEmbedSample.Android/SplashActivity.cs new file mode 100644 index 0000000000..78c555f5c5 --- /dev/null +++ b/samples/interop/NativeEmbedSample.Android/SplashActivity.cs @@ -0,0 +1,16 @@ +using Android.App; +using Android.Content; +using Android.OS; + +namespace NativeEmbedSample.Android; + +[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] +public class SplashActivity : Activity +{ + protected override void OnResume() + { + base.OnResume(); + + StartActivity(new Intent(Application.Context, typeof(MainActivity))); + } +} diff --git a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs b/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs index ed3b9aeeb0..4569992f56 100644 --- a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs +++ b/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs @@ -3,7 +3,6 @@ using System; using System.IO; using System.Diagnostics; using Android.Views; -using Android.Webkit; using Avalonia.Controls.Platform; using Avalonia.Platform; @@ -13,9 +12,24 @@ public partial class EmbedSample { private IPlatformHandle CreateAndroid(IPlatformHandle parent) { - var button = new Android.Widget.Button(Android.App.Application.Context) { Text = "Android button" }; + if (IsSecond) + { + var webView = new Android.Webkit.WebView(Android.App.Application.Context); + webView.LoadUrl("https://www.android.com/"); - return new AndroidViewHandle(button); + return new AndroidViewHandle(webView); + } + else + { + var button = new Android.Widget.Button(Android.App.Application.Context) { Text = "Hello world" }; + var clickCount = 0; + button.Click += (sender, args) => + { + clickCount++; + button.Text = $"Click count {clickCount}"; + }; + return new AndroidViewHandle(button); + } } private void DestroyAndroid(IPlatformHandle control) diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index 8177cf1f69..be0aa27393 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -19,9 +19,8 @@ namespace Avalonia.Android public AvaloniaView(Context context) : base(context) { - _view = new ViewImpl(context); + _view = new ViewImpl(this); AddView(_view.View); - } internal void Prepare () @@ -30,6 +29,8 @@ namespace Avalonia.Android _root.Prepare(); } + internal TopLevelImpl TopLevelImpl => _view; + public object Content { get { return _root.Content; } @@ -73,7 +74,7 @@ namespace Avalonia.Android class ViewImpl : TopLevelImpl { - public ViewImpl(Context context) : base(context) + public ViewImpl(AvaloniaView avaloniaView) : base(avaloniaView) { View.Focusable = true; View.FocusChange += ViewImpl_FocusChange; diff --git a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs new file mode 100644 index 0000000000..57d897f6f5 --- /dev/null +++ b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs @@ -0,0 +1,187 @@ +#nullable enable + +using System; +using System.Diagnostics.CodeAnalysis; +using Android.Views; +using Android.Widget; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +namespace Avalonia.Android.Platform +{ + internal class AndroidNativeControlHostImpl : INativeControlHostImpl + { + private readonly AvaloniaView _avaloniaView; + + public AndroidNativeControlHostImpl(AvaloniaView avaloniaView) + { + _avaloniaView = avaloniaView; + } + + public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) + { + return new AndroidViewControlHandle(new FrameLayout(_avaloniaView.Context!), false); + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) + { + var holder = new AndroidViewControlHandle(_avaloniaView, false); + AndroidNativeControlAttachment? attachment = null; + try + { + var child = create(holder); + // It has to be assigned to the variable before property setter is called so we dispose it on exception +#pragma warning disable IDE0017 // Simplify object initialization + attachment = new AndroidNativeControlAttachment(child); +#pragma warning restore IDE0017 // Simplify object initialization + attachment.AttachedTo = this; + return attachment; + } + catch + { + attachment?.Dispose(); + holder?.Destroy(); + throw; + } + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) + { + return new AndroidNativeControlAttachment(handle) + { + AttachedTo = this + }; + } + + public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == AndroidViewControlHandle.AndroidDescriptor; + + class AndroidNativeControlAttachment : INativeControlHostControlTopLevelAttachment + { + // ReSharper disable once NotAccessedField.Local (keep GC reference) + private IPlatformHandle? _child; + private View? _view; + private AndroidNativeControlHostImpl? _attachedTo; + + public AndroidNativeControlAttachment(IPlatformHandle child) + { + _child = child; + + _view = (child as AndroidViewControlHandle)?.View + ?? Java.Lang.Object.GetObject(child.Handle, global::Android.Runtime.JniHandleOwnership.DoNotTransfer); + } + + [MemberNotNull(nameof(_view))] + private void CheckDisposed() + { + if (_view == null) + throw new ObjectDisposedException(nameof(AndroidNativeControlAttachment)); + } + + public void Dispose() + { + if (_view != null && _attachedTo?._avaloniaView is ViewGroup parent) + { + parent.RemoveView(_view); + } + _child = null; + _attachedTo = null; + _view?.Dispose(); + _view = null; + } + + public INativeControlHostImpl? AttachedTo + { + get => _attachedTo; + set + { + CheckDisposed(); + + var oldAttachedTo = _attachedTo; + _attachedTo = (AndroidNativeControlHostImpl?)value; + if (_attachedTo == null) + { + oldAttachedTo?._avaloniaView.RemoveView(_view); + } + else + { + _attachedTo._avaloniaView.AddView(_view); + } + } + } + + public bool IsCompatibleWith(INativeControlHostImpl host) => host is AndroidNativeControlHostImpl; + + public void HideWithSize(Size size) + { + CheckDisposed(); + _view.Visibility = ViewStates.Gone; + } + + public void ShowInBounds(Rect bounds) + { + CheckDisposed(); + if (_attachedTo == null) + throw new InvalidOperationException("The control isn't currently attached to a toplevel"); + + bounds *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; + _view.Visibility = ViewStates.Visible; + _view.LayoutParameters = new FrameLayout.LayoutParams((int)bounds.Width, (int)bounds.Height) + { + LeftMargin = (int)bounds.X, + TopMargin = (int)bounds.Y + }; + _view.RequestLayout(); + } + } + } + + public class AndroidViewControlHandle : INativeControlHostDestroyableControlHandle, IDisposable + { + internal const string AndroidDescriptor = "JavaHandle"; + + private View? _view; + private bool _disposeView; + + public AndroidViewControlHandle(View view, bool disposeView) + { + _view = view; + _disposeView = disposeView; + } + + public View View => _view ?? throw new ObjectDisposedException(nameof(AndroidViewControlHandle)); + + public string HandleDescriptor => AndroidDescriptor; + + IntPtr IPlatformHandle.Handle => _view?.Handle ?? default; + + public void Destroy() + { + Dispose(true); + } + + void IDisposable.Dispose() + { + Dispose(true); + } + + ~AndroidViewControlHandle() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (_disposeView) + { + _view?.Dispose(); + } + + _view = null; + if (disposing) + { + GC.SuppressFinalize(this); + } + } + } +} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 8a475676a5..dbcc9bccdd 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -20,7 +20,7 @@ using Avalonia.Rendering; namespace Avalonia.Android.Platform.SkiaPlatform { - class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod + class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost { private readonly IGlPlatformSurface _gl; private readonly IFramebufferPlatformSurface _framebuffer; @@ -30,9 +30,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly ITextInputMethodImpl _textInputMethod; private ViewImpl _view; - public TopLevelImpl(Context context, bool placeOnTop = false) + public TopLevelImpl(AvaloniaView avaloniaView, bool placeOnTop = false) { - _view = new ViewImpl(context, this, placeOnTop); + _view = new ViewImpl(avaloniaView.Context, this, placeOnTop); _textInputMethod = new AndroidInputMethod(_view); _keyboardHelper = new AndroidKeyboardEventsHelper(this); _touchHelper = new AndroidTouchEventsHelper(this, () => InputRoot, @@ -44,6 +44,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform MaxClientSize = new PixelSize(_view.Resources.DisplayMetrics.WidthPixels, _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling); + + NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView); } public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) => @@ -222,6 +224,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform public ITextInputMethodImpl TextInputMethod => _textInputMethod; + public INativeControlHostImpl NativeControlHost { get; } + public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { throw new NotImplementedException(); From 7af062e8ba67b8ff8538c8192867ec6de05678ce Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 00:51:22 -0400 Subject: [PATCH 583/820] Respect actual AttachedTo in ios control host --- src/iOS/Avalonia.iOS/NativeControlHostImpl.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs index c5c632e7f7..fc5c90fa4c 100644 --- a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs +++ b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs @@ -21,19 +21,19 @@ namespace Avalonia.iOS public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) { - return new UIViewControlHandle(new UIView()); + return new UIViewControlHandle(new UIView(), true); } public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) { - var holder = new UIViewControlHandle(_avaloniaView); + var holder = new UIViewControlHandle(_avaloniaView, false); NativeControlAttachment? attachment = null; try { var child = create(holder); // It has to be assigned to the variable before property setter is called so we dispose it on exception #pragma warning disable IDE0017 // Simplify object initialization - attachment = new NativeControlAttachment(holder, child); + attachment = new NativeControlAttachment(child); #pragma warning restore IDE0017 // Simplify object initialization attachment.AttachedTo = this; return attachment; @@ -48,7 +48,7 @@ namespace Avalonia.iOS public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) { - return new NativeControlAttachment(new UIViewControlHandle(_avaloniaView), handle) + return new NativeControlAttachment(handle) { AttachedTo = this }; @@ -68,17 +68,14 @@ namespace Avalonia.iOS { // ReSharper disable once NotAccessedField.Local (keep GC reference) private IPlatformHandle? _child; - private UIViewControlHandle? _holder; private UIView? _view; private NativeControlHostImpl? _attachedTo; - public NativeControlAttachment(UIViewControlHandle holder, IPlatformHandle child) + public NativeControlAttachment(IPlatformHandle child) { - _holder = holder; _child = child; _view = (child as UIViewControlHandle)?.View ?? new ViewHolder(child.Handle); - _holder.View.AddSubview(_view); } [MemberNotNull(nameof(_view))] @@ -91,7 +88,6 @@ namespace Avalonia.iOS public void Dispose() { _view?.RemoveFromSuperview(); - _holder = null; _child = null; _attachedTo = null; _view?.Dispose(); @@ -112,7 +108,7 @@ namespace Avalonia.iOS } else { - _holder!.View.AddSubview(_view); + _attachedTo._avaloniaView.AddSubview(_view); } } } @@ -131,7 +127,6 @@ namespace Avalonia.iOS if (_attachedTo == null) throw new InvalidOperationException("The control isn't currently attached to a toplevel"); - //bounds = _attachedTo._avaloniaView.ContentScaleFactor; _view.Frame = new CGRect(bounds.X, bounds.Y, bounds.Width, bounds.Height); _view.Hidden = false; } @@ -143,10 +138,12 @@ namespace Avalonia.iOS internal const string UIViewDescriptor = "UIView"; private UIView? _view; + private bool _disposeView; - public UIViewControlHandle(UIView view) + public UIViewControlHandle(UIView view, bool disposeView) { _view = view; + _disposeView = disposeView; } public UIView View => _view ?? throw new ObjectDisposedException(nameof(UIViewControlHandle)); @@ -172,7 +169,11 @@ namespace Avalonia.iOS private void Dispose(bool disposing) { - _view?.Dispose(); + if (_disposeView) + { + _view?.Dispose(); + } + _view = null; if (disposing) { From 08487446d98311daa2d6d2304fc4647f984ea8b9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 10 May 2022 14:19:35 +0100 Subject: [PATCH 584/820] [OSX] cache IsClientAreaExtendedToDecorations, and apply it when NSPanel / NSWindow is created and Shown. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 56 ++++++++++++------- .../ViewModels/MainWindowViewModel.cs | 2 +- src/Avalonia.Native/WindowImpl.cs | 7 +++ 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 63a38f0c22..7ab2b2b5fc 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -55,8 +55,20 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) { @autoreleasepool { _isDialog = isDialog; + + bool created = Window == nullptr; + WindowBaseImpl::Show(activate, isDialog); + if(created) + { + if(_isClientAreaExtended) + { + [GetWindowProtocol() setIsExtended:true]; + SetExtendClientArea(true); + } + } + HideOrShowTrafficLights(); return SetWindowState(_lastWindowState); @@ -327,37 +339,39 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) { @autoreleasepool { _isClientAreaExtended = enable; - if (enable) { - Window.titleVisibility = NSWindowTitleHidden; + if(Window != nullptr) { + if (enable) { + Window.titleVisibility = NSWindowTitleHidden; - [Window setTitlebarAppearsTransparent:true]; + [Window setTitlebarAppearsTransparent:true]; - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - if (wantsTitleBar) { - [StandardContainer ShowTitleBar:true]; - } else { - [StandardContainer ShowTitleBar:false]; - } + if (wantsTitleBar) { + [StandardContainer ShowTitleBar:true]; + } else { + [StandardContainer ShowTitleBar:false]; + } - if (_extendClientHints & AvnOSXThickTitleBar) { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } else { + Window.toolbar = nullptr; + } } else { + Window.titleVisibility = NSWindowTitleVisible; Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; } - } else { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - [GetWindowProtocol() setIsExtended:enable]; + [GetWindowProtocol() setIsExtended:enable]; - HideOrShowTrafficLights(); + HideOrShowTrafficLights(); - UpdateStyle(); + UpdateStyle(); + } return S_OK; } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 2b0c30f311..c44024d952 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels private WindowState _windowState; private WindowState[] _windowStates; private int _transparencyLevel; - private ExtendClientAreaChromeHints _chromeHints; + private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome; private bool _extendClientAreaEnabled; private bool _systemTitleBarEnabled; private bool _preferSystemChromeEnabled; diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index c082bdb1b8..b5af927ea0 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -107,6 +107,13 @@ namespace Avalonia.Native private bool _isExtended; public bool IsClientAreaExtendedToDecorations => _isExtended; + public override void Show(bool activate, bool isDialog) + { + base.Show(activate, isDialog); + + InvalidateExtendedMargins(); + } + protected override bool ChromeHitTest (RawPointerEventArgs e) { if(_isExtended) From 65ed75970dd54e9c68d37c2bf67440c754018d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:15 +0200 Subject: [PATCH 585/820] Fix TextBox property name registrations --- src/Avalonia.Controls/TextBox.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 0be58e7fcc..45e66828c1 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -55,13 +55,13 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(PasswordChar)); public static readonly StyledProperty SelectionBrushProperty = - AvaloniaProperty.Register(nameof(SelectionBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionBrush)); public static readonly StyledProperty SelectionForegroundBrushProperty = - AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionForegroundBrush)); public static readonly StyledProperty CaretBrushProperty = - AvaloniaProperty.Register(nameof(CaretBrushProperty)); + AvaloniaProperty.Register(nameof(CaretBrush)); public static readonly DirectProperty SelectionStartProperty = AvaloniaProperty.RegisterDirect( From 665b0fa3b6db07ef85463453a5e0a0da2a99d065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:26 +0200 Subject: [PATCH 586/820] Fix TextPresenter property name registrations --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 0785149a73..07061940b5 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -26,13 +26,13 @@ namespace Avalonia.Controls.Presenters AvaloniaProperty.Register(nameof(PasswordChar)); public static readonly StyledProperty SelectionBrushProperty = - AvaloniaProperty.Register(nameof(SelectionBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionBrush)); public static readonly StyledProperty SelectionForegroundBrushProperty = - AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionForegroundBrush)); public static readonly StyledProperty CaretBrushProperty = - AvaloniaProperty.Register(nameof(CaretBrushProperty)); + AvaloniaProperty.Register(nameof(CaretBrush)); public static readonly DirectProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner( From d8e01f0e1a67f72f71a81a271c000042934a6196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:35 +0200 Subject: [PATCH 587/820] Fix DockPanel property name registrations --- src/Avalonia.Controls/DockPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs index 8e23555c2d..3e3ed509b5 100644 --- a/src/Avalonia.Controls/DockPanel.cs +++ b/src/Avalonia.Controls/DockPanel.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls ///

public static readonly StyledProperty LastChildFillProperty = AvaloniaProperty.Register( - nameof(LastChildFillProperty), + nameof(LastChildFill), defaultValue: true); /// From d2475dd53b4830ef5332a9de79f8ac9214bd8c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:28:44 +0200 Subject: [PATCH 588/820] Fix owner of IsTextSearchEnabled property --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index bff6799792..164aab32db 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty IsTextSearchEnabledProperty = - AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false); + AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false); /// /// Event that should be raised by items that implement to From 3880f1abf41e4494c84af8fc52492f5adfbb4127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:30:19 +0200 Subject: [PATCH 589/820] Fix PasswordCharProperty owner --- src/Avalonia.Controls/MaskedTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs index 933788f9ea..080326606e 100644 --- a/src/Avalonia.Controls/MaskedTextBox.cs +++ b/src/Avalonia.Controls/MaskedTextBox.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(Mask), string.Empty); public static new readonly StyledProperty PasswordCharProperty = - AvaloniaProperty.Register(nameof(PasswordChar), '\0'); + AvaloniaProperty.Register(nameof(PasswordChar), '\0'); public static readonly StyledProperty PromptCharProperty = AvaloniaProperty.Register(nameof(PromptChar), '_'); From ee33319be119129cef8ca1fe0a243b4d976ee109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:31:25 +0200 Subject: [PATCH 590/820] Fix TickPlacementProperty property owner --- src/Avalonia.Controls/Slider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 64dfce22d4..b6a6f3d02c 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty TickPlacementProperty = - AvaloniaProperty.Register(nameof(TickPlacement), 0d); + AvaloniaProperty.Register(nameof(TickPlacement), 0d); /// /// Defines the property. From da640adb0cec38e95d2910bf54371ac6f973fa9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:40:00 +0200 Subject: [PATCH 591/820] Fix PaneTemplateProperty property owner --- src/Avalonia.Controls/SplitView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs index ae1605a985..532cb1d329 100644 --- a/src/Avalonia.Controls/SplitView.cs +++ b/src/Avalonia.Controls/SplitView.cs @@ -138,7 +138,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty PaneTemplateProperty = - AvaloniaProperty.Register(nameof(PaneTemplate)); + AvaloniaProperty.Register(nameof(PaneTemplate)); /// /// Defines the property From c6eb4138447fd2e9758a8d893fff6b60150268df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:42:10 +0200 Subject: [PATCH 592/820] Use AddOwner for PlacementRectProperty --- src/Avalonia.Controls/ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index ee2378101a..2b122d4174 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -63,7 +63,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty PlacementRectProperty = - AvaloniaProperty.Register(nameof(PlacementRect)); + Popup.PlacementRectProperty.AddOwner(); /// /// Defines the property. From 05fe5fb4ebfc164723ddbe476049499fae675098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:43:15 +0200 Subject: [PATCH 593/820] Fix owner for WindowManagerAddShadowHintProperty property --- src/Avalonia.Controls/Primitives/Popup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 7a7e41b029..95e5e25c42 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -27,7 +27,7 @@ namespace Avalonia.Controls.Primitives #pragma warning restore CS0612 // Type or member is obsolete { public static readonly StyledProperty WindowManagerAddShadowHintProperty = - AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); + AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); /// /// Defines the property. From 2d6bd60f691434e667cbbbdff933dfda9d4c0f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:46:41 +0200 Subject: [PATCH 594/820] Rename --- src/Avalonia.Controls/Primitives/Track.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 49f0cda982..85343b8fbb 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(IsDirectionReversed)); public static readonly StyledProperty IgnoreThumbDragProperty = - AvaloniaProperty.Register(nameof(IsThumbDragHandled)); + AvaloniaProperty.Register(nameof(IgnoreThumbDrag)); private double _minimum; private double _maximum = 100.0; @@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives set { SetValue(IsDirectionReversedProperty, value); } } - public bool IsThumbDragHandled + public bool IgnoreThumbDrag { get { return GetValue(IgnoreThumbDragProperty); } set { SetValue(IgnoreThumbDragProperty, value); } From da7eecec0788d086fe6a02138755c770509ffe47 Mon Sep 17 00:00:00 2001 From: Kaktusbot Date: Tue, 10 May 2022 22:38:03 +0400 Subject: [PATCH 595/820] Fix missing NotifyCountChanged in AvaloniaList.AddRange --- src/Avalonia.Base/Collections/AvaloniaList.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 9972e72dd4..a05c9c5da3 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -394,7 +394,13 @@ namespace Avalonia.Collections } while (en.MoveNext()); if (notificationItems is not null) + { NotifyAdd(notificationItems, index); + } + else + { + NotifyCountChanged(); + } } } } From 5eca6cc83e3cbb9cbef2e5b0c48959eee32e78b7 Mon Sep 17 00:00:00 2001 From: Kaktusbot Date: Tue, 10 May 2022 22:57:55 +0400 Subject: [PATCH 596/820] Add test to AvaloniaList.AddRange to notify Count property changed --- .../Collections/AvaloniaListTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs index f5ae4cc1e0..b59f272b0c 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs @@ -146,6 +146,23 @@ namespace Avalonia.Base.UnitTests.Collections Assert.True(raised); } + [Fact] + public void AddRange_IEnumerable_Should_Raise_Count_PropertyChanged() + { + var target = new AvaloniaList(new[] { 1, 2, 3, 4, 5 }); + var raised = false; + + target.PropertyChanged += (s, e) => { + Assert.Equal(e.PropertyName, nameof(target.Count)); + Assert.Equal(target.Count, 7); + raised = true; + }; + + target.AddRange(Enumerable.Range(6, 2)); + + Assert.True(raised); + } + [Fact] public void AddRange_Items_Should_Raise_Correct_CollectionChanged() { From 76c3691fb6b3db593e50a1811e06c2bdb8a86ad0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 16:11:16 -0400 Subject: [PATCH 597/820] Resize on HideWithSize --- .../Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs | 4 +++- src/iOS/Avalonia.iOS/NativeControlHostImpl.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs index 57d897f6f5..747c0489ea 100644 --- a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs +++ b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs @@ -116,6 +116,8 @@ namespace Avalonia.Android.Platform { CheckDisposed(); _view.Visibility = ViewStates.Gone; + _view.LayoutParameters = new ViewGroup.LayoutParams(Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height)); + _view.RequestLayout(); } public void ShowInBounds(Rect bounds) @@ -126,7 +128,7 @@ namespace Avalonia.Android.Platform bounds *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; _view.Visibility = ViewStates.Visible; - _view.LayoutParameters = new FrameLayout.LayoutParams((int)bounds.Width, (int)bounds.Height) + _view.LayoutParameters = new ViewGroup.MarginLayoutParams(Math.Max(1, (int)bounds.Width), Math.Max(1, (int)bounds.Height)) { LeftMargin = (int)bounds.X, TopMargin = (int)bounds.Y diff --git a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs index fc5c90fa4c..fc0244fe59 100644 --- a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs +++ b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs @@ -118,7 +118,9 @@ namespace Avalonia.iOS public void HideWithSize(Size size) { CheckDisposed(); + _view.Hidden = true; + _view.Frame = new CGRect(0d, 0d, Math.Max(1d, size.Width), Math.Max(1d, size.Height)); } public void ShowInBounds(Rect bounds) @@ -127,7 +129,7 @@ namespace Avalonia.iOS if (_attachedTo == null) throw new InvalidOperationException("The control isn't currently attached to a toplevel"); - _view.Frame = new CGRect(bounds.X, bounds.Y, bounds.Width, bounds.Height); + _view.Frame = new CGRect(bounds.X, bounds.Y, Math.Max(1d, bounds.Width), Math.Max(1d, bounds.Height)); _view.Hidden = false; } } From 626082970212691af54281d84d65b82f84b50e3e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 16:19:06 -0400 Subject: [PATCH 598/820] Simplify UIViewControlHandle/AndroidViewControlHandle --- Avalonia.sln | 55 ++++++++-------- .../Android/EmbedSample.Android.cs | 42 +++--------- .../NativeEmbedSample.csproj | 6 ++ .../NativeEmbedSample/iOS/EmbedSample.iOS.cs | 28 +------- .../AndroidViewControlHandle.cs | 32 ++++++++++ .../Platform/AndroidNativeControlHostImpl.cs | 64 ++----------------- src/iOS/Avalonia.iOS/NativeControlHostImpl.cs | 50 ++++----------- 7 files changed, 94 insertions(+), 183 deletions(-) create mode 100644 src/Android/Avalonia.Android/AndroidViewControlHandle.cs diff --git a/Avalonia.sln b/Avalonia.sln index 4728d25bfa..4432e572bb 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -219,11 +219,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Android", "samples\interop\NativeEmbedSample.Android\NativeEmbedSample.Android.csproj", "{7D287579-7DB4-4415-A52A-46A5CD6FE30F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample.Android", "samples\interop\NativeEmbedSample.Android\NativeEmbedSample.Android.csproj", "{7D287579-7DB4-4415-A52A-46A5CD6FE30F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample.iOS", "samples\interop\NativeEmbedSample.iOS\NativeEmbedSample.iOS.csproj", "{28DB5AD1-656D-4619-BE0B-5B475E138DF8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample.iOS", "samples\interop\NativeEmbedSample.iOS\NativeEmbedSample.iOS.csproj", "{28DB5AD1-656D-4619-BE0B-5B475E138DF8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1419,6 +1419,30 @@ Global {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhone.Build.0 = Release|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -1971,30 +1995,6 @@ Global {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -2009,6 +2009,7 @@ Global {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhone.ActiveCfg = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhone.Build.0 = Debug|Any CPU {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU diff --git a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs b/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs index 4569992f56..a4bd9b329b 100644 --- a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs +++ b/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs @@ -1,10 +1,6 @@ #if __ANDROID__ || ANDROID -using System; -using System.IO; -using System.Diagnostics; -using Android.Views; -using Avalonia.Controls.Platform; using Avalonia.Platform; +using Avalonia.Android; namespace NativeEmbedSample; @@ -12,23 +8,27 @@ public partial class EmbedSample { private IPlatformHandle CreateAndroid(IPlatformHandle parent) { + var parentContext = (parent as AndroidViewControlHandle)?.View.Context + ?? Android.App.Application.Context; + if (IsSecond) { - var webView = new Android.Webkit.WebView(Android.App.Application.Context); + var webView = new Android.Webkit.WebView(parentContext); webView.LoadUrl("https://www.android.com/"); - return new AndroidViewHandle(webView); + return new AndroidViewControlHandle(webView); } else { - var button = new Android.Widget.Button(Android.App.Application.Context) { Text = "Hello world" }; + var button = new Android.Widget.Button(parentContext) { Text = "Hello world" }; var clickCount = 0; button.Click += (sender, args) => { clickCount++; button.Text = $"Click count {clickCount}"; }; - return new AndroidViewHandle(button); + + return new AndroidViewControlHandle(button); } } @@ -37,28 +37,4 @@ public partial class EmbedSample base.DestroyNativeControlCore(control); } } - -internal sealed class AndroidViewHandle : INativeControlHostDestroyableControlHandle -{ - private View _view; - - public AndroidViewHandle(View view) - { - _view = view; - } - - public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; - public string HandleDescriptor => "JavaHandle"; - - public void Destroy() - { - _view?.Dispose(); - _view = null; - } - - ~AndroidViewHandle() - { - Destroy(); - } -} #endif diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj index 34206c2b63..7783b6f43d 100644 --- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj +++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj @@ -27,6 +27,12 @@ PreserveNewest + + + + + + diff --git a/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs b/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs index 185c2e6b9f..c5996e7deb 100644 --- a/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs +++ b/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs @@ -1,13 +1,10 @@ #if IOS -using System; -using System.IO; -using System.Diagnostics; -using Avalonia.Controls.Platform; using Avalonia.Platform; using CoreGraphics; using Foundation; using UIKit; using WebKit; +using Avalonia.iOS; namespace NativeEmbedSample; @@ -20,7 +17,7 @@ public partial class EmbedSample var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration()); webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/"))); - return new UIViewHandle(webView); + return new UIViewControlHandle(webView); } else { @@ -34,7 +31,7 @@ public partial class EmbedSample button.SetTitle($"Click count {clickCount}", UIControlState.Normal); }, UIControlEvent.TouchDown); - return new UIViewHandle(button); + return new UIViewControlHandle(button); } } @@ -43,23 +40,4 @@ public partial class EmbedSample base.DestroyNativeControlCore(control); } } - -internal class UIViewHandle : INativeControlHostDestroyableControlHandle -{ - private UIView _view; - - public UIViewHandle(UIView view) - { - _view = view; - } - - public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; - public string HandleDescriptor => "UIView"; - - public void Destroy() - { - _view?.Dispose(); - _view = null; - } -} #endif diff --git a/src/Android/Avalonia.Android/AndroidViewControlHandle.cs b/src/Android/Avalonia.Android/AndroidViewControlHandle.cs new file mode 100644 index 0000000000..e999d198c6 --- /dev/null +++ b/src/Android/Avalonia.Android/AndroidViewControlHandle.cs @@ -0,0 +1,32 @@ +#nullable enable + +using System; + +using Android.Views; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +namespace Avalonia.Android +{ + public class AndroidViewControlHandle : INativeControlHostDestroyableControlHandle + { + internal const string AndroidDescriptor = "JavaObjectHandle"; + + public AndroidViewControlHandle(View view) + { + View = view; + } + + public View View { get; } + + public string HandleDescriptor => AndroidDescriptor; + + IntPtr IPlatformHandle.Handle => View.Handle; + + public void Destroy() + { + View?.Dispose(); + } + } +} diff --git a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs index 747c0489ea..b5563d9625 100644 --- a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs +++ b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; + using Android.Views; using Android.Widget; @@ -21,16 +22,16 @@ namespace Avalonia.Android.Platform public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) { - return new AndroidViewControlHandle(new FrameLayout(_avaloniaView.Context!), false); + return new AndroidViewControlHandle(new FrameLayout(_avaloniaView.Context!)); } public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) { - var holder = new AndroidViewControlHandle(_avaloniaView, false); + var parent = new AndroidViewControlHandle(_avaloniaView); AndroidNativeControlAttachment? attachment = null; try { - var child = create(holder); + var child = create(parent); // It has to be assigned to the variable before property setter is called so we dispose it on exception #pragma warning disable IDE0017 // Simplify object initialization attachment = new AndroidNativeControlAttachment(child); @@ -41,7 +42,6 @@ namespace Avalonia.Android.Platform catch { attachment?.Dispose(); - holder?.Destroy(); throw; } } @@ -56,17 +56,13 @@ namespace Avalonia.Android.Platform public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == AndroidViewControlHandle.AndroidDescriptor; - class AndroidNativeControlAttachment : INativeControlHostControlTopLevelAttachment + private class AndroidNativeControlAttachment : INativeControlHostControlTopLevelAttachment { - // ReSharper disable once NotAccessedField.Local (keep GC reference) - private IPlatformHandle? _child; private View? _view; private AndroidNativeControlHostImpl? _attachedTo; public AndroidNativeControlAttachment(IPlatformHandle child) { - _child = child; - _view = (child as AndroidViewControlHandle)?.View ?? Java.Lang.Object.GetObject(child.Handle, global::Android.Runtime.JniHandleOwnership.DoNotTransfer); } @@ -84,7 +80,6 @@ namespace Avalonia.Android.Platform { parent.RemoveView(_view); } - _child = null; _attachedTo = null; _view?.Dispose(); _view = null; @@ -137,53 +132,4 @@ namespace Avalonia.Android.Platform } } } - - public class AndroidViewControlHandle : INativeControlHostDestroyableControlHandle, IDisposable - { - internal const string AndroidDescriptor = "JavaHandle"; - - private View? _view; - private bool _disposeView; - - public AndroidViewControlHandle(View view, bool disposeView) - { - _view = view; - _disposeView = disposeView; - } - - public View View => _view ?? throw new ObjectDisposedException(nameof(AndroidViewControlHandle)); - - public string HandleDescriptor => AndroidDescriptor; - - IntPtr IPlatformHandle.Handle => _view?.Handle ?? default; - - public void Destroy() - { - Dispose(true); - } - - void IDisposable.Dispose() - { - Dispose(true); - } - - ~AndroidViewControlHandle() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - if (_disposeView) - { - _view?.Dispose(); - } - - _view = null; - if (disposing) - { - GC.SuppressFinalize(this); - } - } - } } diff --git a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs index fc0244fe59..d477f81a53 100644 --- a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs +++ b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs @@ -21,16 +21,16 @@ namespace Avalonia.iOS public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) { - return new UIViewControlHandle(new UIView(), true); + return new UIViewControlHandle(new UIView()); } public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) { - var holder = new UIViewControlHandle(_avaloniaView, false); + var parent = new UIViewControlHandle(_avaloniaView); NativeControlAttachment? attachment = null; try { - var child = create(holder); + var child = create(parent); // It has to be assigned to the variable before property setter is called so we dispose it on exception #pragma warning disable IDE0017 // Simplify object initialization attachment = new NativeControlAttachment(child); @@ -41,7 +41,6 @@ namespace Avalonia.iOS catch { attachment?.Dispose(); - holder?.Destroy(); throw; } } @@ -134,53 +133,26 @@ namespace Avalonia.iOS } } } - - public class UIViewControlHandle : INativeControlHostDestroyableControlHandle, IDisposable + + public class UIViewControlHandle : INativeControlHostDestroyableControlHandle { internal const string UIViewDescriptor = "UIView"; + - private UIView? _view; - private bool _disposeView; - - public UIViewControlHandle(UIView view, bool disposeView) + public UIViewControlHandle(UIView view) { - _view = view; - _disposeView = disposeView; + View = view; } - public UIView View => _view ?? throw new ObjectDisposedException(nameof(UIViewControlHandle)); + public UIView View { get; } public string HandleDescriptor => UIViewDescriptor; - IntPtr IPlatformHandle.Handle => _view?.Handle.Handle ?? default; + IntPtr IPlatformHandle.Handle => View.Handle.Handle; public void Destroy() { - Dispose(true); - } - - void IDisposable.Dispose() - { - Dispose(true); - } - - ~UIViewControlHandle() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - if (_disposeView) - { - _view?.Dispose(); - } - - _view = null; - if (disposing) - { - GC.SuppressFinalize(this); - } + View.Dispose(); } } } From 4315f4728f75a0103662a682b4b7553ced5c694c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 10 May 2022 16:37:24 -0400 Subject: [PATCH 599/820] Fix android type exception --- .../Platform/AndroidNativeControlHostImpl.cs | 6 ++++-- src/iOS/Avalonia.iOS/NativeControlHostImpl.cs | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs index b5563d9625..7d12404090 100644 --- a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs +++ b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs @@ -110,8 +110,10 @@ namespace Avalonia.Android.Platform public void HideWithSize(Size size) { CheckDisposed(); + + size *= _attachedTo?._avaloniaView.TopLevelImpl.RenderScaling ?? 1; _view.Visibility = ViewStates.Gone; - _view.LayoutParameters = new ViewGroup.LayoutParams(Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height)); + _view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height)); _view.RequestLayout(); } @@ -123,7 +125,7 @@ namespace Avalonia.Android.Platform bounds *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; _view.Visibility = ViewStates.Visible; - _view.LayoutParameters = new ViewGroup.MarginLayoutParams(Math.Max(1, (int)bounds.Width), Math.Max(1, (int)bounds.Height)) + _view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)bounds.Width), Math.Max(1, (int)bounds.Height)) { LeftMargin = (int)bounds.X, TopMargin = (int)bounds.Y diff --git a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs index d477f81a53..f752936dc8 100644 --- a/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs +++ b/src/iOS/Avalonia.iOS/NativeControlHostImpl.cs @@ -117,6 +117,8 @@ namespace Avalonia.iOS public void HideWithSize(Size size) { CheckDisposed(); + if (_attachedTo == null) + return; _view.Hidden = true; _view.Frame = new CGRect(0d, 0d, Math.Max(1d, size.Width), Math.Max(1d, size.Height)); From a90c4d4f22d737ee4231bacaf9f1763019822687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:47:24 +0200 Subject: [PATCH 600/820] Use correct property name --- src/Avalonia.Controls/Primitives/Track.cs | 2 +- src/Avalonia.Controls/Slider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 85343b8fbb..14ec7a2849 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -442,7 +442,7 @@ namespace Avalonia.Controls.Primitives private void ThumbDragged(object? sender, VectorEventArgs e) { - if (IsThumbDragHandled) + if (IgnoreThumbDrag) return; Value = MathUtilities.Clamp( diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index b6a6f3d02c..be87705b54 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -197,7 +197,7 @@ namespace Avalonia.Controls if (_track != null) { - _track.IsThumbDragHandled = true; + _track.IgnoreThumbDrag = true; } if (_decreaseButton != null) From 409d673215ba683559d062b406ea928303d4bf5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:48:54 +0200 Subject: [PATCH 601/820] Fix MaximumRowsOrColumnsProperty registration name --- src/Avalonia.Base/Layout/UniformGridLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs index 418cd55e41..47c994a350 100644 --- a/src/Avalonia.Base/Layout/UniformGridLayout.cs +++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs @@ -116,7 +116,7 @@ namespace Avalonia.Layout /// Defines the property. /// public static readonly StyledProperty MaximumRowsOrColumnsProperty = - AvaloniaProperty.Register(nameof(MinItemWidth)); + AvaloniaProperty.Register(nameof(MaximumRowsOrColumns)); /// /// Defines the property. From 1e3c5642a3a014fc255d0e11ef1ca7842a7d9f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:52:58 +0200 Subject: [PATCH 602/820] Fix ConicGradientBrush property registrations --- src/Avalonia.Base/Media/ConicGradientBrush.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs index 7c1266fa17..4b50019ddc 100644 --- a/src/Avalonia.Base/Media/ConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/ConicGradientBrush.cs @@ -11,7 +11,7 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty CenterProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(Center), RelativePoint.Center); @@ -19,7 +19,7 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty AngleProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(Angle), 0); From 57e852dfc1b47a62c86df14e0f93e5bea94371c8 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 11 May 2022 00:04:05 -0400 Subject: [PATCH 603/820] Move native control samples to the control catalog --- Avalonia.sln | 111 +---------------- .../EmbedSample.Android.cs | 23 ++-- .../ControlCatalog.Android/MainActivity.cs | 6 +- .../ControlCatalog.NetCore.csproj | 13 ++ .../NativeControls}/Gtk/EmbedSample.Gtk.cs | 22 ++-- .../NativeControls}/Gtk/GtkHelper.cs | 8 +- .../NativeControls}/Gtk/nodes-license.md | 0 .../NativeControls}/Gtk/nodes.mp4 | Bin .../NativeControls}/Mac/EmbedSample.Mac.cs | 21 ++-- .../NativeControls}/Mac/MacHelper.cs | 11 +- .../NativeControls}/Win/EmbedSample.Win.cs | 26 ++-- .../NativeControls}/Win/WinApi.cs | 4 +- samples/ControlCatalog.NetCore/Program.cs | 8 +- samples/ControlCatalog.iOS/AppDelegate.cs | 9 +- .../EmbedSample.iOS.cs | 17 +-- samples/ControlCatalog/ControlCatalog.csproj | 15 +++ samples/ControlCatalog/MainView.xaml | 7 +- .../Pages/NativeEmbedPage.xaml} | 22 ++-- .../Pages/NativeEmbedPage.xaml.cs | 84 +++++++++++++ .../NativeEmbedSample.Android/MainActivity.cs | 12 -- .../NativeEmbedSample.Android.csproj | 50 -------- .../Properties/AndroidManifest.xml | 4 - .../Resources/AboutResources.txt | 44 ------- .../Resources/drawable/splash_screen.xml | 13 -- .../Resources/values/colors.xml | 4 - .../Resources/values/styles.xml | 17 --- .../SplashActivity.cs | 16 --- .../NativeEmbedSample.Desktop.csproj | 15 --- .../NativeEmbedSample.Desktop/Program.cs | 17 --- .../NativeEmbedSample.iOS/AppDelegate.cs | 9 -- .../AppIcon.appiconset/Contents.json | 117 ------------------ .../AppIcon.appiconset/Icon1024.png | Bin 70429 -> 0 bytes .../AppIcon.appiconset/Icon120.png | Bin 3773 -> 0 bytes .../AppIcon.appiconset/Icon152.png | Bin 4750 -> 0 bytes .../AppIcon.appiconset/Icon167.png | Bin 4692 -> 0 bytes .../AppIcon.appiconset/Icon180.png | Bin 5192 -> 0 bytes .../AppIcon.appiconset/Icon20.png | Bin 1313 -> 0 bytes .../AppIcon.appiconset/Icon29.png | Bin 845 -> 0 bytes .../AppIcon.appiconset/Icon40.png | Bin 1101 -> 0 bytes .../AppIcon.appiconset/Icon58.png | Bin 1761 -> 0 bytes .../AppIcon.appiconset/Icon60.png | Bin 2537 -> 0 bytes .../AppIcon.appiconset/Icon76.png | Bin 2332 -> 0 bytes .../AppIcon.appiconset/Icon80.png | Bin 2454 -> 0 bytes .../AppIcon.appiconset/Icon87.png | Bin 2758 -> 0 bytes .../NativeEmbedSample.iOS/Entitlements.plist | 6 - .../interop/NativeEmbedSample.iOS/Info.plist | 42 ------- samples/interop/NativeEmbedSample.iOS/Main.cs | 6 - .../NativeEmbedSample.iOS.csproj | 16 --- .../Resources/LaunchScreen.xib | 43 ------- samples/interop/NativeEmbedSample/App.axaml | 7 -- .../interop/NativeEmbedSample/App.axaml.cs | 23 ---- .../interop/NativeEmbedSample/EmbedSample.cs | 54 -------- .../NativeEmbedSample/MainView.axaml.cs | 45 ------- .../NativeEmbedSample/MainWindow.axaml | 10 -- .../NativeEmbedSample/MainWindow.axaml.cs | 17 --- .../NativeEmbedSample.csproj | 38 ------ .../Platform/AndroidNativeControlHostImpl.cs | 4 +- 57 files changed, 211 insertions(+), 825 deletions(-) rename samples/{interop/NativeEmbedSample/Android => ControlCatalog.Android}/EmbedSample.Android.cs (52%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Gtk/EmbedSample.Gtk.cs (63%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Gtk/GtkHelper.cs (91%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Gtk/nodes-license.md (100%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Gtk/nodes.mp4 (100%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Mac/EmbedSample.Mac.cs (60%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Mac/MacHelper.cs (76%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Win/EmbedSample.Win.cs (62%) rename samples/{interop/NativeEmbedSample => ControlCatalog.NetCore/NativeControls}/Win/WinApi.cs (98%) rename samples/{interop/NativeEmbedSample/iOS => ControlCatalog.iOS}/EmbedSample.iOS.cs (75%) rename samples/{interop/NativeEmbedSample/MainView.axaml => ControlCatalog/Pages/NativeEmbedPage.xaml} (82%) create mode 100644 samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs delete mode 100644 samples/interop/NativeEmbedSample.Android/MainActivity.cs delete mode 100644 samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj delete mode 100644 samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml delete mode 100644 samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt delete mode 100644 samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml delete mode 100644 samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml delete mode 100644 samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml delete mode 100644 samples/interop/NativeEmbedSample.Android/SplashActivity.cs delete mode 100644 samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj delete mode 100644 samples/interop/NativeEmbedSample.Desktop/Program.cs delete mode 100644 samples/interop/NativeEmbedSample.iOS/AppDelegate.cs delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png delete mode 100644 samples/interop/NativeEmbedSample.iOS/Entitlements.plist delete mode 100644 samples/interop/NativeEmbedSample.iOS/Info.plist delete mode 100644 samples/interop/NativeEmbedSample.iOS/Main.cs delete mode 100644 samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj delete mode 100644 samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib delete mode 100644 samples/interop/NativeEmbedSample/App.axaml delete mode 100644 samples/interop/NativeEmbedSample/App.axaml.cs delete mode 100644 samples/interop/NativeEmbedSample/EmbedSample.cs delete mode 100644 samples/interop/NativeEmbedSample/MainView.axaml.cs delete mode 100644 samples/interop/NativeEmbedSample/MainWindow.axaml delete mode 100644 samples/interop/NativeEmbedSample/MainWindow.axaml.cs delete mode 100644 samples/interop/NativeEmbedSample/NativeEmbedSample.csproj diff --git a/Avalonia.sln b/Avalonia.sln index 4432e572bb..c3554a7447 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -97,6 +97,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\DevAnalyzers.props = build\DevAnalyzers.props build\EmbedXaml.props = build\EmbedXaml.props build\HarfBuzzSharp.props = build\HarfBuzzSharp.props + build\ImageSharp.props = build\ImageSharp.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props build\Microsoft.CSharp.props = build\Microsoft.CSharp.props @@ -117,7 +118,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\System.Memory.props = build\System.Memory.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\XUnit.props = build\XUnit.props - build\ImageSharp.props = build\ImageSharp.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" @@ -179,8 +179,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}" @@ -219,12 +217,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample.Android", "samples\interop\NativeEmbedSample.Android\NativeEmbedSample.Android.csproj", "{7D287579-7DB4-4415-A52A-46A5CD6FE30F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample.Desktop", "samples\interop\NativeEmbedSample.Desktop\NativeEmbedSample.Desktop.csproj", "{F2389463-DDB4-4317-B894-D4DF9FF6B763}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample.iOS", "samples\interop\NativeEmbedSample.iOS\NativeEmbedSample.iOS.csproj", "{28DB5AD1-656D-4619-BE0B-5B475E138DF8}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1539,30 +1531,6 @@ Global {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.Build.0 = Release|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -1995,79 +1963,6 @@ Global {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhone.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhone.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|Any CPU.Build.0 = Release|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhone.ActiveCfg = Release|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhone.Build.0 = Release|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D287579-7DB4-4415-A52A-46A5CD6FE30F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhone.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhone.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|Any CPU.Build.0 = Release|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.ActiveCfg = Release|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhone.Build.0 = Release|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F2389463-DDB4-4317-B894-D4DF9FF6B763}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhone.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhone.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|Any CPU.Build.0 = Release|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhone.ActiveCfg = Release|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhone.Build.0 = Release|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {28DB5AD1-656D-4619-BE0B-5B475E138DF8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2114,7 +2009,6 @@ Global {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C} {11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098} {BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098} @@ -2127,9 +2021,6 @@ Global {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} - {7D287579-7DB4-4415-A52A-46A5CD6FE30F} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} - {F2389463-DDB4-4317-B894-D4DF9FF6B763} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} - {28DB5AD1-656D-4619-BE0B-5B475E138DF8} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs b/samples/ControlCatalog.Android/EmbedSample.Android.cs similarity index 52% rename from samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs rename to samples/ControlCatalog.Android/EmbedSample.Android.cs index a4bd9b329b..250121fc53 100644 --- a/samples/interop/NativeEmbedSample/Android/EmbedSample.Android.cs +++ b/samples/ControlCatalog.Android/EmbedSample.Android.cs @@ -1,26 +1,27 @@ -#if __ANDROID__ || ANDROID +using System; using Avalonia.Platform; using Avalonia.Android; +using ControlCatalog.Pages; -namespace NativeEmbedSample; +namespace ControlCatalog.Android; -public partial class EmbedSample +public class EmbedSampleAndroid : INativeDemoControl { - private IPlatformHandle CreateAndroid(IPlatformHandle parent) + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) { var parentContext = (parent as AndroidViewControlHandle)?.View.Context - ?? Android.App.Application.Context; + ?? global::Android.App.Application.Context; - if (IsSecond) + if (isSecond) { - var webView = new Android.Webkit.WebView(parentContext); + var webView = new global::Android.Webkit.WebView(parentContext); webView.LoadUrl("https://www.android.com/"); return new AndroidViewControlHandle(webView); } else { - var button = new Android.Widget.Button(parentContext) { Text = "Hello world" }; + var button = new global::Android.Widget.Button(parentContext) { Text = "Hello world" }; var clickCount = 0; button.Click += (sender, args) => { @@ -31,10 +32,4 @@ public partial class EmbedSample return new AndroidViewControlHandle(button); } } - - private void DestroyAndroid(IPlatformHandle control) - { - base.DestroyNativeControlCore(control); - } } -#endif diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs index 44290d9816..33ca511340 100644 --- a/samples/ControlCatalog.Android/MainActivity.cs +++ b/samples/ControlCatalog.Android/MainActivity.cs @@ -10,7 +10,11 @@ namespace ControlCatalog.Android { protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) { - return base.CustomizeAppBuilder(builder); + return base.CustomizeAppBuilder(builder) + .AfterSetup(_ => + { + Pages.EmbedSample.Implementation = new EmbedSampleAndroid(); + }); } } } diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 2b45ac1508..2db150ec85 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -4,6 +4,7 @@ WinExe net6.0 true + true @@ -12,6 +13,16 @@ 7.0.0-* + + + + + + + PreserveNewest + + + @@ -20,6 +31,8 @@ + + diff --git a/samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs similarity index 63% rename from samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs index 24c9deca36..521d3674eb 100644 --- a/samples/interop/NativeEmbedSample/Gtk/EmbedSample.Gtk.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs @@ -1,24 +1,26 @@ -#if DESKTOP using System.IO; using System.Diagnostics; using Avalonia.Platform; +using Avalonia.Controls.Platform; +using System; +using ControlCatalog.Pages; -namespace NativeEmbedSample; +namespace ControlCatalog.NetCore; -public partial class EmbedSample +public class EmbedSampleGtk : INativeDemoControl { private Process _mplayer; - IPlatformHandle CreateLinux(IPlatformHandle parent) + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) { - if (IsSecond) + if (isSecond) { var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle); if (chooser != null) return chooser; } - var control = base.CreateNativeControlCore(parent); + var control = createDefault(); var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, "..", "nodes.mp4")); @@ -30,12 +32,4 @@ public partial class EmbedSample }); return control; } - - void DestroyLinux(IPlatformHandle handle) - { - _mplayer?.Kill(); - _mplayer = null; - base.DestroyNativeControlCore(handle); - } } -#endif diff --git a/samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs similarity index 91% rename from samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs index 567bc25acb..456f77a44d 100644 --- a/samples/interop/NativeEmbedSample/Gtk/GtkHelper.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs @@ -1,15 +1,12 @@ -#if DESKTOP - using System; using System.Threading.Tasks; using Avalonia.Controls.Platform; -using Avalonia.Platform; using Avalonia.Platform.Interop; using Avalonia.X11.NativeDialogs; using static Avalonia.X11.NativeDialogs.Gtk; using static Avalonia.X11.NativeDialogs.Glib; -namespace NativeEmbedSample; +namespace ControlCatalog.NetCore; internal class GtkHelper { @@ -39,7 +36,7 @@ internal class GtkHelper } - public static IPlatformHandle CreateGtkFileChooser(IntPtr parentXid) + public static INativeControlHostDestroyableControlHandle CreateGtkFileChooser(IntPtr parentXid) { if (s_gtkTask == null) s_gtkTask = StartGtk(); @@ -59,4 +56,3 @@ internal class GtkHelper }).Result; } } -#endif diff --git a/samples/interop/NativeEmbedSample/Gtk/nodes-license.md b/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md similarity index 100% rename from samples/interop/NativeEmbedSample/Gtk/nodes-license.md rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md diff --git a/samples/interop/NativeEmbedSample/Gtk/nodes.mp4 b/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 similarity index 100% rename from samples/interop/NativeEmbedSample/Gtk/nodes.mp4 rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 diff --git a/samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs b/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs similarity index 60% rename from samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs rename to samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs index 911a874c27..7967c9c073 100644 --- a/samples/interop/NativeEmbedSample/Mac/EmbedSample.Mac.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs @@ -1,14 +1,18 @@ -#if DESKTOP +using System; + using Avalonia.Platform; using Avalonia.Threading; + +using ControlCatalog.Pages; + using MonoMac.Foundation; using MonoMac.WebKit; -namespace NativeEmbedSample; +namespace ControlCatalog.NetCore; -public partial class EmbedSample +public class EmbedSampleMac : INativeDemoControl { - IPlatformHandle CreateOSX(IPlatformHandle parent) + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) { // Note: We are using MonoMac for example purposes // It shouldn't be used in production apps @@ -18,15 +22,8 @@ public partial class EmbedSample Dispatcher.UIThread.Post(() => { webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl( - IsSecond ? "https://bing.com": "https://google.com/"))); + isSecond ? "https://bing.com" : "https://google.com/"))); }); return new MacOSViewHandle(webView); - - } - - void DestroyOSX(IPlatformHandle handle) - { - ((MacOSViewHandle)handle).Dispose(); } } -#endif diff --git a/samples/interop/NativeEmbedSample/Mac/MacHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs similarity index 76% rename from samples/interop/NativeEmbedSample/Mac/MacHelper.cs rename to samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs index d72ef5479c..5b3bc9abf1 100644 --- a/samples/interop/NativeEmbedSample/Mac/MacHelper.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs @@ -1,9 +1,9 @@ -#if DESKTOP using System; -using Avalonia.Platform; + +using Avalonia.Controls.Platform; using MonoMac.AppKit; -namespace NativeEmbedSample; +namespace ControlCatalog.NetCore; internal class MacHelper { @@ -18,7 +18,7 @@ internal class MacHelper } } -internal class MacOSViewHandle : IPlatformHandle, IDisposable +internal class MacOSViewHandle : INativeControlHostDestroyableControlHandle { private NSView _view; @@ -30,10 +30,9 @@ internal class MacOSViewHandle : IPlatformHandle, IDisposable public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; public string HandleDescriptor => "NSView"; - public void Dispose() + public void Destroy() { _view.Dispose(); _view = null; } } -#endif diff --git a/samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs similarity index 62% rename from samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs rename to samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs index 2e14e7d766..77982db0ca 100644 --- a/samples/interop/NativeEmbedSample/Win/EmbedSample.Win.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs @@ -1,11 +1,14 @@ -#if DESKTOP using System; using System.Text; + +using Avalonia.Controls.Platform; using Avalonia.Platform; -namespace NativeEmbedSample; +using ControlCatalog.Pages; + +namespace ControlCatalog.NetCore; -public partial class EmbedSample +public class EmbedSampleWin : INativeDemoControl { private const string RichText = @"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}} @@ -14,7 +17,7 @@ public partial class EmbedSample \pard\sa200\sl276\slmult1\f0\fs22\lang9 I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par }"; - IPlatformHandle CreateWin32(IPlatformHandle parent) + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) { WinApi.LoadLibrary("Msftedit.dll"); var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W", @@ -22,16 +25,21 @@ public partial class EmbedSample 0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle, IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero); var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 }; - var text = RichText.Replace("", IsSecond ? "\\qr " : ""); + var text = RichText.Replace("", isSecond ? "\\qr " : ""); var bytes = Encoding.UTF8.GetBytes(text); WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes); - return new PlatformHandle(handle, "HWND"); + return new Win32WindowControlHandle(handle, "HWND"); + } +} +internal class Win32WindowControlHandle : PlatformHandle, INativeControlHostDestroyableControlHandle +{ + public Win32WindowControlHandle(IntPtr handle, string descriptor) : base(handle, descriptor) + { } - void DestroyWin32(IPlatformHandle handle) + public void Destroy() { - WinApi.DestroyWindow(handle.Handle); + _ = WinApi.DestroyWindow(Handle); } } -#endif diff --git a/samples/interop/NativeEmbedSample/Win/WinApi.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs similarity index 98% rename from samples/interop/NativeEmbedSample/Win/WinApi.cs rename to samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs index 5ec6e6bbeb..47d368f7a4 100644 --- a/samples/interop/NativeEmbedSample/Win/WinApi.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs @@ -1,8 +1,7 @@ -#if DESKTOP using System; using System.Runtime.InteropServices; -namespace NativeEmbedSample; +namespace ControlCatalog.NetCore; internal unsafe class WinApi { @@ -72,4 +71,3 @@ internal unsafe class WinApi [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); } -#endif diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 4464413e63..fd080cfc5b 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -7,11 +7,12 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Dialogs; using Avalonia.Headless; using Avalonia.LogicalTree; using Avalonia.Threading; +using ControlCatalog.Pages; + namespace ControlCatalog.NetCore { static class Program @@ -123,6 +124,11 @@ namespace ControlCatalog.NetCore { StartupScreenIndex = 1, }); + + EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin() + : OperatingSystem.IsMacOS() ? new EmbedSampleMac() + : OperatingSystem.IsLinux() ? new EmbedSampleGtk() + : null; }) .LogToTrace(); diff --git a/samples/ControlCatalog.iOS/AppDelegate.cs b/samples/ControlCatalog.iOS/AppDelegate.cs index f1c2241003..f8caffed14 100644 --- a/samples/ControlCatalog.iOS/AppDelegate.cs +++ b/samples/ControlCatalog.iOS/AppDelegate.cs @@ -13,6 +13,13 @@ namespace ControlCatalog [Register("AppDelegate")] public partial class AppDelegate : AvaloniaAppDelegate { - + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .AfterSetup(_ => + { + Pages.EmbedSample.Implementation = new EmbedSampleIOS(); + }); + } } } diff --git a/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs similarity index 75% rename from samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs rename to samples/ControlCatalog.iOS/EmbedSample.iOS.cs index c5996e7deb..ad86d2b578 100644 --- a/samples/interop/NativeEmbedSample/iOS/EmbedSample.iOS.cs +++ b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs @@ -1,18 +1,19 @@ -#if IOS +using System; using Avalonia.Platform; using CoreGraphics; using Foundation; using UIKit; using WebKit; using Avalonia.iOS; +using ControlCatalog.Pages; -namespace NativeEmbedSample; +namespace ControlCatalog; -public partial class EmbedSample +public class EmbedSampleIOS : INativeDemoControl { - private IPlatformHandle CreateIOS(IPlatformHandle parent) + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) { - if (IsSecond) + if (isSecond) { var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration()); webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/"))); @@ -34,10 +35,4 @@ public partial class EmbedSample return new UIViewControlHandle(button); } } - - private void DestroyIOS(IPlatformHandle control) - { - base.DestroyNativeControlCore(control); - } } -#endif diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 7cbd8a3f9c..31349b4197 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -13,6 +13,9 @@ + + + @@ -29,5 +32,17 @@ + + + MSBuild:Compile + + + + + + %(Filename) + + + diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 59d724db69..d8dc3bad2d 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -2,8 +2,8 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:ControlSamples;assembly=ControlSamples" - xmlns:pages="clr-namespace:ControlCatalog.Pages" - xmlns:models="clr-namespace:ControlCatalog.Models"> + xmlns:models="clr-namespace:ControlCatalog.Models" + xmlns:pages="clr-namespace:ControlCatalog.Pages"> - + @@ -46,7 +50,7 @@ - + diff --git a/samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs b/samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs new file mode 100644 index 0000000000..14310500ab --- /dev/null +++ b/samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using Avalonia.Platform; +using Avalonia.Interactivity; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Markup.Xaml; +using Avalonia; + +namespace ControlCatalog.Pages +{ + public class NativeEmbedPage : UserControl + { + public NativeEmbedPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public async void ShowPopupDelay(object sender, RoutedEventArgs args) + { + await Task.Delay(3000); + ShowPopup(sender, args); + } + + public void ShowPopup(object sender, RoutedEventArgs args) + { + new ContextMenu() + { + Items = new List + { + new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } + } + }.Open((Control)sender); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == BoundsProperty) + { + var isMobile = change.GetNewValue().Width < 1200; + this.Find("FirstPanel")!.Classes.Set("mobile", isMobile); + this.Find("SecondPanel")!.Classes.Set("mobile", isMobile); + } + } + } + + public class EmbedSample : NativeControlHost + { + public static INativeDemoControl? Implementation { get; set; } + + static EmbedSample() + { + + } + + public bool IsSecond { get; set; } + + protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) + { + return Implementation?.CreateControl(IsSecond, parent, () => base.CreateNativeControlCore(parent)) + ?? base.CreateNativeControlCore(parent); + } + + protected override void DestroyNativeControlCore(IPlatformHandle control) + { + base.DestroyNativeControlCore(control); + } + } + + public interface INativeDemoControl + { + /// Used to specify which control should be displayed as a demo + IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault); + } +} diff --git a/samples/interop/NativeEmbedSample.Android/MainActivity.cs b/samples/interop/NativeEmbedSample.Android/MainActivity.cs deleted file mode 100644 index 5d2e06a330..0000000000 --- a/samples/interop/NativeEmbedSample.Android/MainActivity.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Android.App; -using Android.Content.PM; - -using Avalonia; -using Avalonia.Android; - -namespace NativeEmbedSample.Android; - -[Activity(Label = "NativeEmbedSample", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] -public class MainActivity : AvaloniaActivity -{ -} diff --git a/samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj b/samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj deleted file mode 100644 index adfd74b9d5..0000000000 --- a/samples/interop/NativeEmbedSample.Android/NativeEmbedSample.Android.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - net6.0-android - 21 - Exe - enable - com.Avalonia.NativeEmbedSample - 1 - 1.0 - apk - true - - - - - - - Resources\drawable\Icon.png - - - - - True - True - True - True - - - - False - False - - - - True - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml b/samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml deleted file mode 100644 index aa570ec504..0000000000 --- a/samples/interop/NativeEmbedSample.Android/Properties/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt b/samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt deleted file mode 100644 index c2bca974c4..0000000000 --- a/samples/interop/NativeEmbedSample.Android/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml b/samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml deleted file mode 100644 index 2e920b4b3b..0000000000 --- a/samples/interop/NativeEmbedSample.Android/Resources/drawable/splash_screen.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml b/samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml deleted file mode 100644 index 59279d5d32..0000000000 --- a/samples/interop/NativeEmbedSample.Android/Resources/values/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - diff --git a/samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml b/samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml deleted file mode 100644 index 2759d2904a..0000000000 --- a/samples/interop/NativeEmbedSample.Android/Resources/values/styles.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/samples/interop/NativeEmbedSample.Android/SplashActivity.cs b/samples/interop/NativeEmbedSample.Android/SplashActivity.cs deleted file mode 100644 index 78c555f5c5..0000000000 --- a/samples/interop/NativeEmbedSample.Android/SplashActivity.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Android.App; -using Android.Content; -using Android.OS; - -namespace NativeEmbedSample.Android; - -[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] -public class SplashActivity : Activity -{ - protected override void OnResume() - { - base.OnResume(); - - StartActivity(new Intent(Application.Context, typeof(MainActivity))); - } -} diff --git a/samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj b/samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj deleted file mode 100644 index 1ec852ab6d..0000000000 --- a/samples/interop/NativeEmbedSample.Desktop/NativeEmbedSample.Desktop.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net6.0 - - - - - - - - - - diff --git a/samples/interop/NativeEmbedSample.Desktop/Program.cs b/samples/interop/NativeEmbedSample.Desktop/Program.cs deleted file mode 100644 index 01684d0301..0000000000 --- a/samples/interop/NativeEmbedSample.Desktop/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia; -using NativeEmbedSample; - -namespace NativeEmbedSample.Desktop; - -public class Program -{ - static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .With(new AvaloniaNativePlatformOptions() - { - }) - .UsePlatformDetect(); - -} diff --git a/samples/interop/NativeEmbedSample.iOS/AppDelegate.cs b/samples/interop/NativeEmbedSample.iOS/AppDelegate.cs deleted file mode 100644 index 9ac8ebab2e..0000000000 --- a/samples/interop/NativeEmbedSample.iOS/AppDelegate.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Avalonia.iOS; - -namespace NativeEmbedSample.iOS; - -[Register("AppDelegate")] -public partial class AppDelegate : AvaloniaAppDelegate -{ - -} diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 98f4d035c8..0000000000 --- a/samples/interop/NativeEmbedSample.iOS/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/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png deleted file mode 100644 index 9174c989a9c8b8a5ca133228f4ed7c173fffd2ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70429 zcmeFZRajh2(>6K-gA?2#xVsaa1b27W;7)KDAh-sH;O-FI-8I483GVKDp7(kG!~bkw zTfeh4Yt`zm?yj!7tNLCOuB0IO0g(U^004ZDmJ(9|06>sS5C9$)006=h5Mo1q0bNui zzW}Nxi4Fk(5rDMVXEhJtNhZRo`he%fekan;Fv2QYQhHiMczDY%oYp3J%F4>N>72R= z-1^hp(p?r-UEFIwQ#s`me58MJTFp?GwuKG)#v+ZzK-FH8BL)tmoPXOmAD@dn_injo z;9~ZW=&g}nu>%*c^PS(>S7P^`Yp6@mAKNYhvFQ?IZ zi&YdXCD1!Y%<}q~#4^yR->Fltpbnn-%2JiIG3t^+AHaca^k8>gq4td;ce2&ZK3`Wu z-@OQmlZ!_ehFK={mFYDvP|Il}9Fdj$;!a;cuSQ2f4XjeSoA(xsq%rn{xEU|1UY)#b z-%(Ko@V~ej^^(hMrLJ7~>w7vsYU>8me1F?9A1F({_=w6Vi?M2{Wy1hQLQ%tz|Iqcg zMA;J^+|UTsyeUHUM@6*@C>=sB9XH{rE=L1M8 z7PfuS7qYYBq}iK9`NM6aBl_EFY>hP^*NxM@Jb*o`jbNWwo7+Y^Azj=x-o(a-i$a ze;O4Mz^r_s?M0IuJa?Swm$A{J3E-WOZOVLGT>X%1?z=n9mU~aQhJ4LpmeKHhTM=0{ zXG2*%db`RXqBGOp+p42T$WF`lllEMwvRHHIiHcb*6TU?Q{L8&)|3TcXK|*k%!8VU* zxIW9k>h*17x^ej=I&)tKco*(k7kgwK?NwGjJEpHcm+kgm^g8QjdQ0eb&E~|W|A8{@ zlU*45aY@yDNpUN^-z+(*es*EH;(3>62hLv&U@e$7Kti2yDIfP6ks+f0le*z^?^WXc zl^4@^A(R=6a$q9%v52NARg-u-&SXc?B}VnnWcx&Ivu|SR>x}H&2EfLX^Wi)q-)R9C zg@@E$TuG7@8lPLUy*bP>;p4a0w<9~Z>S8xGhH^aW>`O$})3=n~UFp;HUH&YG)cO5M zp~pDy>CYz%t9X)$L7q~95xBMWF}GsYdfQ&PT-6`CZeb>{wk7@ZX9)-9nzTajtQ{TOR}6qN$^-Dxk#ZC~{YS1xgAw z%oPibvW@543B5CO%uj2~Lyu8Lvw-kRKa<}O8FN|8ue<3Ib%mt>s5#HXc zb9xq7{V>_XrE;$jGXY(7LM2iZh4>y0Oys7P`F*j>LAFmHU4S%oWH<#jrW$EXOCY4y zzm-+!+G`0hhDh`Q@YkBR`uo^rS{!Nz=|$Auy$pX%^Cq}F_QsSMPR}h1Gp2^slIQ-w zcJRA~YT!kduH(=E78uRMz{6##J(OG+yF6NF_SFbQurgp!1&zKwZ}96-rK=F-V{iVI z9i&Gn#W;M=@N>1S*P&r3i!~8ZY@Hb=M4(xD-mTJj~t2F;dUUn@DNwrur9Q=J1VC_vs zKE39ws@^f-O^Dw(_~J5n-B{gE@>Z&>03Vws1(7s(w5%~yy{ZzfcLT9NFS;VAohFv{ z_)4Q>_npTrG zxA%Ngx|QXn0&DF1fyCcL{A9NPTdT{)u%oU z)On3UmJrZJp~}-pc_PVOp|4_sKR3_6&`v(j<%E#@9+7n5kDY2hy|NmOq9NsZ2GcUG zy}Erm>q%xeVppy6_k=JLahTtphNe9Q>PqP-Sd@Fell{V)vl;6&wH ztFSTwK~19|l`$Y;Rkr+^Rys@B zxbh09d<{1aT_Kk#A)18TM@*>zBPn*79Yw*!^|nII zVe@8|0~$4<4l7yYST@@yFx$~p#LDzZzh{;KD9*Ivo-s)ZL5~QJ9~R^z5G^Kr`AG`-JSJOBvu;OIOvb1W zpJjPw=>jrSGD-o@vJ>AhDk$dU%bONjtoNyC=)s(?RUi8t(vH6mLl8^5pf9#Ocf*}( zxP?H>Ew<5aCQ`JhG=nHEW6B)1(b!u|z3UHIK4vZEazki+zbEg7=Gz5@6JP5&2OFmD z3tht+#KaiZY+vg%g&VmY9bI6$P6ouyh#B8I*a+{YGvQWL0GK~1N@H7=i`Ugc5RCv; zC7@A<^OzpY5@XnbXp(PUR|X}};VCI-zphvJr&jxxpycW%rLFB)Bd+N0%^=Dyd^XX2 zwR_2~>5NS-*MBgXm`dti40PVb7d~AW@PXSuHWG>*%4!_>bth;C;Za-1~RSp26SG#yskb23lTa z_s-P-WyC1e8XIE0Rn|rK4L6BCZ)2W<9rxaxL3ufXkNjoHEOKWB_YmJKtoLTE;&~im zSl`qcYVd*RZ@+rq>|1pDLW;ytOudi(hjnJ_y^$k<1;h(QhQTV+gpA={ga|M8 z{4CqjIOneql!=@^$z|K+{`WllJid%6h-if+^r;2@`B~#7G`fEmAn32p*8Q6+S9`HH zg94*AchlJNl-(X1%rkwj3-@K=+L|yYGfo3wEo*KE z5-3>6qJ#dQ>5A}`*qy)+f~}CBe#5Pqse5!GH2=-+(uSYN1Kg9 z3+3uC=g(!OJ1=nKlO&uPKskP1Wh4$ScNB5K*CI^{)UHQu)!T_xBPC)5h1mp#Y@e0_ z{*&QC{WBg?xdOHG+lJs$>P&wVWkvhh1Qyx2Jwn;H@89u}F1%tGd|b0OD>k$cRe>>t zsfLQ0i>k~+s21O&DDUntZIv`|*zsJT>d=JfCra=?JHHq?^-Gz|5`IZUZrtF}0On;> zGKvIGz#pBGhIFupXvZ;{C0i-r+sZLn_yDwNXMWOrR7N40Jv=3q=wO%7#?bEMjMd$6 zupeS`QD-7`efO3u9--r`9N-{CJ(_hv?t7x^Wt1*KL*$Wv{wTrFohJFQ2u$gjXs#K9 z8m)Fd$6S`Z%~4GJG2McI=lX&tN&|pEcTB)chGK2E>OgX5tvSW6hW)(1A5-!+e&Rs< z7IKM5dT6da<3>7PhuqPSX}&knC!K6QRtR-KTiW!++Fz2_##qsxtCE$0w9ic4Q=Wfh z?&_}!(Cn}L-jmH!SzzhQ2bX!j7V34-EGp(~d5I^ZI4k!AX~LK<)QiYKxL&0oxx3+U}GjQ|~>Ib|1vU zIhtyWchd>ApRl>K=O9QPYB(IoxRpSJBJoK_KDvJb2h7u)sR3s+qBJVX#WrY99MjQLA~C z0gR=vFC7+$H`jv+Tg+hc_;`eWq~EA~jM}>^bDf2aO)3)}jYy>KlxJ{AP`L8!wHRNQ zyxE7X%zmR#et%wb3)j(S{<;!@NQ&fXEBn&mtxhYbpZQNxA<;2C7p>;PW<8=Uf1y?U zF0fUgwIv6twTQ&iUMyLt_7Wiw46vf@a`&^^qnJ@{@aWi+K5kOS7QvAz#3+F26XWyj zx|>V>lTMvOua!?z2?1kWR_>&QJ-w}nMhTvB(2nPv(|TfYHb>^#6R7O~ zG!u8+l0MQm-a9Xvyug=f*t+I(?}d{3RHY5X&GH+WLqH;hd7T|T!L=Cnnf^4Lag-b) zU~KhC75L`74NpV#Wl3-D>@!voxc!`06-Y_@D3i1R74a#8PsKH&ru5Khn)Tx#K1mKv z)M|svs{Y8==lP<9!4{@EZ?(~FTNoueMkf@iO*Kr%k_Wv%R3b3HsSZ4R=)pUPv)I{) zIkLYmAJhOt*d+`?*di%8JC~(^7zQOxhye5Fp&eBqk!DU6L_j|A-Gm_lhY*YaM4F`Aq9UOHSdma-C$h~?kOp=T#eCoo(7FK! zzbTkOL^NO^WUOJRz>knNKYH~CgLfbe#4w;;lI4g3p#N`D>i2f@%VgO5K1&7qd!17; zZIaC7a7Iebp0oCg*|OASXF}|V?DyW?vHcznwcC)j=Ye2Urv2OnBgW{@E8`;sbZA^r z09ewfn86NocgD@0g-uPuhSfQ$W&2bW?=%;A$WZ0Mw|UnW3;B8emBq!9w$1kOeqRb4 z;{cgpIOT))#hE24iS?GaWJ413H7v9DaLy{CL-cNFsqno8oC@6cmaU0I6^b-kC`fLl zfNWog${(RR>x(Rcm5X;TxhABT_%q$~JEc@QNJz-G=Ha;XYeAaX)^snxvdjlkITBOl zK<%QI*gKHVgzI0{#-$x%@e)G@OMJ+wQ-n5%P{t=y3YDhGA?GLd6L-WHv$3{9pT^vg zQUIWm^47^Hc75T@Gm`@w_wIr(0T`^hmwye2-$3nhaOSD3yiNk()Ny+s*R<5OIzbD| zz&-iRxBD2Juf%Rz>n2*+!my+v5g{8-fpO<)ME2;ZULJMLd%ins7|S*FcwqR=K8I|U z^mGr^h;FmfQ|BSzpKla>-=nd<11-gh* zBMaS_H{@47+)6QzyQ~x1waMT-BJzb;t=DC<@7l3M=wrIhbNE)%_$k%rmuzRUD4&BX zA=jaGbCSqX{dhcTf%?V^#0%~OIv1RyF{>GF#hldbwUZrU zgq8LDml19w)Jtsez#?nhj0b;wCAsWCuKe?IW4h<1LK3bKj|&Qw?&YithzQT-khn70g`iXQL?D3W7;4|nNh}K+k_aD_eC5DrE$4o~zsrQ_2 z_Z-gHmWMDxMGHxax{<;WkAaJK7YiEm#p~`xpY|>S8d6L%{V#e7O$OF)KJ+l16H^rt zyNfa6TSNQ)Eln8^UAdbxX#A_U@LXF&iU32G0gQXT%XFEV{+@b;Aawox^R_N-l=A3H zuKdct*Q|{ktS0XGvpzO*OJi9S+w?r$NgaFU4BSz`%S7*oZJOhzww#n8c5XQS^@=}> zmlF5By7##?xk0z2=baNp~bu{@k#c=KillS7E>T-P>z12m&h?*}29#i+PupL~0PW684Oa;>_kMc)Jdut1>Gu1U`r^ADf7&zwsEWC8;h+H+$F&;j2AHE!FUD@Y(2Nw<^?p%kBgu4+@OY;a zE!U=bI!-|Uz4l6r-b@7L?Es)uB^fLm%gpS-(r!cH1L=a{p|shp&xVQz8tI1G9yp$1;d`~1DMfc88u9f zqf)eq+(Ml@bNyn#;RJ^xOD_{AZ+7O-p^>~kUJwG#JV0ttTacFTsqS{GI$8Su^RGY8 z)0g&TdU~(NYigU65n*+oCE{;f`$j+d7s!=`A_P(6_6>K!%!&F-V;<<)E zO7PL;IfDWAdyS9m?d*Z!N8I}Lc0bkLGMp(jn_wLK6{ad*`i&SaI|`!%?+|sa<56Atp_DE>Fkd?7B{Ngq9KPXun>b;A z?84IZkAywVXk2LB69eI#wsPmpvh5ctpBz4V&f6FrNcD4Abh4%n;^yF|((A;c+IAlK zIQv-a1b-VBoPTMGrE14ITOWXi|D$hkUP4ChBpU!$Ac_3)O+mZ|8eUmb_csHJE((e} zLX*E&$46wQXaEHW&T024pFNlUK>{f0 z421{Y9Y-0ALkjnKR_gER<-OX8Fog@_9ypyQqBAKnnMO#3TAvbZ(-~hn`Rf-%hb7!Z z8ByzCm<(nE(EV|9>gq|1uouAhdYTc90ZPT1Q&EK=sKV+%M(Y0oZ9?@4zzLj}_?lXi zEakP2d|fzHn~njSBSSvWm4pr@l$lBXrzu5&V?2dkH4U#CP)c$7GpDoz=IQUzRGRJW zo+XkbH$?L#$I72&dP9bYjk)X%?uPngj9s)Fm)@)Q3BCwTp+TNGGP(bg8Tf?$x60*=QExGIKjQJi@Z8E8;@w&zyxMbSk3S!nvg`I1x;l zf}ew?f()~jUdyM^d~6rDwjGKym4yMCs$^iG6pZPsm|6M8?5f^7wWcXLty_Jh8&4Jq z17kou<|Y*Z9L>!;+0S zU%EQtLHH8P3KC3crR>P7xgwk*4cflQuutxqnqu(wG*l2JWf&=6E>`wKSND>cfsgd8 zFMq$fC6M{CK)fpCXv$Bh!!y*<#3CD|SIbGZ^3^n$LP-E>96D@>j(s+aALrtXM4B!W zuvf(lIf+kn#bEHD_W;nTfo0DPd;7AXhMJ{^{gR6f)`)pNZGC}E-IvY&js`E1OjRfC zLhLh&sVZ59(l5n9z~5^A=08xcU%2R~W0{|InOi~?7It@^1|h+5@5e(_%Uk%5LL6gx zIHU?!V-o-;Jo`y8kR`Yz$+$=NZ&93zQ$ja@_UNtAt(xPcc$j&@vM_m`Gl4-*2N{~a zEW=p%p9GA--957LcxsH){5_!`TIu&?B5%|qgV7jc#7St2+r;1T>3d!Xm=64Ac&-*E zmMDkd;6=LZES1 zY7Qg(V2zOv)h4jti0f|hvHp$i(-MZ*-Hea_A*^oyFC7$Q5#-yGQ{zcbWH}9($H6k5 ziufT7V^#oqy73|lR9s<`dFbZiiZ%^eAu+NDe6C=oKJs($#jn@-b&O+Bp6hoYJelhq zQDZJjkLfE@2u!{@Bn|97sK%`--l+x>rZDp~++j{9?35^ijk}-pqCPw)?WMW}vec&p z(pA@**IkzQEc5r^wU^eiGA=eZ8Uc=K@ZFvTl* zDa*HFHU?N9fr;+wUQ>Ne(3CyhYQ%nLO@5Q5v|=lA6!-c#$%9^(JCFZvev5^Y>gfKkMxl*%N-xb1;;_|Jnycz z`})wqo8TyUdt>!lYERM^jS!e1A-EWKh+(c5}bvH`xYU^X=LUi;}3^ zi%oXDQ|;u9p$ts~Y;Ac&0$?{!(^pXnWauZZJcp1a56Z}In|e`&f7Vc>YaLb8b_ zTrI0n^>3(us=M&NE*HefO%YYD<(fRk6aM;8DJb;JXm1RAa6PyZ)ZExRAsS0uOBbIwq-3*T zHAgSX7w*S|gM}dpuiV|2(78sEDoqD;VV~toiBK5t)>%Vs%Al(5%{^bWCqsJ+t(xDk zMgu>+qamW|UfN_s>qVVDZWCOXeesH?28FlTT=Kkvy2w?GBBhX>^@R|ODsWfpEIvuT zy-t0*S6(?G-`iiaxn+Jk|1P50#0A@A0)WbAc=nI*!I}rGJ{;7pZiw127z{AYJuI5f z_XXD8`d@n8&ijwA9c5-VR7~@wyb4caG9D>wL0_!KKx-W7omsDB8j0)Mkv-j;HBp@H zEAqE;w=M1q>p!Nu!8Xyqn8#wdi{-?@lAarPSr3%oYkC2T*MH@#S86S2OpaSP$N6+T zBp^_jjwrGGUNG>fTsLQ^8c|NwM#XixPWeIrZV!FUv+k&fbFWy#z^>SORg6({C?%wN znx5O|ZpHRo3yv+FTvH#H7e)LE_=gcw+q;amsfg2=$2hn^9WCePtkhC2OSG=|TBpnG zBiAtfuF?&e7<_Os&pFx^MLaW+%H;i|vSIp5@7@RxLFrH-`-yvBqF0lNenOw$)t2)X z?RHHLp`xfv!#+>8a<*McJbZY(_Cje@)(-5QthrWALCd^h=VY_9T01!K15()nt7iRE zV@Aq)SASY^NkpRx8CNJwxmD>)Qsui>X2V-dyZx;N#dGLCJfCw}gLmdApjOA!gaR=y zV~NY~z5Cow#13qk1oo8e(&6~Ah8>yk)k*8J?0OciiK@~g@lia3j_%5?XhofS)+lwJ z^P-|#wlH0nOjg6*b+BB1|)pHi5*D2(gv3(r ziYD0Z;KSmE(J;OgZ1%Creum1f$(rm?)X1B5`-RlxkA*Ys=iW8|y;Q%lf*0f_43hj` z!XbxDok@#y5>M@e^|k|y(c;(6c)xFryJ%0pvN6&&JP& z6WpwdT9TU2a5lOuRX2Xm^3{9*mAS%uHS7H5hfJGw7wj$Lo%!M3fi2Zr?9RrrO#AdD zu8*`dT_Xn#6aS1-z;H2*jR4Osqrc+P>ny@)E zT73rfJF3OV%FMMHijE67w+fX-&X*pBt`$%8(&pmkcz+n6FCOa@hS8FIrN=IxyV9Lo z$yQOe;gSB6ws%))RZO*PD<*9u zOP)E83T+flPZ0Uz7LJ{8-}X$w{4Q(T;8hpZb#{$X{A==xYDzSh=0k>a{J8Hb#czI8 zk@?s@nK$jD^;?6lGcnhG>i(L!5x6zaQ9RPEsyT<6zxS-4c8l=6kL@Yyd(of2G$wfzC5A*@k8F*YCPLU+5mek{_Mz z!AF6(kEc+N-4CwA11e0!ifs4ufMJ>DzXZ36IxAY?=dBmW=D)I5JB7ckB9Z9f@Y~vT zJB5}<%gq*<_Id8PL5|l6#YW^{t3QD2S38lBWbVDDe_7YPL1+km74uy>W4lBF?@jfU zUg-ztg6G0Rge*puBVC&5I_6$>05fA>Je-Ppv4}pu_#Pqj)2A`Vj#z)4mWF$)yp4Cy zx6<(56+A7-!ZgDfG1;6$YC0EAUKf$LOV7MZCPVpfPL;FOOY8a^PnLfwi##rSoR;ix z$gEYFK?EtU{4-DfembkMxDBmo-IQz?m7dzV(alngJ~Mll9oV!!`B8$*P#hM_2H=oD zcAI2MvcKVoSWz4~?et=KP_8u0WIF12V!rD-XtytApX4xr;Kc7I>AFw<)HoNSXH=Gd z6|?h7IYrc9y&YKWk>kadJhz(bZDO%ACIaKy_3&{Lo!i09hL=#BMezOu0ns|U$H}qfuX$Md zpP)$tGK8djg?zDobDkZ`3BUdfCQJ-@&D%}RM|kF&M;9udLpOvNB^6jtfZ6-Lykc$i(zg9|YvesuxTJr0U`dcd;NJX;p zWm`YLLTwW499pY~`)2J#UFok*%3F3Z%wP>`p=48+^vZ%ARL(Y5J32Vm70d-V7uu3K z4uLT@_j!D}PCA|rfwpG$ibodab@z?m^zB`4{tBM_OYe)ge;{rA0X&;x*B6*Apl$an zmT@f1D8(>|u8ZA1UQ_}7t(Sv^CVZNvLS8pqQ^$W`Lj4JAbSvQtA)u5;m-|;-pP%8+ zvc`cXMoBuyDfy304(sI^Nf22@!Brv-b0d67#&%$hIVMsjQ>R<;3w5RG^h~Nx@p2Q$ z%z%SwQAUqo6>=u;Fl45ZSrWq14vgEJ6m|yFcd2blvxvDxI?#y_sQM+~nCZqoDIE#x z)+9XyrDP@54;zFG0YKIrkMX}+J|G?4eOWlWbSO*KpoUwkcvGGhXu?Q=y&unidFoFo zTW13}BzSLbvy~w?Y#-iy;aT1>l+6MCaO*b>yQHzS<8V$4`NZ7zmVVJ{9N3vK6JKeOI- z??Ey{JS+2r?Uazdc?v6SGhVqw$?0`WI^^Ah?Qp9II26fuPhp3}X-rvFZuo>=62jO2Q0CxV37^y*|Ppwgey zNB|5k!OdhCjh3{+1rlknhaFN_?)L{+r0F{y{ot>Zs>CUAvEKu&>(!r7z zc^S4^`;5nd#uC6M4>mu!m=w`7MhT(ORP}4c**bJsi!4FM;zmmDU#mI%B+zp(StFDt zeEC2&U@cb&9&$F{1X7xDOC@3sk~Y&p84?T5s%fn62Epaz$g~4sEb%3c7ZpFS5`&?d zs$&E{li?`Wl9THDXU3LVP^BOpngFosZ`!^tzyFdAHsK`{-#0Cr#NngrVFN^vF6i}% zVT!w!N|-JxqSC;M{4kWg2xkm|!QLvwvnx4}VQbi?5~s;2nmk0C1(l$8=rQZw`$|S{ z?_yx1ieNtf8vis$Swj4}f~lwxD>se^sUcX1r@G%#&Ldc|tA#Tgc3H&m8BozXc|j@< zH-WiN*DDDM%F!|cFi=S`UB^?ZVbX~@kV=6LIpY38w1CF&y)p_1Xt#z$k`HtMk_$DZ z!fr&BMYjklNIl;GL~WZ30K^?{^Vk@*Vr5zv6pn|O@2oHeprsNl;&A!`>7Y-Oi2D3G zj0$crQAw%d=FAjG`kRfC#Fzd3{d!8RXtW=0SOIjJ0g^(WvW$BY(?)l97kt-UrvKm< z=$%lq0q_s}fg8E9N!I3zQ=6LKRk7Ev`dI<^vNlG; zjb9y^4JR0DBhb17`$Jij_Mf6F=P@*>PB-xYcHb!hKzD@SvU^o$aYRtdkXrFFyfgsn z45J&+T+UA!3g(6^3ilTbFt`o!?Cc0-ge*rMQX`6v1CeerL!Py@iaNtvLg)pS6qG>t zW?2Y@;D4I>|Jq#9-hx8gwkdc)q>!(JL;z6qAP;DzTnVCouF=2{wuj@tERlbH0YGZ- zn}8A}3Y34PAw-i;|8hb8*Sn4YwGwo=|A>-8=p;n{(oi5TLR!a$2-DAoLI0`j038LVMZ#moD>fMM#)$p3xD{12Nc z3^kw?^k#l2aXB?+h@DreotVCU=t2Ue zfzb`DQDK6|mN3$kO!>5bCZ1H~yMEUv zAcYRQELu3zC(ajY%LGXbsJ$FXqj?CEgNFq#fs(+OERGOJ1YZ4};DiAM*V;O8(1ru+ z@`UFu-y2e zD{bh)^BdC(UK9%eYeU@tQupNT5fE0f826vo%PL(TX?7(pd=S*UpaQABGgN2xTL<{4 ze?B9F__Z&ajtquSnnE{uTCHtCgTjVfac!^x&YPg|PRsgKj}x?LwJ^j0TZqdu>q}DO zLWt`0&9Y=+TT;ZN_`^g>N(1-SQ<6WBLY-wDz!?SzaEA!C_XQdzqv81-BjuF_%hNL{ z!3aMVzqb@-Sdmi_>NrXe0F4n);3*fDG})X7DKms8k|5{;Mx?u%W9bA(dG$|1vxLBd8D zpx=%Q%DK2s#f2lfi$KWa^Cl^zo&^`Vtxng4lpkLF869WZiP_LZ3bb zKu}l97bB?_RmP4i2YAaq%77q#v#IoQTWa&A>?ez|WE?J;o`0ZL@5< z4CHff0R`-Wv|!>g@Y#;gwCe4e@LcXq2;TW@n?V7b@M;?H^><&>j0jkz^S^+J0rY{~ z0S?S-w4H6%3_GvOln~ta2ShIj?Ah&3T2R1%)=AH&K!bw%05MrkK;NDRsLJO+{Fkdc zT(rM{-uFNeYtSxYz!GjW4rc7fc%5`gHAcw39+-A7EBxsDEbzx*J4mSX3l$qYB`K*U z{L2<(8)VB1aD8SB{Ibaek(>olK{=-xs>(*H=#hU0KpmpTi9+ooGlqM!WTzVB6{x{O zgo2e^T7%8f3|j@HKR~sD3NU|nwTV`=2cRMx)-tO25P`|9bn7Y{8r>rh?invFin@qI zKk_$=uReAd&0on{S? zFP1DLt*JG;xkWT;pJ2zeb7OJ9qKL5FW;M^Ew%6*vOkN*%uqM`C{O6=GXvv{^EGt0; z(}lX1KHIim;{F^R)z{Klt48g7t-<)`!_K3f!R%=SCfcXQqT_F6h-7T0phdWDJZpE3 zr)eac4(pe~A6RQW3@uyvr%%^n?^##68@@alO-M^42zJ@Rrr@Ul8lby5IIoZLtstnJp zPd1JW3L+nzc!^w&Z)OIvq87oh zs_xkKW%*>e0sGzk?d!+wc0;CH3v+Qj$D~2wA^c=g%TQwHlXajW#KJ)i%rtD4^ zht|FD%iZG_g*b+7<;Qd*+48tH4`+y@%7FuWkqSNTB3>Re8u2IQpff)GxYv#6oGi=< zxKhS-?i>h>A))kReP!I4J4s{W9|+Ah*rC$IPMu!zxvKqTvK#lA{!jQ00tEIdVwLJd zA=K?heq8fA`Cc@d!)-8t0FP{DkgfaCf5GQh-ARgqSaHnLpu9v;&Ex;clj>J3AnvIz6y>G14+(*!5HEVSo);n#>?k{=W(TEwh; z9)9g@r}5l-Uk=jq3SD*9_2WwtCx?9|m}H{q_+S485b#y#Dn7NTZVf5M>Y_wm^lnto z$5r^!5I45GW55&m&&rF8+(u~4hAZ7_eb-NjUNFpXYk$bBQ$#>Y9_ct|TA{Sp`8BXK zSiYQ4`_wv;XIS@mD6zlFt9WvD=}r<^PoFtEgD#k9G9uSW7Kfv%Io$(v6j!Ai@ysdL zjmqjMsY!TMV;yZOxc~5x)X(|P68)cs?eUdX*>NB11{Vc@3tj!Jy@0d0Vb5q(V}^zW z9t$hJ#y?t>kTWhf>W+IjC%Ht2f1r71Fg@h;+!O(3#hE(|5YPs*z)2W^vhMB|f3pLful;0eTLKbn<@`sR%BC0Y8X~RkI}YSn zq}AR1SvsEPUeHPC-Bz(D*Tok%@z_@AaJ%u_1rFNLM~N4hEo8+yWA4^pa2 zwXvKdo){$jo?#DdR$mLk`80Ig9TusDc)C8o@!(WG1QaL;^Bh@T`cr2S2xE|Cl0y=r z#MXEOhLpz9MoetFV!<1Uz0Nt!(4g_hl3AEPOw5@9Td#AmHaVz({ZGkOh{Bwsf3oqOSP z0xD*KL(83B-?KFJ?X!tC7dI%g$LubXj8Dc&{yTeJyKht`6P;ChV-D@VdCh1u!2mU6%2(6@Ax$#o9yO!4|hJo(B6!ZQ_)QZ+EWV>g4@<#VyrXQ z%$=4qk=Wm-^$XF5o%--X8m}t09QHEzS5sbO&r?8<4i8+sSjlYjsW5v5x=YnT*@RNs zjeXE?`vXKoMBi#=%aThalNGvSi(=47@a+Azza9nCIR^fd8~cl~;t<@t5|BWDBhoF} zhFB5NkZj$g4;o{l?5?hb!-x7nD;wZJ*JJEW?)R?C8iR4(>qB!HMsOj6p&1PkSRs$z0SJs;kvNe1j{A2I;HePA{#p@#g8NOa=Ktl zw7d`3)6Q+Y9jBu;S@Wd*Sl(do8?PY|K(hY6ltwd5vhg(k(p}8(wm%W}YIeTX+s$yJ9eg?G%AUxKM6!;G~NrPI>R?SCO))UG7;5oD@om+&L4W;)LY5l^io zY6I*Jt#NHE^y6d^`Ute>bm_Eqy51z7&BkDG(&#ZEh&VRLJTT>#oKjkDc-Y@!nxC{u zlAgoidW}9e0~8f4*oA8J;Z@0RCJ#(5E`_0>B=DpS){a(%aDdN zb(4nB*K_z0L6e9_X}n|bMWyO%w5CT#}}8 zb#NTWf{-pW+37+Y-DP#ayGP><6brYYrg{0Xl$RzY_6Ry4;Y1{YAxCSc^EJDXmOyI% zw%~X9$FQ0`y?FeDM{y6DeK0qH40Hs++$GQh$+ChyyNoDZ2*b?N&R>h;Os|4;CU|}C zyK43IUM`%Ktxsuohl1pY{r%41FSGZvy&N&}M%qWl7z0MdRJ}MRz9_~KqKH6g6$KIh ziSUx+;7Kzy_o=V-JyJ_pia76VR(?6VK4#cCPYT!h?2zCJ)r!oQft&4`sO31&Jc8w)_mK}8MGH7Oha66Xw76$N-GpVrdGr98N~ zUe3!jy$vT{+y@X28hDle;>Uls0F_0*FQ+ANj0Jt4A?rpH;UnTuH2>4MW-^#iPX58; zZ(v*iJ8)^hZ|1x4_8^CXnt~|RwiP7g>G!BqjK)`_B1lQ@&Gf~h`Sb4Gq_RyTa68>W z{SsWnr3xueY zP^JH#Sd%NF$5^11A#>?v#TD0__nLBzF zHi`0UYw)@}CF*5uVToz7-TQ|n`>MA|fg`aQd1&LC@v8K8zUlax$sv%BAp#6-6ihH1 z{BWbn5*gZfHh`ccnd&9Cq=iE39+pzgv!Zo&c!FViZjhmE`k1UbgU)!$uFG7S!D`u%@-MLvwi%YOn|IEMZuCmi_&9o&3=C7ru9 z-AQ+UTWx##)5$?;0Abihiz4;+;_P%hH{Z0ZRE`Q<;Gm(s;lvg<1mZT`x+^_33c~f@ zz!{95oSqv=yjV(!#00;6t8qQ6MrO(MW?fu(=WuX1T~TVra@bu0L?I?~exuQwPBr<1 zl&zM9VzjmO6##%Eg)Z@=me#Zqx-oY@@CT7Jd%lkh;bCt+k8y`PR4kgb-xnW&h9?Z< zs_i|ds&T>_q0M*9xy!VWI1>1#Oo_vSY1`2e;JOLbJ5|v#!0uY94^)KjFq$#AqHs4H zKh}B#-gaBKwkI{+|1P7A*6v@vf>|c@DePAg9hOk(^8mtTJ1kAreipE6Z$hPnaNRU^ zcl2XnD}P~rw$ZG-R%*KX4U#JPB2Ahys+}E^e6`uY8~BYvo(XP){KZTLziZex9chea zx6|WoMcj_~a_B@c1I@nC+)7kbem$Spmp@fFz!pM?_p$^GhK~JPeVI{D4`ybF_E$*Q z+UX+2qH*5m_j2;7^o9p7NqcCWF@|Lx=yOBnr7xO%@4%{0b-RZogTWUu@SfHiE-L8flJV%P}{HYAml)-TmHJIWJ?=p;XO} zm+kIt$|Lv9R<&`P(E|TBZmvrkH-DU#YeWF@`j&uFh$c@n($J4a?r&~ zwK74HJXRTwI)d7$kjgwoqelM~){Z2lIg*n0H*RY(5npu+yX)Az^rFgzA5r;D$bap~ zweBBqPa$vob8h&n2Zz1fbIA~=m@RpC*WyocQS>{wj^P^N{Yd}vR2rZaCj(TA_LbA| zdxRzaXqRR%jIl%}H8r-scjSnaEA9Vi`J1pp3^3^u!m|@i-SLWQo1Y^T0Z;G8?%`ge za)=h^CR#%%Nb|GjGq-0hmwtbsGM73VeHS-<8UuuUmwW13jI;6geil72d8GbUxTYMo zG*aMS@I$!3ZKcaBP&Z()!BZTANRQjU&JMT5n8IUy<|TwYg$T&31@WdjOIlHj3I_r_ zbyg66F3v%mtuGcGodwb+-#->SIq3}15IQj9K%5pW;@V%9H+#j?3|ZBB7uV5W52OIO zW9xNkci=w=cLjr;y2FcZSuUy=Hv3Xw; zSFGPXE?EZf_P}tnT-SfO+)yu8o@JjS{73-He`?Mwu4Tuz?kIiKTd;HZ46_{~^b^hpPH`geXHow!x6?r00x zW=S@8nk(7NC5WQ9odlaK8qllY8)T{4dpn4&^>GY7XXKpt65G=IN;hD?q-QYA2 zuAh*5xZQ{9pZ>mx z)xJol#`a%bGTjwkVyd*f-0uF`ZpaziBVO<%0e$;Y*^VZ|7l&pD+QGn;K;#pdyhBi$zCP}VM zsi=w~zKr1JR;G&cn3=^*&grott=i- zd2&y2cqUEN&Ea~>S|CZq%1JRn{A#@61k=XH^M_D`VKU4vHEcMSCk8(4vk}gvaKtWh z2Bg6C1tLr2BurA!>i*BXHr_cT5wBi7Rh9kD`Nw%;^fs%pI^Q|EunWX$!BdqJH()zmT^Q!?ngV@-DFQ~LOA zfyqGh^v=V@T3?nwLho?;%_y0T+VGSjHpIe-sOH3BYHcbSZl1sq)`xgpr#H^{$?2wg z#WAqUFz?O~gWVl=6?GNgkr2v`6Nkk8paqikfp0xBa&Tdn(sTJK;?JNfz0jxF%n&*> zyP-O%;;9(C)Lo9$-&BnrR6dp-xDbHyGd*4I#sF_(6&)F-Zj=wirM79L%E{juf9eK> zW*|PCY6#sh%G4EU#HEtH(*&qluWeA@aV$wpoF|ZUk9Pc!rv%HCl4^0uxq*}&>Bbu!%SilV{% zd3Uu+^MjaYwQI`kbW7bqR$yHCv=$AV#ZS%8<2dk*RK`J%!wUU%9JOcrofW9x9r()C0!MPT!feh9daXZZmg1Dh$C z&%rE);2yJEg>wqf@hA|}Vv*s|umgHVccdVCF9#A#dJi7tjUDcg10jIo!wNRO`a$H|b#BEz<*_;^>@%9^@ zJhN6B))bQY;dD1{;QJg8`T?Duhg}W1U$^5!0Zm+*s(u#WXz5& z2QF13)w#aUqu=QNv-R>f+V=`>+vBA&urM_6x@T$EA7>FiixNkJrZ6c zXq%ty3_z{x6V0&1!`qk53)afI@bBlI&Ir7=&4&%0SM?1BnqEE!(}T=Kx0D;a{*`>v zvN<;+R33e>!zqM1Pg5N(CU1R>vPBkoQ@Hxa{B zpAp+9!NLI|j1bFg7#WShgObK;ld$n--K$6LgN)zY&N<3JY3`0E4%0{~KfQc>;8E>GX9-{~OzY1^~Z4Fd`%WH;F+6#0wWa zWx0P75(j{i+wJ9*{>^xZ0o<-xn;rY#>_t1!P$SKvWM=+vsACpT^}a&VU9A7sBFzF$ z@xKTEPt^Z^Hm(pIO;;b?dw0P9%`yc;d4a)$_8(6n|2)bZ@Tlt%&bpQ?<{`cVjiTZ!W^*?v|AAtN1GXGAw&i{WGBtod*@1MMY45c7MjJ@77@x%0`ZZ7$m zRYKs#-1^|ePy2ya@!Y#cnwqhshgni@;3&VI#m|6PS_wK6Vm% z=hL3$#(f=T{8z|1=Afm66|4T)f$V-*@fU%XnSE+2<+B-349$b6=aphtFkI=5;(}&E_dPbi|{rWnhoTvwh zV+E!c=@$}eWI`guoT#(>yqxlivz&thGjmBbvVk7$2dJ)L!80L`_cTKz^o$`*q!j@D z5ANuZt9AvO2RJ9yd;aDhZhzbAsx_^i0j&|6Z#&CiACP+Ky19`6!BV>|Wyz&U>2SI( zlv70!xp-d`WQyZIhTwz%vqx%oubVu8VGv1=XVElRA;G3t&j@T&Wa2n*LP%ul6FX&b zIN#W)W(yBLSP#66qBf@>ah^_gvdbk7Aq41x4Je7Nigo`NXL8hv|C^OS-mP9@VXiI? zEl;ovYFgs^cE9xZB{EX*LtqaTas=I^QHbW!rgqk;)8X^39C?T?7Pkh}qw0MAi9lLU zd;la47~Kxm6O4a{51x?z9*+;>fF>wffhjq&^YqmkmoD1fB0(X|z=N0NGXp5dQW;B* z%6B(Y?z4n2Tf7T?4X#Z}Z!drNN;Hub35CW2LSmG)qJu!{PMxef;TR(}UsRzIg;^O* z24b{}PY`$j|6xu2^)v!8>YpOGTaFo5--*|41{$7bY2EMZ?L1^-#rp=77PQzErC70? zjn5kKaBkc{(L)>w5Ac*Y=W8uOxry=q+|HMK5mB173iP>rJrM9=a4kJg!VhUH3ij>~ zY7-s)SZ4unxI6i-DetdvHOp-lvsCXq84m@f)b>^Em0uCJYW>2%Fb49dKSi|5-Zd4vyFBhC*&|@ z3rgTL#iJpD@zAME%*B%d#@U-f;sJ`d7LfU8c-w`$7DyI&#(AM(fvPB~HSfWVh9l`h zF_w)$unE;UvLIPs;D8!Deyb=2N<0?)>sMoT+IQ@<3<)`vAoCa)Mk%lw-*Q~`FL>w@2nA3{A__h;%* zTkv0bP=G!2_1WXuo0d`Dup)9F$Hx}M=Yy2#MJeY5Atu1dmfvUfv4>E)>{3ehvfrM4 z_V(klIM7vp_N>WxvB(u0$}eXna4ueDQbG z^(_c!N#DxAUtPV;84~F!vOvb5cfFhi#KcjKs8(HYBdP>Ni*Z! zhI2s8wj}&q!r-1v5y1LCQ)-QFbM_lOT{72O(cQfhvRR4P6Iij9(~AtaHT<6~Lk;}E zXcBPS2GaZs4@Ouy>8*;*2iD#c5?=u7>yGgM;?Z*XoidDHHY@^qYbW<>s^1%th}_k( z{bB9_oU-pbM?o+`EXCOd$s~#a7RAc+uQKiS6{05x-OqR zLO>dT;W4u9+fsH&0Y(D#=k83QN6qT`^ZW-4vS-^zf$%k80!a~ zUNUy=F~!`odVXG-Gf3P$Kq8}B@mj24O_y2bNmcb`lo+_(6R%kv3UscFPb8!u7HKOp25g7jbc721-Hy%$J&K9P#-Ed+VK&d`ErDmdLW_FDO#4E1#l1#Iu5j8IgR4bi;C%vFxZ@Ck~u#;gmHmd=cA_=J$ z8zcogXnCUet~CV_FhA=G%AqBD9D>O8r}}-)q&B}S|`&+P@UVqk(^0Mg*)J^^G`Omd9(s5~5)Dkewh6euTDx1*i^ z3;@6b0&@YwD5B;BYP8(H@aaL^axby+=jgW22B%;zrIhi&`ru0H?BYWG={iftTi^j+ z^umSGG2<(NZ|~Bp#hhtI=`uj#$S^ic(7V$$w0Rnp@_=Nuo|f8ctrni)q~BneLT0g+MZC6nn*7Wc z#jp|qSHBO;rzat(SL=q)4K4Sn!L;OY#J4C`h7_<#B~YfmomJ7_IllMrY=R_H27AR#B23@@cJL*-JZYd_=eV`u}3~%hOw)wqhtg@8FWl0_Z6~{mlK;Ts8{%|u! z#<(U@2PmLX3>tnhj{UjfhlX}6hJ;#67SllLFU$eSYV$XrN^s+6+vB;d8Js^C?@1yG zS*Yu$P;b*=yDi(pz$0%-_&g(l3r73RY1mxf1Bj$i$OE&KJy^cOakEm6!xoH?1Jq~X z=$!z3w`1-v?9t!W8@@bE{R_a+jn*MzF6gm=^2}@#BL?>zsweEfHdJQxjuZ58ZHF9G zTF!IQ@01UC4SOwN|FWd`T7mWajeV>=fXR;9rlE0%Rtkk_`IAl zy}fIYKL35D4>l{51lo4D?D;eR>|{(nukxr})RH>kO~%zTg7TD#IX>>cmXEK@k8{2# z>$!#@^5<;qf#JrR?u62kVhyLMk{5TDBXypFkqr~_xf^b20{(x>^Au7TC5KXL!$}w+ zt%9rPb&b_AE1PBt`dzP1PFC+#(6WZV=Zy$fd--ML=UrZc>p#}2>UOGT#JBH)J@d_f zif%hpH{-iXAnIqz41CWOkQ8uZV-jaBI00Sl*Uk#I@%Z`c$x}FC6KZQkYO^BfgkREE zT>>N4MG_*>RFyul$VT(F4Cr2G^HcGka_q+nw5-ZcpxcD8iTW#k;?PTpo-C#Hb}fJ& z1e>}=H#W7`@zeZ5>n=Tu$_K|^1CAGR>r(Q+8feYK1=^K%`>^3&-GN7J<2&tj5J@Gs8Yq^WvBJbgB@I07)AL>b8I3u65&K|KYje(eGT{ z`D!YsDZbOw^D1qXQtrHA`0jVxnv|H&=yPf7b!?yX>VPYzNj)l7VzD~zuSLs&88eF= zrVM5h4VBTAA7Ijd)&O!61MKPni|+oGp=|9BM{tr@ZgS9~IaT>!-e+?(>d4~DWx(%-vQuL(X*ez~;6(6Mvven^Cw^sGH-KwPl@C+RQUo{VxWaJ{7#K zi>60^$U?QmJyt9BEW zQXqXU7yeoh%eEK=I_bkA@TsL(PDE_O!OR?3F5zsy6@Go z@R6>d1o`5|e-qRAQ%5c<&fOmTI2ZI;^WOIT8XI@?*H{4o6Ot4xE(TLFHNTb@3yo^^ z@!!&ckT^YRys0C5dzYI4rL~Tpw9g^Y#^M$AL{rj5P1BoBt%vXB#h0hhmeMm;*FsOC zsq1(wu9s_D!ZsH+iHra`V0z-Wr+Uo~yeoS9A-0zXve%EV@OgYtgRA`J+WG~y(iVMEf7J8tH7h9WS6v1W??iRv1?32{@(cC@x<h1V)9Ct+r`z}*6Z@yijALJ+T=x8?hD97TuD`sYuIhZ25bN$Y&;kl39C&gK+mZ-o(MLuI0T`ZpW!xl+v#*^1|8%lABRy z82k}UGKX9Gfn{zwQb4@!_%swg>f7;Kt=s37`WVG$gwqTeEn89Igmh~)2 zYo+OHY9FNeT|cCQT86YN_cM+&Cb-l(_P&i#cEFVjpZEJSVo3=K1MSG!nirfJ&X`Ig z_~*aE#ptG2+{tc_DA()RbH1@QZbh@@T4)yE`CalEl@B_+bWBwN9puwKY<3J*QnZ_m z4oF6+!^Qsmd0&SPKQS10do=C&OZq~*kqCP!TnIR0r`A-$aEck;Js6>N?qjyEb7@Tv zg-xh1T4ih#k6J*7J1`p<^M^a(qH0W2Zx+%41|;4nhf6LQ+B&gxj z6%0RVp6rc?zqj~&j2`H>uN?I*h<;s54K!h;+wx^K&5{PE(24$l-gRK~AF*=3O1^k# zP7sZ?VhN%LktE$SU~82BxlZq=`H%%YR=YGrhf~%^L&lp<&^W|XwNA90Vn?O3x)qT& zw`-WZ0CZF3A32P=f)-!sxo^JgajECYOnlpOOIE1#_|!dmgBs-%iWKfCKGL{sGv`yf zCz`ZBXd*N42seAN0;~7t=EBrk$1?80$GM>73qIwvl}FP_dImoVfYU&vlgA4loR~Gr z>nE~h1l#&IbJ3UVedzNiXi4!T_tM zxYZ82kY_-j=bK##599NmO)8@B$`7iFXQq#K-V`!RXj9(O$u}NclWUolV$~0h*}Ig> z{a+c~Q)bs#>e{2V4ipIfzv#l0S|89zcIxRBMeXf5zx?t|q6UJejXyR0tj00_>1%4h z=IXQA)oJbFJ6Z|ht!q#7i9Xs8=YiHgFP>mU&yj>@+W@B z#~@A9c~_q&#=0<1|GM+1s*ajykj`z;xkiLPHkiF>lIYN!^Z)RL{>n~d={sehfNQ=w zz;pwGX8m?vD|>`TT6nJ}Wg!e9pYKP}nWTFO&b~&R{n6{Owl(XWlCJa|6p66tYTN-q?@X5nB6+ zU*+m;VB^`TYPN2L$xNtc^uf8GQ8`3nYJL3LqUihifAV>yW^A3#@q7>K+s)Tu{Vd&cK^LU3C6=48f)W=sjPW=%$Og zPXea3-CM2}W0;17=fY*8+16=PrWWk=36r@jli#U1eQeJk{@L=2a@io?FNcJo)4bjw zX*_ZA{-hcGS(4XP^!L&Y!Gs{fEgZ5FMN8zuZ+aT(?qV5n6|<1*!CDmK_RgZ|_0OT* zR(*_PCRiYHZqgXlun`5 zU$@HoowST$PN><{%z@3pJ=!U;14Z#-$rqMOOR9(RF#3fPYeW4S`Y60mli2x;kX@I# z>9t`-WX$cJn&VF`WL+3#Svhkyg+--BRu&?mKih`kRe3P)e$v5WP$Uw@#-cg%Y&Y^C zOtQgwnB($1?7q=W9pn0J)4~kzURb|B9|DAMJmB4R>C}NG7xr5zefd+(h;{B+dn_s~ zp%Nsux&eWbfMg`U6$>=@26Qn4Ojd4|c0I`bLV@XYfWL|z0fHD;GP<0l7@v7q9RHa{ zX2^(drhhY8`K_)u-p8bN|I>Kpvai?z-}66AkEI%qvAdHsXO z#Um(6;E+ht6Q_|9c3_VpV0t3vH34W!X(u9U?nj6a$agd=!R%o9p8502YXyDm?!!K{ z!5adr6X85VdvmMn-X>0(i!oXA&>)+fFZh@9=V^vsmm`_D9K?OkDWQWmS9N3?xiZfCm)eCg21s3s zyexmBxxO3nE;`X6R7aDA8b#l@aYn5;ghkz^XpKU_sH?}8U z=9ByL?KfqHx5n49K1gtMorcmhsR)t1X+6$g^)A9~JadsAx+d`9xC>a!m_wy*l&U91O3UvY(Uj?Q-&#pTOF`E@QD^7>Mo)d~JlzphzV4{+* znm&9nRM&AcPi}zsI&w6nUl6n(CViA~gwPsJg?fN&iwUSujIy(^Vi1umNCxFr&$s0te=6s{YVqL`1P;` zawiLg`_NxP%y{7GidxI_s_`Yo^2LWEEs(AxxnP-ty*bX~Gx0a!GlBLqlAq7lq5@vt zn!t)?bLJ$SkN!Ls;QIXRDb7R9>@T_W^r=?JUSXJiIoO)7_uD;>*2H_2ikj%X!cD#a zqt-vL61oR|)C>d+z*XVUX69qj=v+GwCM&}HBO;fjCj7I3NY4r2eKfjDhbQ`%^Uo3z z1j?CYHhd)yM?r21Mpw~AAiq=e;`Tvio#~$IX?)Dz^AzvDd;6xr7{Pm7 zO63@onr=vQKdYP8=fIt8#=C>k_ZVC3o)s4ZE6j*gG%B)l_mKwtre6ur??8Idn;LV(&DMY>xgn&klF+ z%~H9*mH!SEjQ`5oiNL&3ML}{5b!|UIVqZ-(yWIl#*C@yWISR~hje zrHtwg;Dbs(`BkrlGy^iT6fn#7#tn|U@XTb#3v2jZzLhJR*iGBjJaY>)nx78a5}vuc zccz87nsX%y6?tJ8DUvg$Y%BGHbDo}FwsJIUMK`M{=xL7w06)2ALDIIbd-mLp!o;d- z!_q%zI;)-?5f!lH4C*eD5d(g*(4F9_@LGv{?6HWsgc;9?_MS_gM3G12-L-F(t=v22 zn_o1quO_>D`A;fKq|irvSI?$ccq(U|^vo}G+H6B+L+tB0aX_?Szk|~)>Y_ZY!24Z( zWa)fYN_rThZ3l;(*9}RVlfFQ~SCtS%KB&00QuX!fGCmo%mVTa<-+Xyys&IGhvL}W5 zjLF00>nkotz!EDJwg$paqTR02{D`A>T`wCc16@b!bY|QROV)Po_ZW&)jpR__{)_iHxv}G&{;6MD&y0+)?u5oNd{Iaj`i$HS9 zid8!npdsEEwC1(V?h{bSo{zH2jRik_xwZEGT#t_XB-cvf6{ zIr4VSTqO7Vow!t#BFo`uiM#ov`wWYxIf2aLVTa6=Y()j$ev(gh)iNkC~)VU3*2Gs0Low{%JQN{ow!Nj(Hrs(pdm@ z9r*Fgt{^hRwCs$D$Co05)_*}j4SFOFoA?-98*SIXo=p;Wwdt{}q@H1%uI4MrFm<;( zyVmz`E+HcKno-RBJj`&`E_jQ>L94C<1o@VxTpfi0h5oLxLF3ygV)VzP_mAjj@?@GU zt#atjj=Osn&u#g6X)TXL+`48z-5)E3aB!+RS%Ko%pHV;T1tGAXJ`90!fFl#~+}&;GHa68BCY<`8 zMCO~xwtlx0gI%{MocY2y9n<>GKfkf_9t33@-GgO0By=6ZZ|o3FEnBJwjVoPwhRVi! zUPY&`$EvngrpjA(He{Gu{T!-#$^0ity;jqpdsf=ltkW+y}tzFG^OC*e@)nIMP$*8uzsii z{vjh`0nFX?RkBV@s(T-}u@REp&{UcwTU>>m__N!N{RUJN=EK+62WH1mWpP42anoxWLK=W#+)Gy|uxuqI-2+ z#{;L%{F67b@Gs87dHk}YBq;rICGnMw2?0OThcLlr-S4lv^}U&M@5HIwnb&1>mp*s@ zr09CfMa9HE^HR=F+e}u6BVjGqJMYZWoViQSV2-5{1n4)8`zH_!dv%k6amC-02KfR( zfwMjUfndS8M%iLtN8-D`@74&e5~-*U#1 zW%aNgNa$mqUvzrw_%=9}r;WDg-5F!ICIp+Xp4dK-fZehJ^;uZ^iYkJ6jtf|jZJ(p% zeq0gQ)s;}L^3w||7VnqCSuk#PU^%%07`eBQ~#)6)!Y z1U357ZgQ`GnTX-ek?sAIR=daRTmBhxyC_4yxxqjpsdh88zCL5UXLKl*!2r<2tg|eYHNLWDuMJ+&p_R|nhP*Aa?*^t= z4T+Ea>b35laT|RP zE|;174^a%5je{WP9#Ki7s~P@!L98tSuDUJ$`eoCsuJE`*kKx zv7B?)!|4-&bEKaO0WGL`g7q%iZ@Vajp8iQ3SD?l5QuMk&b2BPF>L$0R02f2is=>WF zUuLYX{;&}l*yy?v#S@R5c_-2xI2$47?8RDTy#>(j)U}Nk301}kHCzdgNMv#2_F$|? z4!UyBrn3rdW6~l%lv^;)hVD+-GaOv)q1Mb6`4hRjmbJUL^Q)BhK}ww&1Ob`{$5mW= z>`c4qVSqpLqSDr%P_(qHntSvaSN^I&!hZrp(zD^>P{B6o)>}^<4DY8*=8J>lG2Y%F8Zu+)*v;?i5(yj?>`M)o%SP;cIC_7r%(ctXQsrlz6bqM6E-k==Fnt zncQ+qthvbBP-~F;7m{d^o=M-?_?pe-W+e^haa@pupfsM3&4l)#b+ffnZ2P>{>PKrnRQFaD^pTa z1&pBOW$JFu6qn;ySpy%a<^)GBlFMcA*Mn|4zSzp_WXv?)=Ic({S+#Yi9G+PqJ4Km| zVvOL+=u2a3Ki^h#mpA>(6C#-Ki|xanPinKXMQ6l&db|woV_m$*M+O(Rm-%n~b2VBY zw8HY!7f~2wfZXGr+DsCne5d~qJBf?i-9f%T<0OtA_G|EXx@XWVSyeY({BACH^`-slbY%sy(CVaCW9mna$SmtJ(NOo( zEL~*6t9BVCs8PzIc+z-(j3`p7PKNd77JIfPzlC(=YB%VW zpE-7_tP>mN%<@y43;&s}lQF)n`fY*Uky)2ajNmhXa4k_Q7Wd|j3h;ymmk4t{+@+_P zm|aCVY3)6`$akrNDFVSoLp5`|Ok(T0yQ>ie4*WK=LGz zC_USys~h3ptmyA8_N5y7+GujC>pg2hAmA_un;ju#{?4ICnuD#gw*e}93rWm3qiq#e z%zu?G8~8a7Y!}fFLLja`>`j`z_YgOhNH6pxj)r9}pyJ^ZGEK8*NVqlN$Op{l-CxRO{2orDk;p_9xnctDJwI)%m~* z5X4~@!iiH>b)!ztPd+m)Cl~eJ951R$^#MDvaCWBnI3wA}nU&C(Y8`078!c~hXq#a& z{qkk{r$!%-mjcHN`jK*x64dj%Db2>ofABrH>N>pcn_LuK`7Bn#r<&n~Njw-89}@uq z<*HE*P|u2*5P|A>hiaBLkm!3%Wf5kTd#Ud(OQhdb!Eg=hb~LYwKEwPjPd;Fn(yTYK zmEnRWyd8Niir@!=#=(T?8FNoxPe1L*VB5l6%FdzZ(zmrQXUg(>p_q+6cO;Pp4Mkzj zRQj|`NF4%ks6srBV6!ncsUx#hAy3Nl0&KVV> zvu8Wmqj25?gcIQlGwdBT{>3wM7f^b>U2t8V>|natcxI?IkNfDY+A$6NV5{hvV*L$S zo2(8X@PBkDqc1IV3G=dZF_QM@4Qx(&3s9RMF(u~{Dy>?rF&NPMzsDODWWD+Yi$JB> zzi~SwIQ(G!aOcgeQ$~{hZP_#flII-KH5?a;nE`WOO~05Jr1nA}>Q2(#JIT}uHw=?` z7aC@ac7P384w&&w2BCdCs~|F*>P8yIE8h}wobSz}ieO@V$h(b5IOhMwxV$q%?2^o` zE>jIg9YFK-tvU|Wd$qAPKx?z0Uk)M7XLYL6BeJPB$+UplDG zek&qc*`8|~(+^HhzNqqQ+h$~-S(k{cZ#R?%rB3|5nlduaF_PK|0Tv>O3$2aP7yGa< zpZZwmIOMy(nTa12b>99Tp3sTT%T$PIr64|P0blrigK^KjYrJ~4n|O* zT7sM#EN2`(B=8+q0#2xqU$c^ZnS58-=u2Z%`pwGPaBgtza8mq)%Sn)EHLIwnd#+jF zadywTC2XA=kuuS|q)IcVpHem4Wt=||nwzDuK6e=9GyV)%sx!ZK1!0zM*hW~0&4P-s zR!EcOd}?~phr@bv?l>FH4Q&l@=^vn~t~wfJcyeA}%x(l=;sswFF|Xr>t(1Mmt&|e{ z3x}LHWvk=ef+J6@Eq%JQhq>`=@ULmKZqmO*hOFrBB|p0aP1 z_GH^UOYqlEGhh>^t7bu7D;7l{^<{G=8n|d@R)?0e(Jre0^(TnyiJ~7U?yEC(z?#aQ zCf;bVg_i|oU({hCZbJ*f;>cIi^r*}w+*3S3PzC3Ny22$;#MHxxx4CDBK5<{e+e>+Z z`uX8WBs)y~d|NiM`d}(AV(?+m-ilcHAe|foIzmwM^0ptWNtXW3-Sj zG}vRr4>UhfIc}u+P*O=X7z6s;#IE&x>=AEPkw`H~^xxd**Og-q`Xt8tanrhH5uDPG zwBoA-zx~$N!q$$OiGCnAiftM=0TiCa)cd?CS?%HSCqTp#_kT8hsjLkfsk=Y8NgJF)m6 zvEIJcnO6iEKIuS+A0mv7k!@{(QS;a<{VmDeNd3HGhk42x2Q61qR>9W1RRoA%&v?+? z0-@)P=gTnYNyJcR1mk>p3o`3YO3bX~yEF_aP35vS-CnvNq6erlhVG-oePC5g8RJ`- z#xDKaa~qwFcSr|&Q`XKHJcE{z6UsBHd4h~p&ZOB_=kq!A8-MZqXVxOn$Pi5S0D8@DgdsC(isA>l7 zu4GD7Rm~Fs>@Mhol+(hoSqA%H4sAStluS^+mS#*whPp{Mke@w#wZuwR2Slut^ivcGYc)C<>81H^!Kd_5e z13?7e1w;bEbL|yEN0qhnis-jbtT$S%SvEyn)9uk88Xl&ios*6AOaku} zmp^4@NPF7aFWgeNOcUSPkwL;;yJba;OT;(L_s@5KD{FhVR)@;otocvH>;R^Hv;P^8k80z2{*iC*R5rcMX=a+~?xq(q z)fW&&UvFVC*Ztx1lmz_YsmIDQbySC@-38|kfqTro z zCn)b8&=oMu6ygwwJfdasJX|@L6?m1Dv0X9t>JAWO^UIj0#&(3UrHx;vP^3g= zL{(XT!?`D*pP8)WoGHYEZZc$!odTzb8n)q0|88*>6P z`?6&CSv_W7r2yF0beQ2*?V^_%pKktVAo`)T^26X@NpK_*-ni{D7{Sp{C0A<|16l(; zOL*xGW|*sKsiwHvE!h3QXe@^a#6W3}8!DQu-h?A_4gkeRYkt4NC~GR5P8eyp;9kVQ8$QG$5ad7Fo23Z~ak1jY~RXG{v?3G$RarFe`XePu3X{R+=mBOw&X zks)|Sc$RcG-jhn!`~-x|vg!&DA&@}QH^RNdyy9nq56yrU$^qAaS+F_NOaeFb)CVaH z?!UvPajgrK&zqdAs>&Def#wkcG_UhmYOVw^M`VZz@+4IWAVzK%`+za9rm2SD9={u@ zlx5D6UDL;lc7#9`+%vnlP3PescU=N`DHQPt_N55GNBMkVCRMR4?fvp zAFsvcHN4c9rb>J@{*IH>RTr9de%9i4Gd(cbFa9SP4anhoP;TA0!oZyB8?lNMDHPHK zCaOaFU9?x2A!o>p>mCF9r+hKs9Czu_P1l$LWU%}q#)=T3p`ZnYyeHmsewqw`}L^4LuHqfo+CG6<2n7#l^3;H^^!1 zsaieYFnN)Kc7Mv}^xE)4kXUw8<9I+jMB@QV9T9I8haLDt1Ne#exWUfGYG$4uMoEu& zo81#2up18Y40h%tIsOZglp(ltVsE*j1~$lVd|;rN)&${~o~-%KZnJp&3|OFR{^8E9 zJ;fCu53Ysw%}@VYWE*z7r)&4P=^B-SF%a@>*9g84<4aFUZT7x)qdsS+#2tu5NbpU@ zg;EwV)l-#sK>#r9>(0Figx{9lKm>KvRj;y<8 zc8SxMW4<11(s@QMV_}n9MRzA*62->vzxmHh1)GVASEJY7LVtRw`Rv{v`(Fuc00(&o z%m>gS2aJekmdNQ4p<{pD3HqZ-%4hdU1__xYhLi9mTJXD|E zE`t6SX)}l_DY5vO0Xrs#O6_DKtPKn0f+e~SprDYmJL_`<053iA5P`zn z4<5etc%aF58sHFr#M;U-9|=;l)J#Q2vS!Q9(d(EX6fubL%uA_lqa2%!cpNIv78QZ}Ayo(>C(ZpsRtKhzD--fpuoCch87cX-Bna9_{z%$b*dHM0?+T&Hk!+^UM`r|vq z2Id$??bX^|tfYaE+h#Nik(ZcN+wt)28q^gWe!y8jDCXrD<2qV#49x@5$8&Zrd5NTs zNYcix;9fe#PQQ;T?!6hG>9K{K+RCPqiGc9z%t{=`QaX>7O{l(+#7mJ1>Rae^J?82e z6cLqLypskTCyu>uc~$0-XZ^1Qvhwr+pKQ#CKImhGu*MGM*ZrROuAHWuT*yM$ieEy8*KLFMMdLZL|D+yDmy@3_PELTEVMI6nwfcYA3ZQ9wwKdtkT z;`;z7fU{U6>CS7kr3=A-()_G*G(Mjf2wXKe

Fpy)y!S(AQHSG#udd_8#b4sQu!R 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 PgFh2POM|ilu9yD@)d*=D diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png deleted file mode 100644 index 9c60a1761dbf62cc2a45ff98b9fdb63ade16e4d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3773 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 Dh-f%Y diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png deleted file mode 100644 index 448d6efb577d07e227a5c62545173ddf6bd86b55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4750 zcmdsb=QrF9wDnK#B^X9;Q9^V{l#CXA)L_)ej2hhtqlaMh5WS5Wg6N_|8RZ$G_vkf9 z5JZU<_2zy5i+ewuz1RM*&sqD^UhBjd=xI=qvycM-K&7Rr`urbf{=Xq5{)gcIxVZlj zp`)^{G62*iQd}d50Dw+SOI6v}4{!ekg(s{Rq@YE5pOB7&`>m3SpD-<+qnxv4BTc@~ zM{1D|O$!#56?*b|pjiA#`~(%lh{=Se_>I>=aGy#&c20J1)xLMF9?|AKE-r2*uD9=L zRY*6d50*AXL)Jq$@9tJ}ma)sZ0~?*^w~ptSKl}5a9mjs_?y7Pd#S^L|D+OqJQxG540qoJ9dxD4)lwK(7)=k+md0c4*X=xd1L*Bu!u z%IRa8oVJY=UYOj>NnpuG}*2TYAF24V94?je zUn_6KJ`0DnJuwUn#kMy`qNMZoy|$PAr?*5OdiL(X0#Lq<3T~)ZC0OaK@7P&x#jE<9*CKd^1)k_8t0b@>!&CT(6^Vy?`Uq7#5j&EGJlORzv>e%! znNY2P<X(KdS7AjZJSP76n+gVPg|8`_aX=2NCQjf`n$&Bz-=oXMpPbt_7ZJ zh^-Xlyca1Utv+%7>m5TkZ{%Qx(C#Z=+|Ej(;ElO(DCF9luaWBuyGh>)*@GDaGT|BR zod!zD@$y#$wNz2RUfGI#+@(Fab9)QAnmytV*y@sSQ!PL@jUse^PgI$Z$)92HQ~LD{ zETF}D!n%DLy>--g$73{;S&vPo1Op{M5Ow8=Dym*(FD85KiP$$c8#!85;PhF2Y`QUV zFYV765M%m}sXorn6EC=*dKDqU(97Y^MD|aU`n#>k#$3a<^jHyE$E_ zemwewpe2Do>xLc2Qs2o)m%*~Rw{ONg2CjLpZNk*!h2eNhni=!5W?Yo`zF-Mw~$kw3gkv;)WEeRJ%Q#FGB11W}4wRlTZ_TV#D%k#g~SnL+{^%` z!z{{}F%_S;kjB;peqTqeD8S#O4Ew}rkJt3(C6$|Ej8)nF0RPHbe;HZy_f4`qbZctO zJ2n+lCL2LrHFIF=$KUYnMUKU>8P|%UNaM)h9GZRy8an#?)qVHE{XY9^6FT@3&eTm2 zmfrOrEy4-?BYRLOE8bpz~Nldc&T14?{R<3(Au5u#{QUh8Td$cUzy#9flp8IQ*Qj(u}oeZ78W=8^%vHP{^4|N#Bvl`98)G7?ib* zoNPdZFMTRlbt^A=-Q`Xz1*?wU!9+Z|UQXAZ4X|G}riTAG)jiQR$py2ZLE0uN+dG^# zd|fWhqc=?NN~|J)y}8VM=fCrBnVqCpaREogX!bt^Fy07PpnjHSW{Q!Bo<5CWE_v+C za)!T*V-&cDBb&5_`CZuHK1=TW9^ef&mq1{}F}JQk3LuBJgZ?)WRXSZx>W@9xHFd1& z&9ObICBPZVUc`-DDv1^r@5_aaB#W^8`xpJe=_J(qB`m&bHhNh4vRAri(u({~Q_F39 z?XYMfzb{3*TeZj0rikqNKnRpM^k`v$yt0mH8Rs@J2g!{RSc%zeO3#=U3;(IRwN~+Z z?myI?|BNin+Teiq%C8Vcs0l_Ktl+_X0#26De~_A4M%i^+d&6aNuFS(tgT>TdY~>n! zf$orZ*ktv&J&p-vx*+|e5GAexQaP~l%|!2T;*w{bBb1FFeD~T*8Pe8S&hJJ-QNvJ~ z8ime-a|vZ8+`v?z%T8ur9xjS4tY)jqR34HEH!x}F_V^I2Ag~?Q%yiCKO0Gsnp9akF zMysFO^KhSgTd!K}e?JTXbPXNIR_mw~#ra3fza zNY9x!b;s{dzWU16;-4K4r<<&q*^G0ipD3G%<#l*-DqVqNVh&*3SSzn2a&d*F4FvTY z;-^06$>qyavKOs36@iC7Hr8Wn6>6*rH|O_^bLAR5!arFD9R={zZ0Fi#dgvlpSX+T zUa=FNiB~wXLASe7I01qA^knmf?`_* zOGlz=XT63?s{)&Idd46x6&$(Ab@My};^Y3ckF?y+-qvrz^CQQI{3HOwNGUPL91nXk zTvxP}wu+f4Ch%pN1RcggTQKZ~F zs74ss`*&JuYb+(?i$hlx{Eg>KWG6F-#r5{un4~1-EtOAX`aTi|ZnU2|m!kW7eT75j zO`(A~7FD6*`lQr0j;Bx#qq|-y=!>b~rC-p~y!U)^V~`XIr%fgQ-_g>cb+jRJCDHur z(+`%WiWvmgEQ!K*Vhu;1k%~1|iX1G2@+?G`-=)lOw~6hebs-IG(pRs zOb{x3)`8YbZFA6cO5!DJL4-i?EM}RI)IW1C=&q922RESUr(yV)h9n{<{U5e!pB)e! z%*7&CrdxA?Jg7fydY$6Ov`SZmiB%rWI;_&(I>?X=d0afq1A-4D2j?hiQBjcQZ+%MX*%c73h>8}umx>Yk zu%9A@CVcq*DjVu#CwPYRDx2nM8(rYbipb?~!Xv8eZmGZ_P&jHD8S!cH5&Y7X#-e-g^BJ47w zJ=YWa$dfPc|NI`CWwK#epKw_#qw@4m)YeGnj2wR@*m1pDeI?EE??9?yI*z>wWP90; z+qsoIH?Om_4DTqV?2_qkA=Ps-qwahZR14~k2=m2jAu{n#>U;2yYgd`Kq^4}6X}NKYt$M$s_fw8pV9QRPl8=H4k#gS1^M^#1Fr+!c}) za~LH(u*dYD?@|@`52N!Ts9hphYz04~oJ6?<`0DlobtEGk)b-Q)0>q)?x17*u9ru*& zYTu7!Qr?gImCE83qE|s?LG!M60&wSxU#l2l*<9} z&{ro~y}D^!A)u%{9m45WkeHB5hpdTccw6XYwCuDHy)m;)&Up`HcbI0M8YSKz-Y)(B zTli^XzGAR6X1yBm{Nx)UkzfbO?hlZ${iLwJhBuu&#-?gcNP(xT#8Z<$daYs_*~N5~ zhOr-VX%k}P!}}Vxz8AUUFH;qX&Q$r%p#X*iRYx8429g>nUoWodB?xZW8p7y*T3JdgT+tzFIjJ| z$X{d&TB>l6wj5fxEB0$o7r75{NuXjK6V+{afG#yk{~3Y&PC&dSsO$+GdB&AAZvFa1 zOZK;IdxUWe=GqjJ5Pd1J^@BnFADubOZs>8dU#I&^rp+AlEsOTcoMSj8M{AiGg=gK< ze~X`_zI1^l+yRtY_-}(8n?bw8w${K z2}LeY9MEb%k}ym^+?aNudB+yp;yb80EB(Q5)pS352CzlkdfF8FTqm=$8tHavHIl4l zr>1E6u6cr&eF~IvS_T#>g>1694{4KDQ_>p@u$AVykK1udpf0TngCXH z5zQ&a+HwldYT^w$?BQ@e4IBsgOQ`y+1dLPf%$r9PR|0DDS<;Wh;@ml2YMS!$J#gkr z2I8`ly?+YO>2-{fM+YoYbrn@32CkVywO~r$DxLswt&x0x907iFJj0q5;NdTp^x=HG xOgkb~Yyd%RnTwfZ2r)bvM0@({f35M3^J$0L{S2#8=6??+Kub+ewOR!p_CK+I_KyGn diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png deleted file mode 100644 index 8524768f8d764da7e9c452a444208708f2d18ff1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4692 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 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png deleted file mode 100644 index 60a64703c0f11d08705cd607f7751338707f5919..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5192 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 nB>N_)Q7iwV%v7MwAoJ}E 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 T3-0p800000NkvXXu0mjfSlal| diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png deleted file mode 100644 index 1ad04f004b638bf781012290d78e4138f97bbe5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1761 zcmV<71|Io|P)4P%ubY|S^%$zf~ zmwTOa@12BA$oV-Y9!V&U%c=j==#_}M2ylE}1m>yyDoGsZ#Yy zNX}RO*f(MzmKS&u`qiajIyW{Y_LC%m2NqT@Ic|QpvYqwNgBK7n5X%c(3k^?2>EOA` zqGaXjE7H9BiJ55fh0iJRW}@=&(@R^E1hLB>kE%PS6eP@VZVdtn(fh;5DPKg!j;fJZ%)wH{Wn#~V&#n(o1URS zsyS`0Tu2m;-H}z9O^h`!UZAFr@?0a7Z;pYOi0uZhgzh=rOEDi`FIkKtVu*gEcSM!h zmb#_XR$akjlg$JI75MXgWkG7IUnYJ+X=1J!qJ%jXVj{L1I2QU%?=?DgV^U?)92DZm zV?>``xT1#kZdgUt!2n?|0>*6ae4tikA9FAlJ}kjmMQm_z3LB5sZYHBKdbex9_Hv@K z%Y?q@9-)b7vJ6X$3h0B4tH__=#*`9^efY@IQfghn*=E2Nb8sR8lrQlu`Ca_Rmm6>Z z7bkEe^w8M>x;hoNUvWu_GZJPVpI;bMTsBpf(@U$Ch(-gk0T#WpsaB1{7ISQ~Y48mW z;Nk?@LjM_?q{BV-D=veoOmJoncVDC1GwGGz(O5@o7ZkGCIJMPO(7K9b6M_wF?Xqd< zo4J6KF0_U2<1=T3x0qc6G6#g+^=N{QTpChC!GZuCY*|eU{Rw)LMN7a2wwbaCdn_dE zzy+Lip(XU4-+SoTFyEcnH3?HRV^%-;Ylx;|>8v&^Dy459ZJ_1zio`68!6s8SO(6sq zaN75WUKiF9+8MruR3=w5)hzA^Z1clVBuXc)+8@e);xX7bfygR&FsIIt-gQ+==(c;S z#J*CO1qZBF&M&6TRmskZXaMOU6&?jn_(BqY5 z>Y|^?uOh;yp6w0QR1`>tiEz_-{Zu!N#(nhRndJV$7;LOgPyZQ*J2yCin+~*u!qKj# zItsG2IDi{ZH+E6j=D|ht=qWxKNxaA6E>3Wna>=z1gy?*#>|g_1^BGspGro&OWRc(k zPP)(*y0WZ7Z-kICr3#g7($P*LCjE>7S`Xh~s!b~bPTo0XSkRm2T(M`QZbv-`Imx*Ulqk*aI* zaqa*=>61?nFb8w3wuiA&zyVyGz>!cm-pY3xEsMEiY)Th4FVrtqWp%V~gxW82)>4^N z*H{>GR?m$^s6BN^M=4^iEjT7(gM(8z7K#9Sn(M_`)oSH2zHDCla&cmK)bb|4nAMIO ze4S3gI9L8AD+T9c#C8K8JF)I68NBWK`5pE`q^OR#h~-u!e7P2i$UDq7^*uZr< zbp8sR<$nYxb8EKH|BltapZ^j3+PZt_u^PR*lT6;TCNxu^yFH$j(!JXvbmHfQ>a0>O z+2k{tOWD%ln$M`tD&>+*KBKvmEgi1;jOwgXF4_DG_&r&PcxYTT00000NkvXXu0mjf DptxI> diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png deleted file mode 100644 index 2dd52620a8f15577e56ec7fe8e671988dd17ab0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2537 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 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png deleted file mode 100644 index b058cae2f440e5a5875e45c036c99f1fb6356046..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2332 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 Kc&(xHv;P1B$EH01 diff --git a/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png b/samples/interop/NativeEmbedSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png deleted file mode 100644 index 4954a4bd33f613d45f74dc0b12beb516e3b38661..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2758 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 z - - - - - diff --git a/samples/interop/NativeEmbedSample.iOS/Info.plist b/samples/interop/NativeEmbedSample.iOS/Info.plist deleted file mode 100644 index b9656e1c20..0000000000 --- a/samples/interop/NativeEmbedSample.iOS/Info.plist +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CFBundleDisplayName - AvaloniaNative - CFBundleIdentifier - Avalonia.Native.Sample - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - UIDeviceFamily - - 1 - 2 - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - XSAppIconAssets - Assets.xcassets/AppIcon.appiconset - - diff --git a/samples/interop/NativeEmbedSample.iOS/Main.cs b/samples/interop/NativeEmbedSample.iOS/Main.cs deleted file mode 100644 index a1d1502084..0000000000 --- a/samples/interop/NativeEmbedSample.iOS/Main.cs +++ /dev/null @@ -1,6 +0,0 @@ -using NativeEmbedSample.iOS; - -// This is the main entry point of the application. -// If you want to use a different Application Delegate class from "AppDelegate" -// you can specify it here. -UIApplication.Main(args, null, typeof(AppDelegate)); diff --git a/samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj b/samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj deleted file mode 100644 index 83611c90a1..0000000000 --- a/samples/interop/NativeEmbedSample.iOS/NativeEmbedSample.iOS.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net6.0-ios - manual - Exe - enable - true - 11.2 - iossimulator-x64 - - - - - - - diff --git a/samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib b/samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib deleted file mode 100644 index 8190201742..0000000000 --- a/samples/interop/NativeEmbedSample.iOS/Resources/LaunchScreen.xib +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/interop/NativeEmbedSample/App.axaml b/samples/interop/NativeEmbedSample/App.axaml deleted file mode 100644 index d6f182ed3f..0000000000 --- a/samples/interop/NativeEmbedSample/App.axaml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/samples/interop/NativeEmbedSample/App.axaml.cs b/samples/interop/NativeEmbedSample/App.axaml.cs deleted file mode 100644 index 0a89ea441b..0000000000 --- a/samples/interop/NativeEmbedSample/App.axaml.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace NativeEmbedSample; - -public class App : Application -{ - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) - desktopLifetime.MainWindow = new MainWindow(); - else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) - singleViewLifetime.MainView = new MainView(); - - base.OnFrameworkInitializationCompleted(); - } -} diff --git a/samples/interop/NativeEmbedSample/EmbedSample.cs b/samples/interop/NativeEmbedSample/EmbedSample.cs deleted file mode 100644 index 340068058f..0000000000 --- a/samples/interop/NativeEmbedSample/EmbedSample.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; -using Avalonia.Controls; -using Avalonia.Platform; -using Avalonia.Threading; - -namespace NativeEmbedSample -{ - public partial class EmbedSample : NativeControlHost - { - public bool IsSecond { get; set; } - - protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) - { -#if DESKTOP - if (OperatingSystem.IsLinux()) - return CreateLinux(parent); - if (OperatingSystem.IsWindows()) - return CreateWin32(parent); - if (OperatingSystem.IsMacOS()) - return CreateOSX(parent); -#elif __ANDROID__ || ANDROID - if (OperatingSystem.IsAndroid()) - return CreateAndroid(parent); -#elif IOS - if (OperatingSystem.IsIOS()) - return CreateIOS(parent); -#endif - return base.CreateNativeControlCore(parent); - } - - protected override void DestroyNativeControlCore(IPlatformHandle control) - { -#if DESKTOP - if (OperatingSystem.IsLinux()) - DestroyLinux(control); - else if (OperatingSystem.IsWindows()) - DestroyWin32(control); - else if (OperatingSystem.IsMacOS()) - DestroyOSX(control); -#elif __ANDROID__ || ANDROID - if (OperatingSystem.IsAndroid()) - DestroyAndroid(control); -#elif IOS - if (OperatingSystem.IsIOS()) - DestroyIOS(control); -#endif - else base.DestroyNativeControlCore(control); - } - } -} diff --git a/samples/interop/NativeEmbedSample/MainView.axaml.cs b/samples/interop/NativeEmbedSample/MainView.axaml.cs deleted file mode 100644 index 976de7a97c..0000000000 --- a/samples/interop/NativeEmbedSample/MainView.axaml.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; - -namespace NativeEmbedSample; - -public class MainView : UserControl -{ - public MainView() - { - AvaloniaXamlLoader.Load(this); - } - - public async void ShowPopupDelay(object sender, RoutedEventArgs args) - { - await Task.Delay(3000); - ShowPopup(sender, args); - } - - public void ShowPopup(object sender, RoutedEventArgs args) - { - new ContextMenu() - { - Items = new List - { - new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } - } - }.Open((Control)sender); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == BoundsProperty) - { - var isMobile = change.GetNewValue().Width < 1200; - this.Find("FirstPanel")!.Classes.Set("mobile", isMobile); - this.Find("SecondPanel")!.Classes.Set("mobile", isMobile); - } - } -} diff --git a/samples/interop/NativeEmbedSample/MainWindow.axaml b/samples/interop/NativeEmbedSample/MainWindow.axaml deleted file mode 100644 index a615428778..0000000000 --- a/samples/interop/NativeEmbedSample/MainWindow.axaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/samples/interop/NativeEmbedSample/MainWindow.axaml.cs b/samples/interop/NativeEmbedSample/MainWindow.axaml.cs deleted file mode 100644 index a261dad5ed..0000000000 --- a/samples/interop/NativeEmbedSample/MainWindow.axaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -namespace NativeEmbedSample; - -public class MainWindow : Window -{ - public MainWindow() - { - AvaloniaXamlLoader.Load(this); -#if DEBUG && DESKTOP - this.AttachDevTools(); -#endif - } -} - diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj deleted file mode 100644 index 7783b6f43d..0000000000 --- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net6.0;net6.0-android;net6.0-ios - true - - $(DefineConstants);DESKTOP - - - - - - - - - - - - - - - - Gtk\Gtk.cs - - - - PreserveNewest - - - - - - - - - - - diff --git a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs index 7d12404090..4738bd86f9 100644 --- a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs +++ b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs @@ -110,8 +110,10 @@ namespace Avalonia.Android.Platform public void HideWithSize(Size size) { CheckDisposed(); + if (_attachedTo == null) + return; - size *= _attachedTo?._avaloniaView.TopLevelImpl.RenderScaling ?? 1; + size *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; _view.Visibility = ViewStates.Gone; _view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height)); _view.RequestLayout(); From 9db00cf9ded30a0b0ab49e1633bf082b93a838d9 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 11 May 2022 00:04:32 -0400 Subject: [PATCH 604/820] Solve app.manifest error in Win32NativeControlHost --- .../ControlCatalog.NetCore.csproj | 1 + samples/ControlCatalog.NetCore/app.manifest | 28 +++++++++++++++++++ .../Avalonia.Win32/Win32NativeControlHost.cs | 4 +++ 3 files changed, 33 insertions(+) create mode 100644 samples/ControlCatalog.NetCore/app.manifest diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 2db150ec85..0667644643 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -45,6 +45,7 @@ en + app.manifest diff --git a/samples/ControlCatalog.NetCore/app.manifest b/samples/ControlCatalog.NetCore/app.manifest new file mode 100644 index 0000000000..db90057191 --- /dev/null +++ b/samples/ControlCatalog.NetCore/app.manifest @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs index 2a1628ea7d..fd05e780bf 100644 --- a/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs +++ b/src/Windows/Avalonia.Win32/Win32NativeControlHost.cs @@ -94,6 +94,10 @@ namespace Avalonia.Win32 IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + if (Handle == IntPtr.Zero) + throw new InvalidOperationException("Unable to create child window for native control host. Application manifest with supported OS list might be required."); + if (layered) UnmanagedMethods.SetLayeredWindowAttributes(Handle, 0, 255, UnmanagedMethods.LayeredWindowFlags.LWA_ALPHA); From 855dade9d592e0d045599a214f62a3b4c0710298 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 11 May 2022 00:11:47 -0400 Subject: [PATCH 605/820] Add Browser implementation --- samples/ControlCatalog.Web/App.razor.cs | 4 + .../ControlCatalog.Web/EmbedSample.Browser.cs | 34 ++++ .../Shared/MainLayout.razor.css | 70 -------- .../ControlCatalog.Web/wwwroot/css/app.css | 44 +---- samples/ControlCatalog.Web/wwwroot/js/app.js | 11 +- .../Avalonia.Web.Blazor/AvaloniaView.razor | 44 ++++- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 43 +++-- .../Interop/NativeControlHostImpl.cs | 152 ++++++++++++++++++ .../Interop/Typescript/NativeControlHost.ts | 56 +++++++ .../JSObjectControlHandle.cs | 35 ++++ .../RazorViewTopLevelImpl.cs | 12 +- 11 files changed, 371 insertions(+), 134 deletions(-) create mode 100644 samples/ControlCatalog.Web/EmbedSample.Browser.cs delete mode 100644 samples/ControlCatalog.Web/Shared/MainLayout.razor.css create mode 100644 src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs create mode 100644 src/Web/Avalonia.Web.Blazor/Interop/Typescript/NativeControlHost.ts create mode 100644 src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs diff --git a/samples/ControlCatalog.Web/App.razor.cs b/samples/ControlCatalog.Web/App.razor.cs index a150824ac3..c0b7ddbe1e 100644 --- a/samples/ControlCatalog.Web/App.razor.cs +++ b/samples/ControlCatalog.Web/App.razor.cs @@ -7,6 +7,10 @@ public partial class App protected override void OnParametersSet() { WebAppBuilder.Configure() + .AfterSetup(_ => + { + ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); + }) .SetupWithSingleViewLifetime(); base.OnParametersSet(); diff --git a/samples/ControlCatalog.Web/EmbedSample.Browser.cs b/samples/ControlCatalog.Web/EmbedSample.Browser.cs new file mode 100644 index 0000000000..5fe14409de --- /dev/null +++ b/samples/ControlCatalog.Web/EmbedSample.Browser.cs @@ -0,0 +1,34 @@ +using System; + +using Avalonia; +using Avalonia.Platform; +using Avalonia.Web.Blazor; + +using ControlCatalog.Pages; + +using Microsoft.JSInterop; + +namespace ControlCatalog.Web; + +public class EmbedSampleWeb : INativeDemoControl +{ + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + var runtime = AvaloniaLocator.Current.GetRequiredService(); + + if (isSecond) + { + var iframe = runtime.Invoke("document.createElement", "iframe"); + iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70"); + + return new JSObjectControlHandle(iframe); + } + else + { + // window.createAppButton source is defined in "app.js" file. + var button = runtime.Invoke("window.createAppButton"); + + return new JSObjectControlHandle(button); + } + } +} diff --git a/samples/ControlCatalog.Web/Shared/MainLayout.razor.css b/samples/ControlCatalog.Web/Shared/MainLayout.razor.css deleted file mode 100644 index 43c355a47a..0000000000 --- a/samples/ControlCatalog.Web/Shared/MainLayout.razor.css +++ /dev/null @@ -1,70 +0,0 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -.main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - } - - .top-row a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row:not(.auth) { - display: none; - } - - .top-row.auth { - justify-content: space-between; - } - - .top-row a, .top-row .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .main > div { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} diff --git a/samples/ControlCatalog.Web/wwwroot/css/app.css b/samples/ControlCatalog.Web/wwwroot/css/app.css index d2a8dc525c..49ca14e162 100644 --- a/samples/ControlCatalog.Web/wwwroot/css/app.css +++ b/samples/ControlCatalog.Web/wwwroot/css/app.css @@ -44,47 +44,13 @@ a, .btn-link { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } - -.canvas-container { - opacity:1; - background-color:#ccc; - position:fixed; - width:100%; - height:100%; - top:0px; - left:0px; - z-index:500; -} - -canvas -{ - opacity:1; - background-color:#ccc; - position:fixed; - width:100%; - height:100%; - top:0px; - left:0px; - z-index:500; +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; } #app, .page { height: 100%; } - -.overlay{ - opacity:0.0; - background-color:#ccc; - position:fixed; - width:100vw; - height:100vh; - top:0px; - left:0px; - z-index:1000; -} diff --git a/samples/ControlCatalog.Web/wwwroot/js/app.js b/samples/ControlCatalog.Web/wwwroot/js/app.js index 5f282702bb..29697661a6 100644 --- a/samples/ControlCatalog.Web/wwwroot/js/app.js +++ b/samples/ControlCatalog.Web/wwwroot/js/app.js @@ -1 +1,10 @@ - \ No newline at end of file +window.createAppButton = function () { + var button = document.createElement('button'); + button.innerText = 'Hello world'; + var clickCount = 0; + button.onclick = () => { + clickCount++; + button.innerText = 'Click count ' + clickCount; + }; + return button; +} diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 584c77a62c..3dd98f8cd3 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -10,10 +10,42 @@ onkeydown="@OnKeyDown" onkeyup="@OnKeyUp"> - - - + + +

+ + diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index 1ccf53943a..be58d9d49c 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -1,5 +1,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Embedding; +using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Input.TextInput; @@ -18,14 +19,16 @@ namespace Avalonia.Web.Blazor private EmbeddableControlRoot _topLevel; // Interop - private SKHtmlCanvasInterop _interop = null!; - private SizeWatcherInterop _sizeWatcher = null!; - private DpiWatcherInterop _dpiWatcher = null!; - private SKHtmlCanvasInterop.GLInfo? _jsGlInfo = null!; - private InputHelperInterop _inputHelper = null!; - private InputHelperInterop _canvasHelper = null!; + private SKHtmlCanvasInterop? _interop = null; + private SizeWatcherInterop? _sizeWatcher = null; + private DpiWatcherInterop? _dpiWatcher = null; + private SKHtmlCanvasInterop.GLInfo? _jsGlInfo = null; + private InputHelperInterop? _inputHelper = null; + private InputHelperInterop? _canvasHelper = null; + private NativeControlHostInterop? _nativeControlHost = null; private ElementReference _htmlCanvas; private ElementReference _inputElement; + private ElementReference _nativeControlsContainer; private double _dpi = 1; private SKSize _canvasSize = new (100, 100); @@ -49,6 +52,11 @@ namespace Avalonia.Web.Blazor } } + internal INativeControlHostImpl GetNativeControlHostImpl() + { + return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); + } + private void OnTouchStart(TouchEventArgs e) { foreach (var touch in e.ChangedTouches) @@ -243,7 +251,7 @@ namespace Avalonia.Web.Blazor } } - _inputHelper.Clear(); + _inputHelper?.Clear(); } [Parameter(CaptureUnmatchedValues = true)] @@ -253,6 +261,8 @@ namespace Avalonia.Web.Blazor { if (firstRender) { + AvaloniaLocator.CurrentMutable.Bind().ToConstant((IJSInProcessRuntime)Js); + _inputHelper = await InputHelperInterop.ImportAsync(Js, _inputElement); _canvasHelper = await InputHelperInterop.ImportAsync(Js, _htmlCanvas); @@ -264,6 +274,8 @@ namespace Avalonia.Web.Blazor _canvasHelper.SetCursor(x); //windows }; + _nativeControlHost = await NativeControlHostInterop.ImportAsync(Js, _nativeControlsContainer); + Console.WriteLine("starting html canvas setup"); _interop = await SKHtmlCanvasInterop.ImportAsync(Js, _htmlCanvas, OnRenderFrame); @@ -319,9 +331,9 @@ namespace Avalonia.Web.Blazor public void Dispose() { - _dpiWatcher.Unsubscribe(OnDpiChanged); - _sizeWatcher.Dispose(); - _interop.Dispose(); + _dpiWatcher?.Unsubscribe(OnDpiChanged); + _sizeWatcher?.Dispose(); + _interop?.Dispose(); } private void ForceBlit() @@ -345,7 +357,7 @@ namespace Avalonia.Web.Blazor { _dpi = newDpi; - _interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); + _interop!.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); _topLevelImpl.SetClientSize(_canvasSize, _dpi); @@ -359,7 +371,7 @@ namespace Avalonia.Web.Blazor { _canvasSize = newSize; - _interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); + _interop!.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); _topLevelImpl.SetClientSize(_canvasSize, _dpi); @@ -369,6 +381,11 @@ namespace Avalonia.Web.Blazor public void SetClient(ITextInputMethodClient? client) { + if (_inputHelper is null) + { + return; + } + _inputHelper.Clear(); var active = client is { }; @@ -394,7 +411,7 @@ namespace Avalonia.Web.Blazor public void Reset() { - _inputHelper.Clear(); + _inputHelper?.Clear(); } } } diff --git a/src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs b/src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs new file mode 100644 index 0000000000..48362b03c4 --- /dev/null +++ b/src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs @@ -0,0 +1,152 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; + +namespace Avalonia.Web.Blazor.Interop +{ + + internal class NativeControlHostInterop : JSModuleInterop, INativeControlHostImpl + { + private const string JsFilename = "./_content/Avalonia.Web.Blazor/NativeControlHost.js"; + private const string CreateDefaultChildSymbol = "NativeControlHost.CreateDefaultChild"; + private const string CreateAttachmentSymbol = "NativeControlHost.CreateAttachment"; + private const string GetReferenceSymbol = "NativeControlHost.GetReference"; + + private readonly ElementReference hostElement; + + public static async Task ImportAsync(IJSRuntime js, ElementReference element) + { + var interop = new NativeControlHostInterop(js, element); + await interop.ImportAsync(); + return interop; + } + + public NativeControlHostInterop(IJSRuntime js, ElementReference element) + : base(js, JsFilename) + { + hostElement = element; + } + + public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) + { + var element = Invoke(CreateDefaultChildSymbol); + return new JSObjectControlHandle(element); + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) + { + Attachment? a = null; + try + { + using var hostElementJsReference = Invoke(GetReferenceSymbol, hostElement); + var child = create(new JSObjectControlHandle(hostElementJsReference)); + var attachmenetReference = Invoke(CreateAttachmentSymbol); + // It has to be assigned to the variable before property setter is called so we dispose it on exception +#pragma warning disable IDE0017 // Simplify object initialization + a = new Attachment(attachmenetReference, child); +#pragma warning restore IDE0017 // Simplify object initialization + a.AttachedTo = this; + return a; + } + catch + { + a?.Dispose(); + throw; + } + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) + { + var attachmenetReference = Invoke(CreateAttachmentSymbol); + var a = new Attachment(attachmenetReference, handle); + a.AttachedTo = this; + return a; + } + + public bool IsCompatibleWith(IPlatformHandle handle) => handle is JSObjectControlHandle; + + class Attachment : INativeControlHostControlTopLevelAttachment + { + private const string InitializeWithChildHandleSymbol = "InitializeWithChildHandle"; + private const string AttachToSymbol = "AttachTo"; + private const string ShowInBoundsSymbol = "ShowInBounds"; + private const string HideWithSizeSymbol = "HideWithSize"; + private const string ReleaseChildSymbol = "ReleaseChild"; + + private IJSInProcessObjectReference? _native; + private NativeControlHostInterop? _attachedTo; + + public Attachment(IJSInProcessObjectReference native, IPlatformHandle handle) + { + _native = native; + _native.InvokeVoid(InitializeWithChildHandleSymbol, ((JSObjectControlHandle)handle).Object); + } + + public void Dispose() + { + if (_native != null) + { + _native.InvokeVoid(ReleaseChildSymbol); + _native.Dispose(); + _native = null; + } + } + + public INativeControlHostImpl? AttachedTo + { + get => _attachedTo!; + set + { + CheckDisposed(); + + var host = (NativeControlHostInterop?)value; + if (host == null) + { + _native.InvokeVoid(AttachToSymbol); + } + else + { + _native.InvokeVoid(AttachToSymbol, host.hostElement); + } + _attachedTo = host; + } + } + + public bool IsCompatibleWith(INativeControlHostImpl host) => host is NativeControlHostInterop; + + public void HideWithSize(Size size) + { + CheckDisposed(); + if (_attachedTo == null) + return; + + _native.InvokeVoid(HideWithSizeSymbol, Math.Max(1, (float)size.Width), Math.Max(1, (float)size.Height)); + } + + public void ShowInBounds(Rect bounds) + { + CheckDisposed(); + + if (_attachedTo == null) + throw new InvalidOperationException("Native control isn't attached to a toplevel"); + + bounds = new Rect(bounds.X, bounds.Y, Math.Max(1, bounds.Width), + Math.Max(1, bounds.Height)); + + _native.InvokeVoid(ShowInBoundsSymbol, (float)bounds.X, (float)bounds.Y, (float)bounds.Width, (float)bounds.Height); + } + + [MemberNotNull(nameof(_native))] + private void CheckDisposed() + { + if (_native == null) + throw new ObjectDisposedException(nameof(Attachment)); + } + } + } +} diff --git a/src/Web/Avalonia.Web.Blazor/Interop/Typescript/NativeControlHost.ts b/src/Web/Avalonia.Web.Blazor/Interop/Typescript/NativeControlHost.ts new file mode 100644 index 0000000000..baa9191845 --- /dev/null +++ b/src/Web/Avalonia.Web.Blazor/Interop/Typescript/NativeControlHost.ts @@ -0,0 +1,56 @@ +export class NativeControlHost { + public static CreateDefaultChild(parent: HTMLElement): HTMLElement { + return document.createElement("div"); + } + + // Used to convert ElementReference to JSObjectReference. + // Is there a better way? + public static GetReference(element: Element): Element { + return element; + } + + public static CreateAttachment(): NativeControlHostTopLevelAttachment { + return new NativeControlHostTopLevelAttachment(); + } +} + +class NativeControlHostTopLevelAttachment +{ + _child: HTMLElement; + _host: HTMLElement; + + InitializeWithChildHandle(child: HTMLElement) { + this._child = child; + this._child.style.position = "absolute"; + } + + AttachTo(host: HTMLElement): void { + if (this._host) { + this._host.removeChild(this._child); + } + + this._host = host; + + if (this._host) { + this._host.appendChild(this._child); + } + } + + ShowInBounds(x: number, y: number, width: number, height: number): void { + this._child.style.top = y + "px"; + this._child.style.left = x + "px"; + this._child.style.width = width + "px"; + this._child.style.height = height + "px"; + this._child.style.display = "block"; + } + + HideWithSize(width: number, height: number): void { + this._child.style.width = width + "px"; + this._child.style.height = height + "px"; + this._child.style.display = "none"; + } + + ReleaseChild(): void { + this._child = null; + } +} diff --git a/src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs b/src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs new file mode 100644 index 0000000000..4426c3fbd7 --- /dev/null +++ b/src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs @@ -0,0 +1,35 @@ +#nullable enable +using Avalonia.Controls.Platform; + +using Microsoft.JSInterop; + +namespace Avalonia.Web.Blazor +{ + public class JSObjectControlHandle : INativeControlHostDestroyableControlHandle + { + internal const string ElementReferenceDescriptor = "JSObjectReference"; + + public JSObjectControlHandle(IJSObjectReference reference) + { + Object = reference; + } + + public IJSObjectReference Object { get; } + + public IntPtr Handle => throw new NotSupportedException(); + + public string? HandleDescriptor => ElementReferenceDescriptor; + + public void Destroy() + { + if (Object is IJSInProcessObjectReference inProcess) + { + inProcess.Dispose(); + } + else + { + _ = Object.DisposeAsync(); + } + } + } +} diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs index 209a635a7b..50070e6e2c 100644 --- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs +++ b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs @@ -13,19 +13,19 @@ using SkiaSharp; namespace Avalonia.Web.Blazor { - internal class RazorViewTopLevelImpl : ITopLevelImplWithTextInputMethod + internal class RazorViewTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost { private Size _clientSize; private BlazorSkiaSurface? _currentSurface; private IInputRoot? _inputRoot; private readonly Stopwatch _sw = Stopwatch.StartNew(); - private readonly ITextInputMethodImpl _textInputMethod; + private readonly AvaloniaView _avaloniaView; private readonly TouchDevice _touchDevice; private string _currentCursor = CssCursor.Default; - public RazorViewTopLevelImpl(ITextInputMethodImpl textInputMethod) + public RazorViewTopLevelImpl(AvaloniaView avaloniaView) { - _textInputMethod = textInputMethod; + _avaloniaView = avaloniaView; TransparencyLevel = WindowTransparencyLevel.None; AcrylicCompensationLevels = new AcrylicPlatformCompensationLevels(1, 1, 1); _touchDevice = new TouchDevice(); @@ -175,6 +175,8 @@ namespace Avalonia.Web.Blazor public WindowTransparencyLevel TransparencyLevel { get; } public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } - public ITextInputMethodImpl TextInputMethod => _textInputMethod; + public ITextInputMethodImpl TextInputMethod => _avaloniaView; + + public INativeControlHostImpl? NativeControlHost => _avaloniaView.GetNativeControlHostImpl(); } } From a5b2eb08d4000d7bb4c8c4d1f21afe51bb2031b9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 11 May 2022 21:12:25 +0300 Subject: [PATCH 606/820] Added support for IReflectableType in InpcPropertyAccessorPlugin --- .../Plugins/InpcPropertyAccessorPlugin.cs | 19 +- .../Data/BindingTests.cs | 19 ++ .../Data/DynamicReflectableType.cs | 221 ++++++++++++++++++ 3 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index 33cecd10a7..b93bf87fdf 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -17,7 +17,7 @@ namespace Avalonia.Data.Core.Plugins new Dictionary<(Type, string), PropertyInfo?>(); /// - public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null; + public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null; /// /// Starts monitoring the value of a property on an object. @@ -36,7 +36,7 @@ namespace Avalonia.Data.Core.Plugins if (!reference.TryGetTarget(out var instance) || instance is null) return null; - var p = GetFirstPropertyWithName(instance.GetType(), propertyName); + var p = GetFirstPropertyWithName(instance, propertyName); if (p != null) { @@ -50,8 +50,16 @@ namespace Avalonia.Data.Core.Plugins } } - private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName) + private const BindingFlags PropertyBindingFlags = + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + + private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName) { + if (instance is IReflectableType reflectableType) + return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags); + + var type = instance.GetType(); + var key = (type, propertyName); if (!_propertyLookup.TryGetValue(key, out var propertyInfo)) @@ -66,10 +74,7 @@ namespace Avalonia.Data.Core.Plugins { PropertyInfo? found = null; - const BindingFlags bindingFlags = - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; - - var properties = type.GetProperties(bindingFlags); + var properties = type.GetProperties(PropertyBindingFlags); foreach (PropertyInfo propertyInfo in properties) { diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs index 055de999e2..11a22f0dec 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs @@ -617,6 +617,25 @@ namespace Avalonia.Markup.UnitTests.Data Assert.Equal(0, source.SubscriberCount); } + + [Fact] + public void Binding_Can_Resolve_Property_From_IReflectableType_Type() + { + var source = new DynamicReflectableType { ["Foo"] = "foo" }; + var target = new TwoWayBindingTest { DataContext = source }; + var binding = new Binding + { + Path = "Foo", + }; + + target.Bind(TwoWayBindingTest.TwoWayProperty, binding); + + Assert.Equal("foo", target.TwoWay); + source["Foo"] = "bar"; + Assert.Equal("bar", target.TwoWay); + target.TwoWay = "baz"; + Assert.Equal("baz", source["Foo"]); + } private class StyledPropertyClass : AvaloniaObject { diff --git a/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs b/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs new file mode 100644 index 0000000000..3c57bd38cf --- /dev/null +++ b/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using Moq; + +namespace Avalonia.Markup.UnitTests.Data; + +class DynamicReflectableType : IReflectableType, INotifyPropertyChanged, IEnumerable> +{ + private Dictionary _dic = new(); + + public TypeInfo GetTypeInfo() + { + return new FakeTypeInfo(); + } + + public void Add(string key, object value) + { + _dic.Add(key, value); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key)); + } + + public object this[string key] + { + get => _dic[key]; + set + { + _dic[key] = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key)); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + public IEnumerator> GetEnumerator() + { + return _dic.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_dic).GetEnumerator(); + } + + + class FakeTypeInfo : TypeInfo + { + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, + ParameterModifier[] modifiers) + { + var propInfo = new Mock(); + propInfo.SetupGet(x => x.Name).Returns(name); + propInfo.SetupGet(x => x.PropertyType).Returns(typeof(object)); + propInfo.SetupGet(x => x.CanWrite).Returns(true); + propInfo.Setup(x => x.GetValue(It.IsAny(), It.IsAny())) + .Returns((object target, object [] _) => ((DynamicReflectableType)target)._dic.GetValueOrDefault(name)); + propInfo.Setup(x => x.SetValue(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((object target, object value, object [] _) => + { + ((DynamicReflectableType)target)._dic[name] = value; + }); + return propInfo.Object; + } + + #region NotSupported + + + public override object[] GetCustomAttributes(bool inherit) + { + throw new NotSupportedException(); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + throw new NotSupportedException(); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + throw new NotSupportedException(); + } + + public override Module Module { get; } + public override string Namespace { get; } + public override string Name { get; } + protected override TypeAttributes GetAttributeFlagsImpl() + { + throw new NotSupportedException(); + } + + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, + Type[] types, ParameterModifier[] modifiers) + { + throw new NotSupportedException(); + } + + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type GetElementType() + { + throw new NotSupportedException(); + } + + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, + Type[] types, ParameterModifier[] modifiers) + { + throw new NotSupportedException(); + } + + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, + ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + throw new NotSupportedException(); + } + + public override Type UnderlyingSystemType { get; } + + protected override bool IsArrayImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsByRefImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsCOMObjectImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsPointerImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsPrimitiveImpl() + { + throw new NotSupportedException(); + } + + public override Assembly Assembly { get; } + public override string AssemblyQualifiedName { get; } + public override Type BaseType { get; } + public override string FullName { get; } + public override Guid GUID { get; } + + + + protected override bool HasElementTypeImpl() + { + throw new NotSupportedException(); + } + + public override Type GetNestedType(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type GetInterface(string name, bool ignoreCase) + { + throw new NotSupportedException(); + } + + public override Type[] GetInterfaces() + { + throw new NotSupportedException(); + } + + + #endregion + + } +} \ No newline at end of file From 35db70c8d4905eba47f0fd73e2de0830d5e8e2fe Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 12 May 2022 14:37:23 +0200 Subject: [PATCH 607/820] Don't expose viewbox container as logical child. #7735 introduced an internal container control which hosts the child, but it exposed this child in the logical tree, breaking any styles which relied on the `Viewbox.Child` being the logical child of the `Viewbox`. Fix this by introducing a simple internal `ViewboxContainer` control as the container. --- src/Avalonia.Controls/Viewbox.cs | 43 +++++++++++++++++-- .../ViewboxTests.cs | 20 +++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 33a05f126d..f3ec53ed2d 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -8,7 +8,7 @@ namespace Avalonia.Controls /// public class Viewbox : Control { - private Decorator _containerVisual; + private ViewboxContainer _containerVisual; /// /// Defines the property. @@ -37,9 +37,8 @@ namespace Avalonia.Controls public Viewbox() { - _containerVisual = new Decorator(); + _containerVisual = new ViewboxContainer(); _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft; - LogicalChildren.Add(_containerVisual); VisualChildren.Add(_containerVisual); } @@ -88,7 +87,22 @@ namespace Avalonia.Controls if (change.Property == ChildProperty) { + var (oldChild, newChild) = change.GetOldAndNewValue(); + + if (oldChild is not null) + { + ((ISetLogicalParent)oldChild).SetParent(null); + LogicalChildren.Remove(oldChild); + } + _containerVisual.Child = change.GetNewValue(); + + if (newChild is not null) + { + ((ISetLogicalParent)newChild).SetParent(this); + LogicalChildren.Add(newChild); + } + InvalidateMeasure(); } } @@ -129,5 +143,28 @@ namespace Avalonia.Controls return finalSize; } + + private class ViewboxContainer : Control + { + private IControl? _child; + + public IControl? Child + { + get => _child; + set + { + if (_child != value) + { + if (_child is not null) + VisualChildren.Remove(_child); + + _child = value; + + if (_child is not null) + VisualChildren.Add(_child); + } + } + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index d33e55341b..39a14a6a7e 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Shapes; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -170,5 +171,24 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(expectedScale, scaleTransform.ScaleX); Assert.Equal(expectedScale, scaleTransform.ScaleY); } + + [Fact] + public void Child_Should_Be_Logical_Child_Of_Viewbox() + { + var target = new Viewbox(); + + Assert.Empty(target.GetLogicalChildren()); + + var child = new Canvas(); + target.Child = child; + + Assert.Single(target.GetLogicalChildren(), child); + Assert.Same(child.GetLogicalParent(), target); + + target.Child = null; + + Assert.Empty(target.GetLogicalChildren()); + Assert.Null(child.GetLogicalParent()); + } } } From 36fefd871704a48ef6fd553845ad6c03e70ea9ad Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 12 May 2022 22:44:29 +0200 Subject: [PATCH 608/820] Fix copypasta. Co-authored-by: Max Katz --- src/Avalonia.Controls/Viewbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index f3ec53ed2d..55c52d8ed9 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -95,7 +95,7 @@ namespace Avalonia.Controls LogicalChildren.Remove(oldChild); } - _containerVisual.Child = change.GetNewValue(); + _containerVisual.Child = newChild; if (newChild is not null) { From e4f4837a8d3ac9e8a30cb84c03c6dbcb2da39d0b Mon Sep 17 00:00:00 2001 From: robloo Date: Thu, 12 May 2022 21:52:36 -0400 Subject: [PATCH 609/820] Simplify getting changed property values Co-authored-by: Max Katz --- src/Avalonia.Controls/Calendar/CalendarDatePicker.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index 0005e07536..8dffc43d91 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -291,8 +291,7 @@ namespace Avalonia.Controls // Text else if (change.Property == TextProperty) { - var oldValue = change.GetOldValue(); - var value = change.GetNewValue(); + var (oldValue, value) = change.GetOldAndNewValue(); if (!_suspendTextChangeHandler) { From ccce304c69385fc3f5c41a3646084792c0f37f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:13:20 +0200 Subject: [PATCH 610/820] Fix TimePicker property registrations --- src/Avalonia.Controls/DateTimePickers/TimePicker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index f04c79505e..047667567d 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -38,13 +38,13 @@ namespace Avalonia.Controls /// Defines the property /// public static readonly StyledProperty HeaderProperty = - AvaloniaProperty.Register(nameof(Header)); + AvaloniaProperty.Register(nameof(Header)); /// /// Defines the property /// public static readonly StyledProperty HeaderTemplateProperty = - AvaloniaProperty.Register(nameof(HeaderTemplate)); + AvaloniaProperty.Register(nameof(HeaderTemplate)); /// /// Defines the property From 2ee94118305364b72aee6865d427f1330314bc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:16:05 +0200 Subject: [PATCH 611/820] Fix SelectingItemsControl WrapSelection property owner --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 164aab32db..a730659330 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty WrapSelectionProperty = - AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); + AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); private static readonly IList Empty = Array.Empty(); private string _textSearchTerm = string.Empty; From 08cbec6f23848dab0680531a059eb8c92babb290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:28:42 +0200 Subject: [PATCH 612/820] Fix ContentPresenter property field types --- .../Presenters/ContentPresenter.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 996cb29534..12a8dd747d 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -52,67 +52,67 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly AttachedProperty ForegroundProperty = + public static readonly StyledProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontFamilyProperty = + public static readonly StyledProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontSizeProperty = + public static readonly StyledProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStyleProperty = + public static readonly StyledProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontWeightProperty = + public static readonly StyledProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStretchProperty = + public static readonly StyledProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextAlignmentProperty = + public static readonly StyledProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextWrappingProperty = + public static readonly StyledProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextTrimmingProperty = + public static readonly StyledProperty TextTrimmingProperty = TextBlock.TextTrimmingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty LineHeightProperty = + public static readonly StyledProperty LineHeightProperty = TextBlock.LineHeightProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty MaxLinesProperty = + public static readonly StyledProperty MaxLinesProperty = TextBlock.MaxLinesProperty.AddOwner(); /// From b34095fda37618cde431112f0abc3be3056f5ac2 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 13 May 2022 23:53:26 +0200 Subject: [PATCH 613/820] Optimize memory usage of styles and fix ordering issue in devtools. --- src/Avalonia.Base/Styling/IStyleInstance.cs | 5 +++ .../Styling/PropertySetterInstance.cs | 10 +++--- ...e.cs => PropertySetterTemplateInstance.cs} | 31 ++++++++-------- src/Avalonia.Base/Styling/Setter.cs | 9 ----- src/Avalonia.Base/Styling/StyleInstance.cs | 35 ++++++++++--------- .../ViewModels/ControlDetailsViewModel.cs | 3 +- 6 files changed, 46 insertions(+), 47 deletions(-) rename src/Avalonia.Base/Styling/{PropertySetterLazyInstance.cs => PropertySetterTemplateInstance.cs} (79%) diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 8ddb989bc0..d4f7510eb3 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -14,6 +14,11 @@ namespace Avalonia.Styling /// IStyle Source { get; } + /// + /// Gets a value indicating whether this style has an activator. + /// + bool HasActivator { get; } + /// /// Gets a value indicating whether this style is active. /// diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs index 48f462d006..c4e8f47e67 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -44,7 +44,7 @@ namespace Avalonia.Styling { if (hasActivator) { - if (_styledProperty is object) + if (_styledProperty is not null) { _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger); } @@ -55,13 +55,15 @@ namespace Avalonia.Styling } else { - if (_styledProperty is object) + var target = (AvaloniaObject) _target; + + if (_styledProperty is not null) { - _subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style); + _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style); } else { - _target.SetValue(_directProperty!, _value); + target.SetValue(_directProperty!, _value); } } } diff --git a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs similarity index 79% rename from src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs index 92653d0064..0f6efef1be 100644 --- a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs @@ -11,42 +11,42 @@ namespace Avalonia.Styling /// evaluated. /// /// The target property type. - internal class PropertySetterLazyInstance : SingleSubscriberObservableBase>, + internal class PropertySetterTemplateInstance : SingleSubscriberObservableBase>, ISetterInstance { private readonly IStyleable _target; private readonly StyledPropertyBase? _styledProperty; private readonly DirectPropertyBase? _directProperty; - private readonly Func _valueFactory; + private readonly ITemplate _template; private BindingValue _value; private IDisposable? _subscription; private bool _isActive; - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, StyledPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _styledProperty = property; - _valueFactory = valueFactory; + _template = template; } - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, DirectPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _directProperty = property; - _valueFactory = valueFactory; + _template = template; } public void Start(bool hasActivator) { _isActive = !hasActivator; - if (_styledProperty is object) + if (_styledProperty is not null) { var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style; _subscription = _target.Bind(_styledProperty, this, priority); @@ -77,7 +77,7 @@ namespace Avalonia.Styling public override void Dispose() { - if (_subscription is object) + if (_subscription is not null) { var sub = _subscription; _subscription = null; @@ -85,7 +85,7 @@ namespace Avalonia.Styling } else if (_isActive) { - if (_styledProperty is object) + if (_styledProperty is not null) { _target.ClearValue(_styledProperty); } @@ -101,22 +101,21 @@ namespace Avalonia.Styling protected override void Subscribed() => PublishNext(); protected override void Unsubscribed() { } - private T GetValue() + private void EnsureTemplate() { if (_value.HasValue) { - return _value.Value; + return; } - _value = _valueFactory(); - return _value.Value; + _value = (T) _template.Build(); } private void PublishNext() { if (_isActive) { - GetValue(); + EnsureTemplate(); PublishNext(_value); } else diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index b4b3399022..d989bb0706 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -1,9 +1,7 @@ using System; using Avalonia.Animation; using Avalonia.Data; -using Avalonia.Data.Core; using Avalonia.Metadata; -using Avalonia.Utilities; #nullable enable @@ -70,12 +68,5 @@ namespace Avalonia.Styling return Property.CreateSetterInstance(target, Value); } - - private struct SetterVisitorData - { - public IStyleable target; - public object? value; - public ISetterInstance? result; - } } } diff --git a/src/Avalonia.Base/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs index 830cf49a0d..db96da6821 100644 --- a/src/Avalonia.Base/Styling/StyleInstance.cs +++ b/src/Avalonia.Base/Styling/StyleInstance.cs @@ -11,10 +11,10 @@ namespace Avalonia.Styling /// /// A which has been instanced on a control. /// - internal class StyleInstance : IStyleInstance, IStyleActivatorSink + internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink { - private readonly List? _setters; - private readonly List? _animations; + private readonly ISetterInstance[]? _setters; + private readonly IDisposable[]? _animations; private readonly IStyleActivator? _activator; private readonly Subject? _animationTrigger; @@ -30,41 +30,42 @@ namespace Avalonia.Styling _activator = activator; IsActive = _activator is null; - if (setters is object) + if (setters is not null) { var setterCount = setters.Count; - _setters = new List(setterCount); + _setters = new ISetterInstance[setterCount]; for (var i = 0; i < setterCount; ++i) { - _setters.Add(setters[i].Instance(Target)); + _setters[i] = setters[i].Instance(Target); } } - if (animations is object && target is Animatable animatable) + if (animations is not null && target is Animatable animatable) { var animationsCount = animations.Count; - _animations = new List(animationsCount); + _animations = new IDisposable[animationsCount]; _animationTrigger = new Subject(); for (var i = 0; i < animationsCount; ++i) { - _animations.Add(animations[i].Apply(animatable, null, _animationTrigger)); + _animations[i] = animations[i].Apply(animatable, null, _animationTrigger); } } } + public bool HasActivator => _activator is not null; public bool IsActive { get; private set; } public IStyle Source { get; } public IStyleable Target { get; } public void Start() { - var hasActivator = _activator is object; + var hasActivator = HasActivator; - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -76,7 +77,7 @@ namespace Avalonia.Styling { _activator!.Subscribe(this, 0); } - else if (_animationTrigger != null) + else if (_animationTrigger is not null) { _animationTrigger.OnNext(true); } @@ -84,7 +85,7 @@ namespace Avalonia.Styling public void Dispose() { - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -92,11 +93,11 @@ namespace Avalonia.Styling } } - if (_animations is object) + if (_animations is not null) { - foreach (var subscripion in _animations) + foreach (var subscription in _animations) { - subscripion.Dispose(); + subscription.Dispose(); } } @@ -111,7 +112,7 @@ namespace Avalonia.Styling _animationTrigger?.OnNext(value); - if (_setters is object) + if (_setters is not null) { if (IsActive) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index a1fd425571..e383c160e3 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -60,7 +60,8 @@ namespace Avalonia.Diagnostics.ViewModels var styleDiagnostics = styledElement.GetStyleDiagnostics(); - foreach (var appliedStyle in styleDiagnostics.AppliedStyles) + // We need to place styles without activator first, such styles will be overwritten by ones with activators. + foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator)) { var styleSource = appliedStyle.Source; From 6c7dc426fcec124fcb2edb75be0ec1fa0bdb0afc Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 13 May 2022 23:59:09 +0200 Subject: [PATCH 614/820] Missing property changes. --- src/Avalonia.Base/DirectPropertyBase.cs | 5 ++--- src/Avalonia.Base/StyledPropertyBase.cs | 8 +++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 9c1ffce24c..efcb7dfecb 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -2,7 +2,6 @@ using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -188,10 +187,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index dd5eb703ea..da607720ff 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -1,9 +1,7 @@ using System; -using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -12,7 +10,7 @@ namespace Avalonia /// public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor { - private bool _inherits; + private readonly bool _inherits; /// /// Initializes a new instance of the class. @@ -243,10 +241,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { From 90b67c3b9f73f2dfc6698f5f43486b0367c986cf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 14 May 2022 01:22:34 +0200 Subject: [PATCH 615/820] Modify test setup so actual framework classes are used. --- tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs index ef2586fcb7..ed4c78aa3e 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs @@ -91,16 +91,13 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Setter_Should_Apply_Value_Without_Activator_With_Style_Priority() { - var control = new Mock(); - var style = Mock.Of - - - - - - From 3fccb1417459a5cb96fedb156895af11ec460a0c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 19 May 2022 16:43:58 +0200 Subject: [PATCH 672/820] Added style caching to Style children. Extracted out the caching code from `Styles` and reuse for `Style.Children`. --- src/Avalonia.Base/Styling/Style.cs | 11 +++-- src/Avalonia.Base/Styling/StyleCache.cs | 58 +++++++++++++++++++++++++ src/Avalonia.Base/Styling/Styles.cs | 41 ++--------------- 3 files changed, 66 insertions(+), 44 deletions(-) create mode 100644 src/Avalonia.Base/Styling/StyleCache.cs diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index a8707e00c0..8fcf5eec8a 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -16,6 +16,7 @@ namespace Avalonia.Styling private IResourceDictionary? _resources; private List? _setters; private List? _animations; + private StyleCache? _childCache; /// /// Initializes a new instance of the class. @@ -124,12 +125,10 @@ namespace Avalonia.Styling if (_children is not null) { - foreach (var child in _children) - { - var childResult = child.TryAttach(target, host); - if (childResult > result) - result = childResult; - } + _childCache ??= new StyleCache(); + var childResult = _childCache.TryAttach(_children, target, host); + if (childResult > result) + result = childResult; } return result; diff --git a/src/Avalonia.Base/Styling/StyleCache.cs b/src/Avalonia.Base/Styling/StyleCache.cs new file mode 100644 index 0000000000..3285476880 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleCache.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Styling +{ + /// + /// Simple cache for improving performance of applying styles. + /// + /// + /// Maps to a list of styles that are known be be possible + /// matches. + /// + internal class StyleCache : Dictionary?> + { + public SelectorMatchResult TryAttach(IList styles, IStyleable target, IStyleHost? host) + { + if (TryGetValue(target.StyleKey, out var cached)) + { + if (cached is object) + { + var result = SelectorMatchResult.NeverThisType; + + foreach (var style in cached) + { + var childResult = style.TryAttach(target, host); + if (childResult > result) + result = childResult; + } + + return result; + } + else + { + return SelectorMatchResult.NeverThisType; + } + } + else + { + List? matches = null; + + foreach (var child in styles) + { + if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType) + { + matches ??= new List(); + matches.Add(child); + } + } + + Add(target.StyleKey, matches); + + return matches is null ? + SelectorMatchResult.NeverThisType : + SelectorMatchResult.AlwaysThisType; + } + } + } +} diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index d79081152e..7c0bc4ad7f 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -20,7 +20,7 @@ namespace Avalonia.Styling private readonly AvaloniaList _styles = new AvaloniaList(); private IResourceHost? _owner; private IResourceDictionary? _resources; - private Dictionary?>? _cache; + private StyleCache? _cache; public Styles() { @@ -111,43 +111,8 @@ namespace Avalonia.Styling public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) { - _cache ??= new Dictionary?>(); - - if (_cache.TryGetValue(target.StyleKey, out var cached)) - { - if (cached is object) - { - foreach (var style in cached) - { - style.TryAttach(target, host); - } - - return SelectorMatchResult.AlwaysThisType; - } - else - { - return SelectorMatchResult.NeverThisType; - } - } - else - { - List? matches = null; - - foreach (var child in this) - { - if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType) - { - matches ??= new List(); - matches.Add(child); - } - } - - _cache.Add(target.StyleKey, matches); - - return matches is null ? - SelectorMatchResult.NeverThisType : - SelectorMatchResult.AlwaysThisType; - } + _cache ??= new StyleCache(); + return _cache.TryAttach(this, target, host); } /// From 0a7f34f4c681d0e445d91637cfc54108006c56d9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 19 May 2022 20:25:14 +0200 Subject: [PATCH 673/820] Use nested styles in CheckBox template. --- .../Controls/CheckBox.xaml | 603 +++++++++--------- 1 file changed, 315 insertions(+), 288 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index f16e1ed99f..2d70a35b13 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -1,294 +1,321 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 75a10efcf10860c3644d6a2639f2b70355bd7617 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 19 May 2022 23:55:48 -0400 Subject: [PATCH 674/820] Avoid static references in DataGrid fluent theme --- .../Themes/Fluent.xaml | 141 +++++++----------- 1 file changed, 53 insertions(+), 88 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 8243d9c22d..2baa8c88c9 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -1,87 +1,51 @@ - + - 12,0,12,0 - 0.6 0.8 + 12,0,12,0 M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Opacity="0.4" + Color="{DynamicResource SystemBaseMediumLowColor}" /> + + + From 55b19b445f210d579fad8105baaa9acbc3a12f9e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 20 May 2022 16:59:53 +0200 Subject: [PATCH 675/820] Remove unused vars. --- .../Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index c5f779cbbb..d49fcf03a2 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -222,7 +222,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Adding_Child_With_No_Nesting_Selector_Fails() { - var control = new Control1(); var parent = new Style(x => x.OfType()); var child = new Style(x => x.Class("foo")); @@ -232,7 +231,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Adding_Combinator_Selector_Child_With_No_Nesting_Selector_Fails() { - var control = new Control1(); var parent = new Style(x => x.OfType()); var child = new Style(x => x.Class("foo").Descendant().Class("bar")); @@ -242,7 +240,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Adding_Or_Selector_Child_With_No_Nesting_Selector_Fails() { - var control = new Control1(); var parent = new Style(x => x.OfType()); var child = new Style(x => Selectors.Or( x.Nesting().Class("foo"), @@ -254,7 +251,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Can_Add_Child_Without_Nesting_Selector_To_Style_Without_Selector() { - var control = new Control1(); var parent = new Style(); var child = new Style(x => x.Class("foo")); From 060ec9694b62d74a21ccef2420feb53c3ccb78cf Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 20 May 2022 19:50:41 +0100 Subject: [PATCH 676/820] Merge pull request #8165 from AvaloniaUI/fixes/position-osx Fix more OSX regressions --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 17 ----------------- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 15 +++++++++------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index cf3ecefb3c..cb52047148 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -34,23 +34,6 @@ protected: return NSWindowStyleMaskBorderless; } - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - [Window setContentSize:NSSize{x, y}]; - - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; - } - - return S_OK; - } - } - public: virtual bool ShouldTakeFocusOnShow() override { diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 0e482f9f30..e3e646ff2a 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -35,6 +35,7 @@ BEGIN_INTERFACE_MAP() ComPtr _glContext; NSObject *renderTarget; AvnPoint lastPositionSet; + bool hasPosition; NSSize lastSize; NSSize lastMinSize; NSSize lastMaxSize; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index b84e999825..bc512f80d8 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -30,8 +30,8 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) View = [[AvnView alloc] initWithParent:this]; StandardContainer = [[AutoFitContentView new] initWithContent:View]; - lastPositionSet.X = -1; - lastPositionSet.Y = -1; + lastPositionSet = { 0, 0 }; + hasPosition = false; lastSize = NSSize { 100, 100 }; lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMinSize = NSSize { 0, 0 }; @@ -92,9 +92,12 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { CreateNSWindow(isDialog); InitialiseNSWindow(); - if(lastPositionSet.X >= 0 && lastPositionSet.Y >= 0) + if(hasPosition) { SetPosition(lastPositionSet); + } else + { + [Window center]; } UpdateStyle(); @@ -288,12 +291,12 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso } @try { + lastSize = NSSize {x, y}; + if (!_shown) { BaseEvents->Resized(AvnSize{x, y}, reason); } - lastSize = NSSize {x, y}; - if(Window != nullptr) { [Window setContentSize:lastSize]; } @@ -385,6 +388,7 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { @autoreleasepool { lastPositionSet = point; + hasPosition = true; if(Window != nullptr) { [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; @@ -577,7 +581,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; - [Window center]; if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu]; From 4845c10d881d7aa3bdf10e4e417f0755a6b0c2c4 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 21 May 2022 11:36:57 +0200 Subject: [PATCH 677/820] fix(ExpressionNode): ensure _subscriber do not change during ValueChanged --- src/Avalonia.Base/Data/Core/ExpressionNode.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 54785f18e8..4f755ff140 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -98,36 +98,36 @@ namespace Avalonia.Data.Core private void ValueChanged(object? value, bool notify) { - if (_subscriber is null) - return; - - var notification = value as BindingNotification; - - if (notification == null) + if (_subscriber is { } subscriber) { - LastValue = value != null ? new WeakReference(value) : NullReference; + var notification = value as BindingNotification; + var next = Next; - if (Next != null) + if (notification == null) { - Next.Target = LastValue; + LastValue = value != null ? new WeakReference(value) : NullReference; + if (next != null) + { + next.Target = LastValue; + } + else if (notify) + { + subscriber(value); + } } - else if (notify) + else { - _subscriber(value); - } - } - else - { - LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; + LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; - if (Next != null) - { - Next.Target = LastValue; - } + if (next != null) + { + next.Target = LastValue; + } - if (Next == null || notification.Error != null) - { - _subscriber(value); + if (next == null || notification.Error != null) + { + subscriber(value); + } } } } From 81eb964ae6082bd4aac045000146f6d756fb8683 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Mon, 23 May 2022 19:19:19 +0300 Subject: [PATCH 678/820] Fix Menu selection to match UWP. --- .../Platform/DefaultMenuInteractionHandler.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 3b38750de7..66b2a84e6b 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -56,6 +56,7 @@ namespace Avalonia.Controls.Platform Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.AddHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.AddHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); _root = Menu.VisualRoot; @@ -91,6 +92,7 @@ namespace Avalonia.Controls.Platform Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.RemoveHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.RemoveHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); if (_root is InputElement inputRoot) { @@ -340,6 +342,21 @@ namespace Avalonia.Controls.Platform } } + protected internal virtual void PointerMoved(object? sender, PointerEventArgs e) + { + var item = GetMenuItem(e.Source as IControl) as MenuItem; + if (item?.TransformedBounds == null) + { + return; + } + var point = e.GetCurrentPoint(null); + + if (point.Properties.IsLeftButtonPressed && item.TransformedBounds.Value.Contains(point.Position) == false) + { + e.Pointer.Capture(null); + } + } + protected internal virtual void PointerLeave(object? sender, PointerEventArgs e) { var item = GetMenuItem(e.Source as IControl); From 40a4ea745bd93929d7e645f5cbaeaf4805886b0f Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Mon, 23 May 2022 21:43:27 +0300 Subject: [PATCH 679/820] replacement of ViewboxContainer inheritance from Control to Layoutable --- src/Avalonia.Controls/Viewbox.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 01a41a0157..01667c5136 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -1,6 +1,7 @@ using Avalonia.Media; using Avalonia.Media.Immutable; using Avalonia.Metadata; +using Avalonia.Layout; namespace Avalonia.Controls { @@ -150,7 +151,7 @@ namespace Avalonia.Controls /// /// A simple container control which hosts its child as a visual but not logical child. /// - private class ViewboxContainer : Control + private class ViewboxContainer : Layoutable { private IControl? _child; From ac24ee467ad565accf6bcd47f6901d6db07c0696 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 01:35:08 -0400 Subject: [PATCH 680/820] Update TopLevelImpl.cs --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 8a475676a5..65a9adc937 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -40,7 +40,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _gl = GlPlatformSurface.TryCreate(this); _framebuffer = new FramebufferManager(this); - RenderScaling = (int)_view.Scaling; + RenderScaling = _view.Scaling; MaxClientSize = new PixelSize(_view.Resources.DisplayMetrics.WidthPixels, _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling); From 1c2da897e5bdea98100ef1f0c4f6ce0364010d76 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Tue, 24 May 2022 15:18:56 +0300 Subject: [PATCH 681/820] Add comment. --- src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 66b2a84e6b..3a6d06f150 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -344,6 +344,7 @@ namespace Avalonia.Controls.Platform protected internal virtual void PointerMoved(object? sender, PointerEventArgs e) { + // HACK: #8179 needs to be addressed to correctly implement it in the PointerPressed method. var item = GetMenuItem(e.Source as IControl) as MenuItem; if (item?.TransformedBounds == null) { From 0654d636932c863897121e3d66c6226c7a1cdd94 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 24 May 2022 15:13:19 +0200 Subject: [PATCH 682/820] Added failing tests for #8178. --- tests/Avalonia.LeakTests/ControlTests.cs | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 3a5a8f1474..09d6764bae 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -6,6 +6,7 @@ using System.Runtime.Remoting.Contexts; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; +using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Input; using Avalonia.Layout; @@ -661,6 +662,70 @@ namespace Avalonia.LeakTests } } + [Fact] + public void ElementName_Binding_In_DataTemplate_Is_Freed() + { + using (Start()) + { + var items = new ObservableCollection(Enumerable.Range(0, 10)); + NameScope ns; + TextBox tb; + ListBox lb; + var window = new Window + { + [NameScope.NameScopeProperty] = ns = new NameScope(), + Width = 100, + Height = 100, + Content = new StackPanel + { + Children = + { + (tb = new TextBox + { + Name = "tb", + Text = "foo", + }), + (lb = new ListBox + { + Items = items, + ItemTemplate = new FuncDataTemplate((_, _) => + new Canvas + { + Width = 10, + Height = 10, + [!Control.TagProperty] = new Binding + { + ElementName = "tb", + Path = "Text", + NameScope = new WeakReference(ns), + } + }) + }), + } + } + }; + + tb.RegisterInNameScope(ns); + + window.Show(); + window.LayoutManager.ExecuteInitialLayoutPass(); + + Assert.Equal(10, lb.ItemContainerGenerator.Containers.Count()); + + var item0 = (ListBoxItem)lb.ItemContainerGenerator.Containers.First().ContainerControl; + var canvas0 = (Canvas)item0.Presenter.Child; + Assert.Equal("foo", canvas0.Tag); + + items.Clear(); + window.LayoutManager.ExecuteLayoutPass(); + + Assert.Empty(lb.ItemContainerGenerator.Containers); + + dotMemory.Check(memory => + Assert.Equal(0, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); + } + } + private IDisposable Start() { return UnitTestApplication.Start(TestServices.StyledWindow.With( @@ -669,6 +734,7 @@ namespace Avalonia.LeakTests inputManager: new InputManager())); } + private class Node { public string Name { get; set; } From 69f5f7a48e695bf54d33ba49d5ae74b7d93be22a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 24 May 2022 15:17:52 +0200 Subject: [PATCH 683/820] Use weak events in AvaloniaProperty plugin. Fixes #8178. --- .../Plugins/AvaloniaPropertyAccessorPlugin.cs | 40 +++++++++++++------ src/Avalonia.Base/Utilities/WeakEvents.cs | 15 ++++++- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs index 3c4120ad0b..cc6d92ceb7 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.ExceptionServices; +using Avalonia.Utilities; namespace Avalonia.Data.Core.Plugins { @@ -60,11 +61,10 @@ namespace Avalonia.Data.Core.Plugins return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); } - private class Accessor : PropertyAccessorBase, IObserver + private class Accessor : PropertyAccessorBase, IWeakEventSubscriber { private readonly WeakReference _reference; private readonly AvaloniaProperty _property; - private IDisposable? _subscription; public Accessor(WeakReference reference, AvaloniaProperty property) { @@ -95,29 +95,45 @@ namespace Avalonia.Data.Core.Plugins return false; } - protected override void SubscribeCore() + void IWeakEventSubscriber. + OnEvent(object? notifyPropertyChanged, WeakEvent ev, AvaloniaPropertyChangedEventArgs e) { - _subscription = Instance?.GetObservable(_property).Subscribe(this); + if (e.Property == _property) + { + SendCurrentValue(); + } } - protected override void UnsubscribeCore() + protected override void SubscribeCore() { - _subscription?.Dispose(); - _subscription = null; + SubscribeToChanges(); + SendCurrentValue(); } - void IObserver.OnCompleted() + protected override void UnsubscribeCore() { + var instance = Instance; + + if (instance != null) + WeakEvents.AvaloniaPropertyChanged.Unsubscribe(instance, this); } - void IObserver.OnError(Exception error) + private void SendCurrentValue() { - ExceptionDispatchInfo.Capture(error).Throw(); + try + { + var value = Value; + PublishValue(value); + } + catch { } } - void IObserver.OnNext(object? value) + private void SubscribeToChanges() { - PublishValue(value); + var instance = Instance; + + if (instance != null) + WeakEvents.AvaloniaPropertyChanged.Subscribe(instance, this); } } } diff --git a/src/Avalonia.Base/Utilities/WeakEvents.cs b/src/Avalonia.Base/Utilities/WeakEvents.cs index d1b5e7f12d..6da899bab2 100644 --- a/src/Avalonia.Base/Utilities/WeakEvents.cs +++ b/src/Avalonia.Base/Utilities/WeakEvents.cs @@ -31,10 +31,23 @@ public class WeakEvents return () => s.PropertyChanged -= handler; }); + + /// + /// Represents PropertyChanged event from + /// + public static readonly WeakEvent + AvaloniaPropertyChanged = WeakEvent.Register( + (s, h) => + { + EventHandler handler = (_, e) => h(s, e); + s.PropertyChanged += handler; + return () => s.PropertyChanged -= handler; + }); + /// /// Represents CanExecuteChanged event from /// public static readonly WeakEvent CommandCanExecuteChanged = WeakEvent.Register((s, h) => s.CanExecuteChanged += h, (s, h) => s.CanExecuteChanged -= h); -} \ No newline at end of file +} From 006aef23886ad3699a8fb68b7fab89d09b1d6ece Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 24 May 2022 19:11:24 +0200 Subject: [PATCH 684/820] Rework hit testing to not rely on cluster values and instead use the currently covered TextSourceLength --- .../Media/TextFormatting/TextBounds.cs | 11 +- .../Media/TextFormatting/TextLayout.cs | 29 +- .../Media/TextFormatting/TextLineImpl.cs | 383 +++++++++++------- .../Media/TextFormatting/TextLineTests.cs | 164 +++++++- 4 files changed, 420 insertions(+), 167 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs b/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs index a0b51671f0..93edf68348 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs @@ -10,20 +10,27 @@ namespace Avalonia.Media.TextFormatting /// /// Constructing TextBounds object /// - internal TextBounds(Rect bounds, FlowDirection flowDirection) + internal TextBounds(Rect bounds, FlowDirection flowDirection, IList runBounds) { Rectangle = bounds; FlowDirection = flowDirection; + TextRunBounds = runBounds; } /// /// Bounds rectangle /// - public Rect Rectangle { get; } + public Rect Rectangle { get; internal set; } /// /// Text flow direction inside the boundary rectangle /// public FlowDirection FlowDirection { get; } + + /// + /// Get a list of run bounding rectangles + /// + /// Array of text run bounds + public IList TextRunBounds { get; } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index 5d5d45db2d..4f7c43a6d1 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs @@ -230,7 +230,7 @@ namespace Avalonia.Media.TextFormatting foreach (var textLine in TextLines) { //Current line isn't covered. - if (textLine.FirstTextSourceIndex + textLine.Length <= start) + if (textLine.FirstTextSourceIndex + textLine.Length < start) { currentY += textLine.Height; @@ -239,18 +239,27 @@ namespace Avalonia.Media.TextFormatting var textBounds = textLine.GetTextBounds(start, length); - foreach (var bounds in textBounds) + if(textBounds.Count > 0) { - Rect? last = result.Count > 0 ? result[result.Count - 1] : null; - - if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY)) + foreach (var bounds in textBounds) { - result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width); + Rect? last = result.Count > 0 ? result[result.Count - 1] : null; + + if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY)) + { + result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width); + } + else + { + result.Add(bounds.Rectangle.WithY(currentY)); + } + + foreach (var runBounds in bounds.TextRunBounds) + { + start += runBounds.Length; + length -= runBounds.Length; + } } - else - { - result.Add(bounds.Rectangle.WithY(currentY)); - } } if(textLine.FirstTextSourceIndex + textLine.Length >= start + length) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 26e73cdf3b..8b5e2cc2ce 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -184,6 +184,10 @@ namespace Avalonia.Media.TextFormatting { characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + var offset = Math.Max(0, currentPosition - shapedRun.Text.Start); + + characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength); + break; } default: @@ -215,9 +219,11 @@ namespace Avalonia.Media.TextFormatting /// public override double GetDistanceFromCharacterHit(CharacterHit characterHit) { - var characterIndex = characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0); + var isTrailingHit = characterHit.TrailingLength > 0; + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; var currentDistance = Start; var currentPosition = FirstTextSourceIndex; + var remainingLength = characterIndex - FirstTextSourceIndex; GlyphRun? lastRun = null; @@ -242,8 +248,10 @@ namespace Avalonia.Media.TextFormatting } //Look for a hit in within the current run - if (characterIndex >= textRun.Text.Start && characterIndex <= textRun.Text.Start + textRun.Text.Length) + if (currentPosition + remainingLength <= currentPosition + textRun.Text.Length) { + characterHit = new CharacterHit(textRun.Text.Start + remainingLength); + var distance = currentRun.GetDistanceFromCharacterHit(characterHit); return currentDistance + distance; @@ -254,28 +262,27 @@ namespace Avalonia.Media.TextFormatting { if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) { - if (characterIndex <= textRun.Text.Start) + if (characterIndex <= currentPosition) { return currentDistance; } } else { - if (characterIndex == textRun.Text.Start) + if (characterIndex == currentPosition) { return currentDistance; } } - if (characterIndex == textRun.Text.Start + textRun.Text.Length && - characterHit.TrailingLength > 0) + if (characterIndex == currentPosition + textRun.Text.Length && isTrailingHit) { return currentDistance + currentRun.Size.Width; } } else { - if (characterIndex == textRun.Text.Start) + if (characterIndex == currentPosition) { return currentDistance + currentRun.Size.Width; } @@ -286,20 +293,24 @@ namespace Avalonia.Media.TextFormatting if (nextRun != null) { - if (characterHit.FirstCharacterIndex == textRun.Text.End && - nextRun.ShapedBuffer.IsLeftToRight) + if (nextRun.ShapedBuffer.IsLeftToRight) { - return currentDistance; + if (characterIndex == currentPosition + textRun.Text.Length) + { + return currentDistance; + } } - - if (characterIndex > textRun.Text.End && nextRun.Text.End < textRun.Text.End) + else { - return currentDistance; + if (currentPosition + nextRun.Text.Length == characterIndex) + { + return currentDistance; + } } } else { - if (characterIndex > textRun.Text.End) + if (characterIndex > currentPosition + textRun.Text.Length) { return currentDistance; } @@ -329,6 +340,12 @@ namespace Avalonia.Media.TextFormatting //No hit hit found so we add the full width currentDistance += textRun.Size.Width; currentPosition += textRun.TextSourceLength; + remainingLength -= textRun.TextSourceLength; + + if (remainingLength <= 0) + { + break; + } } return currentDistance; @@ -394,217 +411,299 @@ namespace Avalonia.Media.TextFormatting return GetPreviousCaretCharacterHit(characterHit); } - public override IReadOnlyList GetTextBounds(int firstTextSourceCharacterIndex, int textLength) + private IReadOnlyList GetTextBoundsLeftToRight(int firstTextSourceIndex, int textLength) { - if (firstTextSourceCharacterIndex + textLength <= FirstTextSourceIndex) - { - return Array.Empty(); - } + var characterIndex = firstTextSourceIndex + textLength; var result = new List(TextRuns.Count); - var lastDirection = _flowDirection; + var lastDirection = FlowDirection.LeftToRight; var currentDirection = lastDirection; + var currentPosition = FirstTextSourceIndex; - var currentRect = Rect.Empty; + var remainingLength = textLength; + var startX = Start; - var runStart = startX; + double currentWidth = 0; + var currentRect = Rect.Empty; - //A portion of the line is covered. for (var index = 0; index < TextRuns.Count; index++) { - var currentRun = TextRuns[index] as DrawableTextRun; - - if (currentRun is null) + if (TextRuns[index] is not DrawableTextRun currentRun) { continue; } - TextRun? nextRun = null; - - if (index + 1 < TextRuns.Count) + if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex) { - nextRun = TextRuns[index + 1]; + startX += currentRun.Size.Width; + + currentPosition += currentRun.TextSourceLength; + + continue; } - if (nextRun != null) + var characterLength = 0; + var endX = startX; + + if (currentRun is ShapedTextCharacters currentShapedRun) { - switch (nextRun) - { - case ShapedTextCharacters when currentRun is ShapedTextCharacters: - { - if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End && _flowDirection == FlowDirection.LeftToRight) - { - goto skip; - } + var offset = Math.Max(0, firstTextSourceIndex - currentPosition); - if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) - { - goto skip; - } + currentPosition += offset; - if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) - { - goto skip; - } + var startIndex = currentRun.Text.Start + offset; - if (currentRun.Text.End < firstTextSourceCharacterIndex) - { - goto skip; - } + var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex + remainingLength) : + new CharacterHit(startIndex)); - goto noop; - } - default: - { - goto noop; - } - } + endX += endOffset; - skip: + var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex) : + new CharacterHit(startIndex + remainingLength)); + + startX += startOffset; + + var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); + + characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength); + + currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ? + FlowDirection.LeftToRight : + FlowDirection.RightToLeft; + } + else + { + if (currentPosition < firstTextSourceIndex) { startX += currentRun.Size.Width; - currentPosition += currentRun.TextSourceLength; } - continue; - - noop: + if (currentPosition + currentRun.TextSourceLength <= characterIndex) { + endX += currentRun.Size.Width; + + characterLength = currentRun.TextSourceLength; } } - var endX = startX; - var endOffset = 0d; + if (endX < startX) + { + (endX, startX) = (startX, endX); + } - switch (currentRun) + //Lines that only contain a linebreak need to be covered here + if(characterLength == 0) { - case ShapedTextCharacters shapedRun: - { - endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( - shapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(firstTextSourceCharacterIndex + textLength) : - new CharacterHit(firstTextSourceCharacterIndex)); + characterLength = NewLineLength; + } - endX += endOffset; + var runwidth = endX - startX; + var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runwidth, Height), currentPosition, characterLength, currentRun); - var startOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( - shapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(firstTextSourceCharacterIndex) : - new CharacterHit(firstTextSourceCharacterIndex + textLength)); + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentWidth + runwidth); - startX += startOffset; + var textBounds = result[result.Count - 1]; - var characterHit = _flowDirection == FlowDirection.LeftToRight ? - shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) : - shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); + textBounds.Rectangle = currentRect; - currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + textBounds.TextRunBounds.Add(currentRunBounds); + } + else + { + currentRect = currentRunBounds.Rectangle; - currentDirection = shapedRun.ShapedBuffer.IsLeftToRight ? - FlowDirection.LeftToRight : - FlowDirection.RightToLeft; + result.Add(new TextBounds(currentRect, currentDirection, new List { currentRunBounds })); + } - if (nextRun is ShapedTextCharacters nextShaped) - { - if (shapedRun.ShapedBuffer.IsLeftToRight == nextShaped.ShapedBuffer.IsLeftToRight) - { - endOffset = nextShaped.GlyphRun.GetDistanceFromCharacterHit( - nextShaped.ShapedBuffer.IsLeftToRight ? - new CharacterHit(firstTextSourceCharacterIndex + textLength) : - new CharacterHit(firstTextSourceCharacterIndex)); + currentWidth += runwidth; + currentPosition += characterLength; - index++; + if (currentDirection == FlowDirection.LeftToRight) + { + if (currentPosition > characterIndex) + { + break; + } + } + else + { + if (currentPosition <= firstTextSourceIndex) + { + break; + } + } - endX += endOffset; + startX = endX; + lastDirection = currentDirection; + remainingLength -= characterLength; - currentRun = nextShaped; + if (remainingLength <= 0) + { + break; + } + } - if (nextShaped.ShapedBuffer.IsLeftToRight) - { - characterHit = nextShaped.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + return result; + } - currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - } - } - } + private IReadOnlyList GetTextBoundsRightToLeft(int firstTextSourceIndex, int textLength) + { + var characterIndex = firstTextSourceIndex + textLength; - break; - } - default: - { - if (currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex + textLength) - { - endX += currentRun.Size.Width; - } + var result = new List(TextRuns.Count); + var lastDirection = FlowDirection.LeftToRight; + var currentDirection = lastDirection; - if (currentPosition < firstTextSourceCharacterIndex) - { - startX += currentRun.Size.Width; - } + var currentPosition = FirstTextSourceIndex; + var remainingLength = textLength; - currentPosition += currentRun.TextSourceLength; + var startX = Start + WidthIncludingTrailingWhitespace; + double currentWidth = 0; + var currentRect = Rect.Empty; - break; - } + for (var index = TextRuns.Count - 1; index >= 0; index--) + { + if (TextRuns[index] is not DrawableTextRun currentRun) + { + continue; } - if (endX < startX) + if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex) { - (endX, startX) = (startX, endX); + startX -= currentRun.Size.Width; + + currentPosition += currentRun.TextSourceLength; + + continue; } - var width = endX - startX; + var characterLength = 0; + var endX = startX; - if (!MathUtilities.IsZero(width)) + if (currentRun is ShapedTextCharacters currentShapedRun) { - if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) - { - currentRect = currentRect.WithWidth(currentRect.Width + width); + var offset = Math.Max(0, firstTextSourceIndex - currentPosition); + + currentPosition += offset; + + var startIndex = currentRun.Text.Start + offset; + + var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex + remainingLength) : + new CharacterHit(startIndex)); + + endX += endOffset - currentShapedRun.Size.Width; + + var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex) : + new CharacterHit(startIndex + remainingLength)); + + startX += startOffset - currentShapedRun.Size.Width; + + var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); - var textBounds = new TextBounds(currentRect, currentDirection); + characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength); - result[result.Count - 1] = textBounds; + currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ? + FlowDirection.LeftToRight : + FlowDirection.RightToLeft; + } + else + { + if (currentPosition + currentRun.TextSourceLength <= characterIndex) + { + endX -= currentRun.Size.Width; } - else + + if (currentPosition < firstTextSourceIndex) { + startX -= currentRun.Size.Width; - currentRect = new Rect(startX, 0, width, Height); + characterLength = currentRun.TextSourceLength; + } + } - result.Add(new TextBounds(currentRect, currentDirection)); + if (endX < startX) + { + (endX, startX) = (startX, endX); + } - } + //Lines that only contain a linebreak need to be covered here + if (characterLength == 0) + { + characterLength = NewLineLength; } + var runWidth = endX - startX; + var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun); + + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentWidth + runWidth); + + var textBounds = result[result.Count - 1]; + + textBounds.Rectangle = currentRect; + + textBounds.TextRunBounds.Add(currentRunBounds); + } + else + { + currentRect = currentRunBounds.Rectangle; + + result.Add(new TextBounds(currentRect, currentDirection, new List { currentRunBounds })); + } + + currentWidth += runWidth; + currentPosition += characterLength; + if (currentDirection == FlowDirection.LeftToRight) { - if (currentPosition > firstTextSourceCharacterIndex + textLength) + if (currentPosition > characterIndex) { break; } - - if (_flowDirection == FlowDirection.RightToLeft) - { - endX += currentRun.Size.Width - endOffset; - } } else { - if (currentPosition <= firstTextSourceCharacterIndex) + if (currentPosition <= firstTextSourceIndex) { break; } - - endX += currentRun.Size.Width - endOffset; } - startX = endX; lastDirection = currentDirection; - runStart += currentRun.Size.Width; + remainingLength -= characterLength; + + if (remainingLength <= 0) + { + break; + } } return result; } + public override IReadOnlyList GetTextBounds(int firstTextSourceIndex, int textLength) + { + if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight) + { + return GetTextBoundsLeftToRight(firstTextSourceIndex, textLength); + } + + return GetTextBoundsRightToLeft(firstTextSourceIndex, textLength); + } + public TextLineImpl FinalizeLine() { _textLineMetrics = CreateLineMetrics(); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index f29dddf86b..a974e06385 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -543,6 +543,98 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_Get_Distance_From_CharacterHit_Mixed_TextBuffer() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new MixedTextBufferTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(10)); + + Assert.Equal(72.01171875, distance); + + distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(20)); + + Assert.Equal(144.0234375, distance); + + distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(30)); + + Assert.Equal(216.03515625, distance); + + distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(40)); + + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, distance); + } + } + + [Fact] + public void Should_Get_TextBounds_From_Mixed_TextBuffer() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var textSource = new MixedTextBufferTextSource(); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var textBounds = textLine.GetTextBounds(0, 10); + + Assert.Equal(1, textBounds.Count); + + Assert.Equal(72.01171875, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(0, 20); + + Assert.Equal(1, textBounds.Count); + + Assert.Equal(144.0234375, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(0, 30); + + Assert.Equal(1, textBounds.Count); + + Assert.Equal(216.03515625, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(0, 40); + + Assert.Equal(1, textBounds.Count); + + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds[0].Rectangle.Width); + } + } + + private class MixedTextBufferTextSource : ITextSource + { + public TextRun? GetTextRun(int textSourceIndex) + { + switch (textSourceIndex) + { + case 0: + return new TextCharacters(new ReadOnlySlice("aaaaaaaaaa".AsMemory()), new GenericTextRunProperties(Typeface.Default)); + case 10: + return new TextCharacters(new ReadOnlySlice("bbbbbbbbbb".AsMemory()), new GenericTextRunProperties(Typeface.Default)); + case 20: + return new TextCharacters(new ReadOnlySlice("cccccccccc".AsMemory()), new GenericTextRunProperties(Typeface.Default)); + case 30: + return new TextCharacters(new ReadOnlySlice("dddddddddd".AsMemory()), new GenericTextRunProperties(Typeface.Default)); + default: + return null; + } + } + } + private class DrawableRunTextSource : ITextSource { const string Text = "_A_A"; @@ -713,7 +805,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } [Fact] - public void Should_Get_TextBounds_BiDi() + public void Should_Get_TextBounds_BiDi_LeftToRight() { using (Start()) { @@ -725,38 +817,84 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var textLine = formatter.FormatLine(textSource, 0, 200, - new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left, true, true, defaultProperties, TextWrapping.NoWrap, 0, 0)); + new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, true, true, defaultProperties, TextWrapping.NoWrap, 0, 0)); - var textBounds = textLine.GetTextBounds(0, text.Length); + var textBounds = textLine.GetTextBounds(0, 3); - Assert.Equal(2, textBounds.Count); - Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + var firstRun = textLine.TextRuns[0] as ShapedTextCharacters; - textBounds = textLine.GetTextBounds(0, 4); + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); + + textBounds = textLine.GetTextBounds(3, 4); var secondRun = textLine.TextRuns[1] as ShapedTextCharacters; Assert.Equal(1, textBounds.Count); Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); - textBounds = textLine.GetTextBounds(4, 3); + textBounds = textLine.GetTextBounds(0, 4); - var firstRun = textLine.TextRuns[0] as ShapedTextCharacters; + Assert.Equal(2, textBounds.Count); + + Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + + Assert.Equal(7.201171875, textBounds[1].Rectangle.Width); + + Assert.Equal(firstRun.Size.Width, textBounds[1].Rectangle.Left); + + textBounds = textLine.GetTextBounds(0, text.Length); + + Assert.Equal(2, textBounds.Count); + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + } + } + + [Fact] + public void Should_Get_TextBounds_BiDi_RightToLeft() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var text = "אאא AAA"; + var textSource = new SingleBufferTextSource(text, defaultProperties); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, 200, + new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left, true, true, defaultProperties, TextWrapping.NoWrap, 0, 0)); + + var textBounds = textLine.GetTextBounds(0, 4); + + var firstRun = textLine.TextRuns[1] as ShapedTextCharacters; Assert.Equal(1, textBounds.Count); Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); + textBounds = textLine.GetTextBounds(4, 3); + + var secondRun = textLine.TextRuns[0] as ShapedTextCharacters; + + Assert.Equal(1, textBounds.Count); + + Assert.Equal(3, textBounds[0].TextRunBounds.Sum(x=> x.Length)); + Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); + textBounds = textLine.GetTextBounds(0, 5); Assert.Equal(2, textBounds.Count); + Assert.Equal(5, textBounds.Sum(x=> x.TextRunBounds.Sum(x => x.Length))); - Assert.Equal(7.201171875, textBounds[0].Rectangle.Width); - - Assert.Equal(textLine.Start, textBounds[0].Rectangle.Left); + Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + Assert.Equal(7.201171875, textBounds[1].Rectangle.Width); + Assert.Equal(textLine.Start + 7.201171875, textBounds[1].Rectangle.Right); - Assert.Equal(secondRun.Size.Width, textBounds[1].Rectangle.Width); + textBounds = textLine.GetTextBounds(0, text.Length); - Assert.Equal(textLine.Start + firstRun.Size.Width, textBounds[1].Rectangle.Left); + Assert.Equal(2, textBounds.Count); + Assert.Equal(7, textBounds.Sum(x => x.TextRunBounds.Sum(x => x.Length))); + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); } } From e4bbebac7d0e1f97f9864439f67e50e797110a85 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:48:19 +0100 Subject: [PATCH 685/820] restore _canBecomeKeyWindow flag, important to distinguish behavior of a window and a popup. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 34 +++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 54bfe6e38a..7c1f548786 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -31,6 +31,7 @@ ComPtr _parent; bool _closed; bool _isEnabled; + bool _canBecomeKeyWindow; bool _isExtended; AvnMenu* _menu; } @@ -216,29 +217,38 @@ -(BOOL)canBecomeKeyWindow { - // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) + if(_canBecomeKeyWindow) { - if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)]) + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + for(NSWindow* uch in [self childWindows]) { - continue; - } + if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)]) + { + continue; + } - id ch = (id ) uch; + id ch = (id ) uch; - return !ch.isDialog; - } + if(ch.isDialog) + return true; + } - return true; + return true; + } + + return false; } +#ifndef IS_NSPANEL -(BOOL)canBecomeMainWindow { -#ifdef IS_NSPANEL - return false; -#else return true; +} #endif + +-(void)setCanBecomeKeyWindow:(bool)value +{ + _canBecomeKeyWindow = value; } -(bool)shouldTryToHandleEvents From aa06e02c6d8bdd775350926da702d9fb29206c22 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:48:41 +0100 Subject: [PATCH 686/820] arrange fields nicely. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 39 ++++++++++--------- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 3 -- native/Avalonia.Native/src/OSX/WindowImpl.h | 3 ++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 + 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index e3e646ff2a..a879fecc21 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -16,8 +16,6 @@ class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, public INSWindowHolder { -private: - NSCursor *cursor; public: FORWARD_IUNKNOWN() @@ -28,23 +26,6 @@ BEGIN_INTERFACE_MAP() virtual ~WindowBaseImpl(); - AutoFitContentView *StandardContainer; - AvnView *View; - NSWindow * Window; - ComPtr BaseEvents; - ComPtr _glContext; - NSObject *renderTarget; - AvnPoint lastPositionSet; - bool hasPosition; - NSSize lastSize; - NSSize lastMinSize; - NSSize lastMaxSize; - AvnMenu* lastMenu; - NSString *_lastTitle; - - bool _shown; - bool _inResize; - WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); virtual HRESULT ObtainNSWindowHandle(void **ret) override; @@ -128,6 +109,26 @@ private: void CreateNSWindow (bool isDialog); void CleanNSWindow (); void InitialiseNSWindow (); + + NSCursor *cursor; + ComPtr _glContext; + bool hasPosition; + NSSize lastSize; + NSSize lastMinSize; + NSSize lastMaxSize; + AvnMenu* lastMenu; + bool _inResize; + +protected: + AvnPoint lastPositionSet; + AutoFitContentView *StandardContainer; + bool _shown; + +public: + NSObject *renderTarget; + NSWindow * Window; + ComPtr BaseEvents; + AvnView *View; }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bc512f80d8..c97989556f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -35,7 +35,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastSize = NSSize { 100, 100 }; lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMinSize = NSSize { 0, 0 }; - _lastTitle = @""; Window = nullptr; lastMenu = nullptr; @@ -102,8 +101,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { UpdateStyle(); - [Window setTitle:_lastTitle]; - if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index a4ee4f447c..dc82ee59c2 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -91,6 +91,9 @@ BEGIN_INTERFACE_MAP() protected: virtual NSWindowStyleMask GetStyle() override; + +private: + NSString *_lastTitle; }; #endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 5b15b4cdfc..5bb739d369 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -19,6 +19,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _inSetWindowState = false; _lastWindowState = Normal; _actualWindowState = Normal; + _lastTitle = @""; WindowEvents = events; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; From a77f9d3e54d580a41290654b4872064b053bf1f6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:49:42 +0100 Subject: [PATCH 687/820] add setCanBecomeKeyWindow to protocol --- native/Avalonia.Native/src/OSX/WindowProtocol.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 92194706de..0e5c5869e7 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -22,4 +22,6 @@ -(void) setIsExtended:(bool)value; -(void) disconnectParent; -(bool) isDialog; -@end \ No newline at end of file + +-(void) setCanBecomeKeyWindow:(bool)value; +@end From 02b3bf253af52c3c08ade06fba827dce7d3cf93b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:50:39 +0100 Subject: [PATCH 688/820] add OnInitialiseNSWindow to allow inheritors to hook into NSWindow/Panel setup before being shown. --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 8 +++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 ++ .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 7 +++++ native/Avalonia.Native/src/OSX/WindowImpl.h | 2 ++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 29 ++++++++++--------- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index cb52047148..3c5afd9424 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -26,13 +26,17 @@ private: PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; - [Window setLevel:NSPopUpMenuWindowLevel]; } protected: virtual NSWindowStyleMask GetStyle() override { return NSWindowStyleMaskBorderless; } + + virtual void OnInitialiseNSWindow () override + { + [Window setLevel:NSPopUpMenuWindowLevel]; + } public: virtual bool ShouldTakeFocusOnShow() override @@ -54,4 +58,4 @@ extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); return ptr; } -} \ No newline at end of file +} diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index a879fecc21..83850e780c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -104,6 +104,8 @@ protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); + + virtual void OnInitialiseNSWindow (); private: void CreateNSWindow (bool isDialog); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index c97989556f..022769bad0 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -567,6 +567,11 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } +void WindowBaseImpl::OnInitialiseNSWindow() +{ + +} + void WindowBaseImpl::InitialiseNSWindow() { if(Window != nullptr) { [Window setContentView:StandardContainer]; @@ -586,6 +591,8 @@ void WindowBaseImpl::InitialiseNSWindow() { [GetWindowProtocol() showWindowMenuWithAppMenu]; } } + + OnInitialiseNSWindow(); } } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index dc82ee59c2..db19497b29 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -88,6 +88,8 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetWindowState (AvnWindowState state) override; virtual bool IsDialog() override; + + virtual void OnInitialiseNSWindow() override; protected: virtual NSWindowStyleMask GetStyle() override; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 5bb739d369..d43a8beee4 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -21,9 +21,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _actualWindowState = Normal; _lastTitle = @""; WindowEvents = events; - [Window disableCursorRects]; - [Window setTabbingMode:NSWindowTabbingModeDisallowed]; - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } void WindowImpl::HideOrShowTrafficLights() { @@ -51,25 +48,29 @@ void WindowImpl::HideOrShowTrafficLights() { } } +void WindowImpl::OnInitialiseNSWindow(){ + [GetWindowProtocol() setCanBecomeKeyWindow:true]; + [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + + [Window setTitle:_lastTitle]; + + if(_isClientAreaExtended) + { + [GetWindowProtocol() setIsExtended:true]; + SetExtendClientArea(true); + } +} + HRESULT WindowImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { _isDialog = isDialog; - bool created = Window == nullptr; - WindowBaseImpl::Show(activate, isDialog); - if(created) - { - if(_isClientAreaExtended) - { - [GetWindowProtocol() setIsExtended:true]; - SetExtendClientArea(true); - } - } - HideOrShowTrafficLights(); return SetWindowState(_lastWindowState); From 30377966e6b0623df63b7f620cd5dcc39de7ac90 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 21:02:52 +0100 Subject: [PATCH 689/820] fix incorrect return value. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 7c1f548786..f51c693777 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -230,7 +230,7 @@ id ch = (id ) uch; if(ch.isDialog) - return true; + return false; } return true; From 8e4d904a5af3d71bfdaee77daf2f319961f4b619 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 22:49:09 -0400 Subject: [PATCH 690/820] Simplify some runtime platform interfaces and prefer net6 alternatives --- .../Platform/Interop/IDynamicLibraryLoader.cs | 5 + src/Avalonia.PlatformSupport/DynLoader.cs | 56 ++++--- .../Internal/AssemblyDescriptorResolver.cs | 4 +- .../StandardRuntimePlatform.cs | 146 +++--------------- .../StandardRuntimePlatformServices.cs | 11 +- 5 files changed, 65 insertions(+), 157 deletions(-) diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs index 9389ebc703..d9db4b71d5 100644 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs @@ -16,5 +16,10 @@ namespace Avalonia.Platform.Interop { } + + public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException) + { + + } } } diff --git a/src/Avalonia.PlatformSupport/DynLoader.cs b/src/Avalonia.PlatformSupport/DynLoader.cs index ad76ac4724..ef2166d943 100644 --- a/src/Avalonia.PlatformSupport/DynLoader.cs +++ b/src/Avalonia.PlatformSupport/DynLoader.cs @@ -26,25 +26,6 @@ namespace Avalonia.PlatformSupport } } - static class AndroidImports - { - [DllImport("libdl.so")] - private static extern IntPtr dlopen(string path, int flags); - - [DllImport("libdl.so")] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport("libdl.so")] - private static extern IntPtr dlerror(); - - public static void Init() - { - DlOpen = dlopen; - DlSym = dlsym; - DlError = dlerror; - } - } - static class OsXImports { [DllImport("/usr/lib/libSystem.dylib")] @@ -77,10 +58,6 @@ namespace Avalonia.PlatformSupport Marshal.FreeHGlobal(buffer); if (unixName == "Darwin") OsXImports.Init(); -#if NET6_0_OR_GREATER - else if (OperatingSystem.IsAndroid()) - AndroidImports.Init(); -#endif else LinuxImports.Init(); } @@ -135,6 +112,39 @@ namespace Avalonia.PlatformSupport return ptr; } } + +#if NET6_0_OR_GREATER + internal class Net6Loader : IDynamicLibraryLoader + { + public IntPtr LoadLibrary(string dll) + { + try + { + return NativeLibrary.Load(dll); + } + catch (Exception ex) + { + throw new DynamicLibraryLoaderException("Error loading " + dll, ex); + } + } + + public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) + { + try + { + if (optional) + { + return NativeLibrary.TryGetExport(dll, proc, out var address) ? address : default; + } + return NativeLibrary.GetExport(dll, proc); + } + catch (Exception ex) + { + throw new DynamicLibraryLoaderException("Error " + dll, ex); + } + } + } +#endif internal class NotSupportedLoader : IDynamicLibraryLoader { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs index 28ae35d57d..6b85200c76 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace Avalonia.PlatformSupport.Internal; @@ -29,9 +30,8 @@ internal class AssemblyDescriptorResolver: IAssemblyDescriptorResolver } else { - // iOS does not support loading assemblies dynamically! #if NET6_0_OR_GREATER - if (OperatingSystem.IsIOS()) + if (!RuntimeFeature.IsDynamicCodeSupported) { throw new InvalidOperationException( $"Assembly {name} needs to be referenced and explicitly loaded before loading resources"); diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs b/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs index 4eeb9232cf..048f09570f 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Avalonia.Platform; @@ -14,45 +12,20 @@ namespace Avalonia.PlatformSupport return new Timer(_ => tick(), null, interval, interval); } - public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size); + public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); private class UnmanagedBlob : IUnmanagedBlob { - private readonly StandardRuntimePlatform _plat; private IntPtr _address; private readonly object _lock = new object(); -#if DEBUG - private static readonly List Backtraces = new List(); - private static Thread? GCThread; - private readonly string _backtrace; - private static readonly object _btlock = new object(); - class GCThreadDetector - { - ~GCThreadDetector() - { - GCThread = Thread.CurrentThread; - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static void Spawn() => new GCThreadDetector(); - - static UnmanagedBlob() - { - Spawn(); - GC.WaitForPendingFinalizers(); - } -#endif - - public UnmanagedBlob(StandardRuntimePlatform plat, int size) + public UnmanagedBlob(int size) { try { if (size <= 0) throw new ArgumentException("Positive number required", nameof(size)); - _plat = plat; - _address = plat.Alloc(size); + _address = Marshal.AllocHGlobal(size); GC.AddMemoryPressure(size); Size = size; } @@ -61,24 +34,15 @@ namespace Avalonia.PlatformSupport GC.SuppressFinalize(this); throw; } -#if DEBUG - _backtrace = Environment.StackTrace; - lock (_btlock) - Backtraces.Add(_backtrace); -#endif } - void DoDispose() + private void DoDispose() { lock (_lock) { if (!IsDisposed) { -#if DEBUG - lock (_btlock) - Backtraces.Remove(_backtrace); -#endif - _plat?.Free(_address, Size); + Marshal.FreeHGlobal(_address); GC.RemoveMemoryPressure(Size); IsDisposed = true; _address = IntPtr.Zero; @@ -89,29 +53,12 @@ namespace Avalonia.PlatformSupport public void Dispose() { -#if DEBUG - if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) - { - lock (_lock) - { - if (!IsDisposed) - { - Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " - + Environment.StackTrace - + "\n\nBlob created by " + _backtrace); - } - } - } -#endif DoDispose(); GC.SuppressFinalize(this); } ~UnmanagedBlob() { -#if DEBUG - Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); -#endif DoDispose(); } @@ -119,82 +66,25 @@ namespace Avalonia.PlatformSupport public int Size { get; private set; } public bool IsDisposed { get; private set; } } - -#if NET461 || NETCOREAPP2_0_OR_GREATER - [DllImport("libc", SetLastError = true)] - private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); - [DllImport("libc", SetLastError = true)] - private static extern int munmap(IntPtr addr, IntPtr length); - [DllImport("libc", SetLastError = true)] - private static extern long sysconf(int name); - - private bool? _useMmap; - private bool UseMmap - => _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value; - - IntPtr Alloc(int size) - { - if (UseMmap) - { - var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); - if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) - { - var errno = Marshal.GetLastWin32Error(); - throw new Exception("Unable to allocate memory: " + errno); - } - return rv; - } - else - return Marshal.AllocHGlobal(size); - } - - void Free(IntPtr ptr, int len) - { - if (UseMmap) - { - if (munmap(ptr, new IntPtr(len)) == -1) - { - var errno = Marshal.GetLastWin32Error(); - throw new Exception("Unable to free memory: " + errno); - } - } - else - Marshal.FreeHGlobal(ptr); - } -#else - IntPtr Alloc(int size) => Marshal.AllocHGlobal(size); - void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr); -#endif - - private static readonly Lazy Info = new Lazy(() => + + private static readonly Lazy Info = new(() => { OperatingSystemType os; -#if NET5_0_OR_GREATER - if (OperatingSystem.IsWindows()) - os = OperatingSystemType.WinNT; - else if (OperatingSystem.IsMacOS()) - os = OperatingSystemType.OSX; - else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) - os = OperatingSystemType.Linux; - else if (OperatingSystem.IsAndroid()) - os = OperatingSystemType.Android; - else if (OperatingSystem.IsIOS()) - os = OperatingSystemType.iOS; - else if (OperatingSystem.IsBrowser()) - os = OperatingSystemType.Browser; - else - throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); -#else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) os = OperatingSystemType.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"))) os = OperatingSystemType.Linux; else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) os = OperatingSystemType.WinNT; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android"))) + os = OperatingSystemType.Android; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("iOS"))) + os = OperatingSystemType.iOS; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Browser"))) + os = OperatingSystemType.Browser; else throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); -#endif return new RuntimePlatformInfo { @@ -203,10 +93,10 @@ namespace Avalonia.PlatformSupport #elif NETFRAMEWORK IsDotNetFramework = true, #endif - IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT, - IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser, - IsMobile = os == OperatingSystemType.Android || os == OperatingSystemType.iOS, - IsUnix = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.Android, + IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT, + IsMono = os is OperatingSystemType.Android or OperatingSystemType.iOS or OperatingSystemType.Browser, + IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS, + IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android, IsBrowser = os == OperatingSystemType.Browser, OperatingSystem = os, }; diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs b/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs index ae7478feb9..11ca906782 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs @@ -9,22 +9,25 @@ namespace Avalonia.PlatformSupport public static void Register(Assembly? assembly = null) { var standardPlatform = new StandardRuntimePlatform(); - var os = standardPlatform.GetRuntimeInfo().OperatingSystem; AssetLoader.RegisterResUriParsers(); AvaloniaLocator.CurrentMutable .Bind().ToConstant(standardPlatform) .Bind().ToConstant(new AssetLoader(assembly)) .Bind().ToConstant( - os switch +#if NET6_0_OR_GREATER + new Net6Loader() +#else + standardPlatform.GetRuntimeInfo().OperatingSystem switch { - OperatingSystemType.WinNT => new Win32Loader(), + OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(), OperatingSystemType.OSX => new UnixLoader(), OperatingSystemType.Linux => new UnixLoader(), OperatingSystemType.Android => new UnixLoader(), // iOS, WASM, ... - _ => (IDynamicLibraryLoader)new NotSupportedLoader() + _ => new NotSupportedLoader() } +#endif ); } } From ad0a06bf24732d0d7505951184bfc89e936d53b7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 23:35:32 -0400 Subject: [PATCH 691/820] Merge PlatformSupport project into Avalonia.Base --- Avalonia.sln | 53 ------------------- build/CoreLibraries.props | 1 - src/Avalonia.Base/Media/PathGeometry.cs | 1 - .../Media/PathGeometryCollections.cs | 2 +- .../Platform}/AssetLoader.cs | 5 +- .../Platform}/Internal/AssemblyDescriptor.cs | 2 +- .../Internal/AssemblyDescriptorResolver.cs | 2 +- .../Platform}/Internal/AssetDescriptor.cs | 2 +- .../Platform}/Internal/Constants.cs | 2 +- .../Platform/Internal}/DynLoader.cs | 2 +- .../Platform}/Internal/SlicedStream.cs | 2 +- .../Platform/PathGeometryContext.cs | 3 +- .../Platform}/StandardRuntimePlatform.cs | 3 +- .../StandardRuntimePlatformServices.cs | 4 +- src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 - .../AppBuilder.cs | 2 +- src/Avalonia.Controls/AppBuilderBase.cs | 36 ++----------- .../Avalonia.Web.Blazor.csproj | 1 - .../AvaloniaBlazorAppBuilder.cs | 1 - .../AssetLoaderTests.cs | 5 +- .../Media/PathMarkupParserTests.cs | 1 - .../Styling/ResourceBenchmarks.cs | 1 - .../Themes/FluentBenchmark.cs | 1 - .../Themes/ThemeBenchmark.cs | 2 +- tests/Avalonia.RenderTests/TestBase.cs | 2 - .../Avalonia.UnitTests.csproj | 1 - tests/Avalonia.UnitTests/TestServices.cs | 1 - .../Avalonia.UnitTests/UnitTestApplication.cs | 1 - 28 files changed, 22 insertions(+), 118 deletions(-) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/AssetLoader.cs (98%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/Internal/AssemblyDescriptor.cs (97%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/Internal/AssemblyDescriptorResolver.cs (96%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/Internal/AssetDescriptor.cs (96%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/Internal/Constants.cs (69%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform/Internal}/DynLoader.cs (99%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/Internal/SlicedStream.cs (97%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/StandardRuntimePlatform.cs (98%) rename src/{Avalonia.PlatformSupport => Avalonia.Base/Platform}/StandardRuntimePlatformServices.cs (95%) rename src/{Avalonia.PlatformSupport => Avalonia.Controls}/AppBuilder.cs (94%) rename tests/{Avalonia.PlatformSupport.UnitTests => Avalonia.Base.UnitTests}/AssetLoaderTests.cs (95%) diff --git a/Avalonia.sln b/Avalonia.sln index c8e513f94c..1df0209b8d 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -209,12 +209,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "sampl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" @@ -1845,30 +1841,6 @@ Global {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.Build.0 = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.Build.0 = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.ActiveCfg = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.Build.0 = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -1893,30 +1865,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 - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhone.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|Any CPU.Build.0 = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.ActiveCfg = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -2046,7 +1994,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} - {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index 9448a31d73..00a1e3094b 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -8,6 +8,5 @@ - diff --git a/src/Avalonia.Base/Media/PathGeometry.cs b/src/Avalonia.Base/Media/PathGeometry.cs index 2c8a51c541..8662c3351d 100644 --- a/src/Avalonia.Base/Media/PathGeometry.cs +++ b/src/Avalonia.Base/Media/PathGeometry.cs @@ -2,7 +2,6 @@ using System; using Avalonia.Collections; using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.Visuals.Platform; namespace Avalonia.Media { diff --git a/src/Avalonia.Base/Media/PathGeometryCollections.cs b/src/Avalonia.Base/Media/PathGeometryCollections.cs index 1165b192a7..c663b1ebe5 100644 --- a/src/Avalonia.Base/Media/PathGeometryCollections.cs +++ b/src/Avalonia.Base/Media/PathGeometryCollections.cs @@ -1,5 +1,5 @@ using Avalonia.Collections; -using Avalonia.Visuals.Platform; +using Avalonia.Platform; namespace Avalonia.Media { diff --git a/src/Avalonia.PlatformSupport/AssetLoader.cs b/src/Avalonia.Base/Platform/AssetLoader.cs similarity index 98% rename from src/Avalonia.PlatformSupport/AssetLoader.cs rename to src/Avalonia.Base/Platform/AssetLoader.cs index 0e33c3d4c7..a74da2a178 100644 --- a/src/Avalonia.PlatformSupport/AssetLoader.cs +++ b/src/Avalonia.Base/Platform/AssetLoader.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using Avalonia.Platform; -using Avalonia.PlatformSupport.Internal; +using Avalonia.Platform.Internal; using Avalonia.Utilities; -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform { /// /// Loads assets compiled into the application binary. diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs similarity index 97% rename from src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs rename to src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs index 64ffec8482..df2a26ddd3 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs +++ b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Reflection; using Avalonia.Utilities; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal interface IAssemblyDescriptor { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs similarity index 96% rename from src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs rename to src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs index 6b85200c76..b12130b1f7 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal interface IAssemblyDescriptorResolver { diff --git a/src/Avalonia.PlatformSupport/Internal/AssetDescriptor.cs b/src/Avalonia.Base/Platform/Internal/AssetDescriptor.cs similarity index 96% rename from src/Avalonia.PlatformSupport/Internal/AssetDescriptor.cs rename to src/Avalonia.Base/Platform/Internal/AssetDescriptor.cs index baae1f99e7..52f8f5e68d 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssetDescriptor.cs +++ b/src/Avalonia.Base/Platform/Internal/AssetDescriptor.cs @@ -2,7 +2,7 @@ using System.IO; using System.Reflection; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal interface IAssetDescriptor { diff --git a/src/Avalonia.PlatformSupport/Internal/Constants.cs b/src/Avalonia.Base/Platform/Internal/Constants.cs similarity index 69% rename from src/Avalonia.PlatformSupport/Internal/Constants.cs rename to src/Avalonia.Base/Platform/Internal/Constants.cs index c8a0f7b1ce..cad864e7e3 100644 --- a/src/Avalonia.PlatformSupport/Internal/Constants.cs +++ b/src/Avalonia.Base/Platform/Internal/Constants.cs @@ -1,4 +1,4 @@ -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal static class Constants { diff --git a/src/Avalonia.PlatformSupport/DynLoader.cs b/src/Avalonia.Base/Platform/Internal/DynLoader.cs similarity index 99% rename from src/Avalonia.PlatformSupport/DynLoader.cs rename to src/Avalonia.Base/Platform/Internal/DynLoader.cs index ef2166d943..07903669b1 100644 --- a/src/Avalonia.PlatformSupport/DynLoader.cs +++ b/src/Avalonia.Base/Platform/Internal/DynLoader.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using Avalonia.Platform.Interop; // ReSharper disable InconsistentNaming -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform.Internal { class UnixLoader : IDynamicLibraryLoader { diff --git a/src/Avalonia.PlatformSupport/Internal/SlicedStream.cs b/src/Avalonia.Base/Platform/Internal/SlicedStream.cs similarity index 97% rename from src/Avalonia.PlatformSupport/Internal/SlicedStream.cs rename to src/Avalonia.Base/Platform/Internal/SlicedStream.cs index e310db964a..124c248aa8 100644 --- a/src/Avalonia.PlatformSupport/Internal/SlicedStream.cs +++ b/src/Avalonia.Base/Platform/Internal/SlicedStream.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal class SlicedStream : Stream { diff --git a/src/Avalonia.Base/Platform/PathGeometryContext.cs b/src/Avalonia.Base/Platform/PathGeometryContext.cs index 694e9f8d80..6c0bbe0f3f 100644 --- a/src/Avalonia.Base/Platform/PathGeometryContext.cs +++ b/src/Avalonia.Base/Platform/PathGeometryContext.cs @@ -1,9 +1,8 @@ using System; using System.Diagnostics.CodeAnalysis; using Avalonia.Media; -using Avalonia.Platform; -namespace Avalonia.Visuals.Platform +namespace Avalonia.Platform { public class PathGeometryContext : IGeometryContext { diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs similarity index 98% rename from src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs rename to src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index 048f09570f..fdbd48a9f8 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -1,9 +1,8 @@ using System; using System.Runtime.InteropServices; using System.Threading; -using Avalonia.Platform; -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform { public class StandardRuntimePlatform : IRuntimePlatform { diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs similarity index 95% rename from src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs rename to src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index 11ca906782..65d6733399 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -1,8 +1,8 @@ using System.Reflection; -using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Platform.Interop; -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform { public static class StandardRuntimePlatformServices { diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 2c40c768f5..c8368e6d7a 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -26,7 +26,6 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.PlatformSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.PlatformSupport/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs similarity index 94% rename from src/Avalonia.PlatformSupport/AppBuilder.cs rename to src/Avalonia.Controls/AppBuilder.cs index 136f1f39b3..5bcd87162e 100644 --- a/src/Avalonia.PlatformSupport/AppBuilder.cs +++ b/src/Avalonia.Controls/AppBuilder.cs @@ -1,5 +1,5 @@ using Avalonia.Controls; -using Avalonia.PlatformSupport; +using Avalonia.Platform; namespace Avalonia { diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 8779ae9122..6b7101cd49 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Reflection; using System.Linq; using Avalonia.Controls.ApplicationLifetimes; @@ -120,38 +119,8 @@ namespace Avalonia.Controls return Self; } - /// - /// Starts the application with an instance of . - /// - /// The window type. - /// A delegate that will be called to create a data context for the window (optional). - [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] - public void Start(Func? dataContextProvider = null) - where TMainWindow : Window, new() - { - AfterSetup(builder => - { - var window = new TMainWindow(); - if (dataContextProvider != null) - window.DataContext = dataContextProvider(); - ((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!) - .MainWindow = window; - }); - - // Copy-pasted because we can't call extension methods due to generic constraints - var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = ShutdownMode.OnMainWindowClose}; - SetupWithLifetime(lifetime); - lifetime.Start(Array.Empty()); - } - public delegate void AppMainDelegate(Application app, string[] args); - - [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] - public void Start() - { - throw new NotSupportedException(); - } - + public void Start(AppMainDelegate main, string[] args) { Setup(); @@ -234,6 +203,9 @@ namespace Avalonia.Controls protected virtual bool CheckSetup => true; + /// + /// Searches and initiates modules included with attribute. + /// private void SetupAvaloniaModules() { var moduleInitializers = from assembly in AppDomain.CurrentDomain.GetAssemblies() diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj index 98fdccfe83..1531c95830 100644 --- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj +++ b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj @@ -51,7 +51,6 @@ - diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs index 2eb340406b..11d9bcc98f 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs @@ -1,6 +1,5 @@ using Avalonia.Controls; using Avalonia.Platform; -using Avalonia.PlatformSupport; namespace Avalonia.Web.Blazor { diff --git a/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs b/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs similarity index 95% rename from tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs rename to tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs index dfd195073b..06b7a4ce94 100644 --- a/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs +++ b/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs @@ -1,10 +1,11 @@ using System; using System.Reflection; -using Avalonia.PlatformSupport.Internal; +using Avalonia.Platform; +using Avalonia.Platform.Internal; using Moq; using Xunit; -namespace Avalonia.PlatformSupport.UnitTests; +namespace Avalonia.Base.UnitTests; public class AssetLoaderTests { diff --git a/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs index c829690eb4..73e97bf13c 100644 --- a/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs @@ -2,7 +2,6 @@ using System.Globalization; using System.IO; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Visuals.Platform; using Moq; using Xunit; diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index 2a048beefa..b16e891924 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -1,7 +1,6 @@ using System; using Avalonia.Controls; using Avalonia.Platform; -using Avalonia.PlatformSupport; using Avalonia.Styling; using Avalonia.UnitTests; using BenchmarkDotNet.Attributes; diff --git a/tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs b/tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs index 6f04bb5206..ceb015ee63 100644 --- a/tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs @@ -1,7 +1,6 @@ using System; using Avalonia.Controls; using Avalonia.Platform; -using Avalonia.PlatformSupport; using Avalonia.Styling; using Avalonia.UnitTests; using BenchmarkDotNet.Attributes; diff --git a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs index 81264f109c..9a5b49790d 100644 --- a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml.Styling; -using Avalonia.PlatformSupport; +using Avalonia.Platform; using Avalonia.Styling; using Avalonia.UnitTests; diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 523876500f..39250f2aa7 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -25,8 +25,6 @@ namespace Avalonia.Skia.RenderTests namespace Avalonia.Direct2D1.RenderTests #endif { - using Avalonia.PlatformSupport; - public class TestBase { #if AVALONIA_SKIA diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index f54ccaa857..52ef23c966 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -14,7 +14,6 @@ - diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index 1d55b77aab..c1be745aca 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -5,7 +5,6 @@ using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.PlatformSupport; using Avalonia.Styling; using Avalonia.Themes.Default; using Avalonia.Rendering; diff --git a/tests/Avalonia.UnitTests/UnitTestApplication.cs b/tests/Avalonia.UnitTests/UnitTestApplication.cs index bb6e53d74f..63c2832b92 100644 --- a/tests/Avalonia.UnitTests/UnitTestApplication.cs +++ b/tests/Avalonia.UnitTests/UnitTestApplication.cs @@ -10,7 +10,6 @@ using System.Reactive.Disposables; using System.Reactive.Concurrency; using Avalonia.Input.Platform; using Avalonia.Animation; -using Avalonia.PlatformSupport; namespace Avalonia.UnitTests { From 2538d2cde662d601bd3d2af436443c25ae752043 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 23:36:05 -0400 Subject: [PATCH 692/820] Copy runtime detection from dotnet/runtime repository --- .../Platform/StandardRuntimePlatform.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index fdbd48a9f8..3acb5f6330 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -85,15 +85,18 @@ namespace Avalonia.Platform else throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); + // Source: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs + var isCoreClr = Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); + var isMonoRuntime = Type.GetType("Mono.RuntimeStructs") != null; + var isFramework = !isCoreClr && RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); + return new RuntimePlatformInfo { -#if NETCOREAPP - IsCoreClr = true, -#elif NETFRAMEWORK - IsDotNetFramework = true, -#endif + IsCoreClr = isCoreClr, + IsDotNetFramework = isFramework, + IsMono = isMonoRuntime, + IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT, - IsMono = os is OperatingSystemType.Android or OperatingSystemType.iOS or OperatingSystemType.Browser, IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS, IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android, IsBrowser = os == OperatingSystemType.Browser, From 7fee2359dce9e8cb12f97400ec6e7f4319fb8ccf Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 23:42:36 -0400 Subject: [PATCH 693/820] Remove Ad-Hoc/AppStore/iPhone/iPhoneSimulator configurations --- Avalonia.sln | 1431 -------------------------------------------------- 1 file changed, 1431 deletions(-) diff --git a/Avalonia.sln b/Avalonia.sln index 1df0209b8d..04aad99211 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -217,1726 +217,296 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\D EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Ad-Hoc|Any CPU = Ad-Hoc|Any CPU - Ad-Hoc|iPhone = Ad-Hoc|iPhone - Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator - AppStore|Any CPU = AppStore|Any CPU - AppStore|iPhone = AppStore|iPhone - AppStore|iPhoneSimulator = AppStore|iPhoneSimulator Debug|Any CPU = Debug|Any CPU - Debug|iPhone = Debug|iPhone - Debug|iPhoneSimulator = Debug|iPhoneSimulator Release|Any CPU = Release|Any CPU - Release|iPhone = Release|iPhone - Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhone.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhone.Build.0 = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhone.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhone.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|Any CPU.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhone.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|Any CPU.Build.0 = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhone.Build.0 = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.ActiveCfg = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhone.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhone.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhone.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhone.Build.0 = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhone.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhone.Build.0 = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhone.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhone.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhone.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhone.Build.0 = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|Any CPU.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhone.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhone.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhone.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhone.Build.0 = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhone.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhone.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhone.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhone.Build.0 = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|Any CPU.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhone.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhone.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|Any CPU.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhone.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhone.Build.0 = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.ActiveCfg = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhone.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhone.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhone.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhone.Build.0 = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.ActiveCfg = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhone.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhone.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|Any CPU.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhone.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhone.Build.0 = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhone.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhone.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|Any CPU.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhone.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhone.Build.0 = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|Any CPU.ActiveCfg = Release|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|Any CPU.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhone.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhone.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhone.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhone.Build.0 = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|Any CPU.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhone.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhone.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhone.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhone.Build.0 = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.ActiveCfg = Release|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhone.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhone.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhone.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhone.Build.0 = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Release|Any CPU.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhone.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhone.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|Any CPU.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhone.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhone.Build.0 = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|Any CPU.ActiveCfg = Release|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|Any CPU.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhone.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhone.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|Any CPU.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhone.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhone.Build.0 = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|Any CPU.ActiveCfg = Release|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|Any CPU.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhone.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhone.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.Build.0 = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhone.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhone.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhone.Build.0 = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|Any CPU.ActiveCfg = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|Any CPU.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhone.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhone.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|Any CPU.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhone.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhone.Build.0 = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|Any CPU.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhone.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhone.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.Build.0 = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.ActiveCfg = Release|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhone.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhone.Build.0 = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|Any CPU.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.Build.0 = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhone.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhone.Build.0 = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|Any CPU.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhone.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhone.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhone.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhone.Build.0 = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|Any CPU.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|Any CPU.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhone.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhone.Build.0 = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {52F55355-D120-42AC-8116-8410A7D602FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {52F55355-D120-42AC-8116-8410A7D602FA}.Release|Any CPU.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhone.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhone.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|Any CPU.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhone.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhone.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhone.Build.0 = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|Any CPU.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhone.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhone.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|Any CPU.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhone.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.Build.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.Deploy.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.Build.0 = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|Any CPU.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhone.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhone.Build.0 = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|Any CPU.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhone.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhone.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhone.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.Build.0 = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhone.ActiveCfg = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhone.Build.0 = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.Build.0 = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.ActiveCfg = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.Build.0 = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.Build.0 = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.ActiveCfg = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.Build.0 = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.Build.0 = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhone.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|Any CPU.Build.0 = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhone.ActiveCfg = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhone.Build.0 = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.Build.0 = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.ActiveCfg = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.Build.0 = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.Build.0 = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.ActiveCfg = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.Build.0 = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.Build.0 = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.Build.0 = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhone.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|Any CPU.Build.0 = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhone.ActiveCfg = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhone.Build.0 = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhone.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhone.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|Any CPU.ActiveCfg = Release|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|Any CPU.Build.0 = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.ActiveCfg = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.ActiveCfg = Release|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhone.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhone.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|Any CPU.ActiveCfg = Release|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|Any CPU.Build.0 = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhone.ActiveCfg = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhone.Build.0 = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhone.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|Any CPU.Build.0 = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.ActiveCfg = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.Build.0 = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.Build.0 = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.ActiveCfg = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.Build.0 = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.ActiveCfg = Release|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.Build.0 = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.ActiveCfg = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.Build.0 = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.Build.0 = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.ActiveCfg = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.Build.0 = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhone.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhone.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.Build.0 = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhone.ActiveCfg = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhone.Build.0 = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhone.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|Any CPU.ActiveCfg = Release|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|Any CPU.Build.0 = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.ActiveCfg = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.Build.0 = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.Build.0 = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.ActiveCfg = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.Build.0 = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.Build.0 = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.ActiveCfg = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.Build.0 = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhone.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhone.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|Any CPU.Build.0 = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.ActiveCfg = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.Build.0 = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhone.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhone.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.Build.0 = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.ActiveCfg = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhone.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhone.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|Any CPU.Build.0 = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhone.ActiveCfg = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhone.Build.0 = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhone.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhone.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|Any CPU.Build.0 = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhone.ActiveCfg = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhone.Build.0 = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.ActiveCfg = Release|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.Build.0 = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.ActiveCfg = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|Any CPU.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhone.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhone.Build.0 = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|Any CPU.ActiveCfg = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|Any CPU.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhone.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhone.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhone.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhone.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|Any CPU.Build.0 = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.ActiveCfg = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.Build.0 = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.Build.0 = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.ActiveCfg = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.ActiveCfg = Release|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.Build.0 = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.ActiveCfg = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.Build.0 = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.ActiveCfg = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.Build.0 = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhone.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhone.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhone.ActiveCfg = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhone.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhone.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhone.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.ActiveCfg = Release|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhone.ActiveCfg = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhone.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhone.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhone.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.Build.0 = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhone.ActiveCfg = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhone.Build.0 = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhone.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhone.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|Any CPU.Build.0 = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.ActiveCfg = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.Build.0 = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.Build.0 = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.ActiveCfg = Release|Any CPU - {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 - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.Build.0 = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.ActiveCfg = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.Build.0 = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.Build.0 = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.ActiveCfg = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1968,7 +538,6 @@ Global {29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098} {7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E} {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} = {A689DEF5-D50F-4975-8B72-124C9EB54066} {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} From 17a2a4e2e03d754923b428960fa63879f397cd78 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 00:01:59 -0400 Subject: [PATCH 694/820] Extract UnmanagedBlob to a separated file --- .../Platform/Internal/UnmanagedBlob.cs | 57 +++++++++++++++++++ .../Platform/StandardRuntimePlatform.cs | 54 +----------------- 2 files changed, 58 insertions(+), 53 deletions(-) create mode 100644 src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs new file mode 100644 index 0000000000..eacf79d4f4 --- /dev/null +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Platform.Internal; + +internal class UnmanagedBlob : IUnmanagedBlob +{ + private IntPtr _address; + private readonly object _lock = new object(); + + public UnmanagedBlob(int size) + { + try + { + if (size <= 0) + throw new ArgumentException("Positive number required", nameof(size)); + _address = Marshal.AllocHGlobal(size); + GC.AddMemoryPressure(size); + Size = size; + } + catch + { + GC.SuppressFinalize(this); + throw; + } + } + + private void DoDispose() + { + lock (_lock) + { + if (!IsDisposed) + { + Marshal.FreeHGlobal(_address); + GC.RemoveMemoryPressure(Size); + IsDisposed = true; + _address = IntPtr.Zero; + Size = 0; + } + } + } + + public void Dispose() + { + DoDispose(); + GC.SuppressFinalize(this); + } + + ~UnmanagedBlob() + { + DoDispose(); + } + + public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; + public int Size { get; private set; } + public bool IsDisposed { get; private set; } +} diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index 3acb5f6330..ebda6f453b 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using System.Threading; +using Avalonia.Platform.Internal; namespace Avalonia.Platform { @@ -12,59 +13,6 @@ namespace Avalonia.Platform } public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); - - private class UnmanagedBlob : IUnmanagedBlob - { - private IntPtr _address; - private readonly object _lock = new object(); - - public UnmanagedBlob(int size) - { - try - { - if (size <= 0) - throw new ArgumentException("Positive number required", nameof(size)); - _address = Marshal.AllocHGlobal(size); - GC.AddMemoryPressure(size); - Size = size; - } - catch - { - GC.SuppressFinalize(this); - throw; - } - } - - private void DoDispose() - { - lock (_lock) - { - if (!IsDisposed) - { - Marshal.FreeHGlobal(_address); - GC.RemoveMemoryPressure(Size); - IsDisposed = true; - _address = IntPtr.Zero; - Size = 0; - } - } - } - - public void Dispose() - { - DoDispose(); - GC.SuppressFinalize(this); - } - - ~UnmanagedBlob() - { - DoDispose(); - } - - public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; - public int Size { get; private set; } - public bool IsDisposed { get; private set; } - } private static readonly Lazy Info = new(() => { From a907b942c5fde9edbb6bdd48bde09480e64f262e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 03:00:03 -0400 Subject: [PATCH 695/820] Remove platform support projects --- .../Avalonia.PlatformSupport.csproj | 24 ------------------- .../Avalonia.PlatformSupport.UnitTests.csproj | 24 ------------------- 2 files changed, 48 deletions(-) delete mode 100644 src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj delete mode 100644 tests/Avalonia.PlatformSupport.UnitTests/Avalonia.PlatformSupport.UnitTests.csproj diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj deleted file mode 100644 index 5336f1e630..0000000000 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net6.0;net461;netstandard2.0 - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.PlatformSupport.UnitTests/Avalonia.PlatformSupport.UnitTests.csproj b/tests/Avalonia.PlatformSupport.UnitTests/Avalonia.PlatformSupport.UnitTests.csproj deleted file mode 100644 index d714941e5a..0000000000 --- a/tests/Avalonia.PlatformSupport.UnitTests/Avalonia.PlatformSupport.UnitTests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net6.0 - enable - Library - true - latest - - - - - - - - - - - - - - - - From 7f90e74f254031499774ce13572d49cc38c3ac9d Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 03:09:10 -0400 Subject: [PATCH 696/820] Unwanted namespace changes --- src/Avalonia.Base/Media/PathGeometry.cs | 1 + src/Avalonia.Base/Media/PathGeometryCollections.cs | 2 +- src/Avalonia.Base/Platform/PathGeometryContext.cs | 3 ++- tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/PathGeometry.cs b/src/Avalonia.Base/Media/PathGeometry.cs index 8662c3351d..2c8a51c541 100644 --- a/src/Avalonia.Base/Media/PathGeometry.cs +++ b/src/Avalonia.Base/Media/PathGeometry.cs @@ -2,6 +2,7 @@ using System; using Avalonia.Collections; using Avalonia.Metadata; using Avalonia.Platform; +using Avalonia.Visuals.Platform; namespace Avalonia.Media { diff --git a/src/Avalonia.Base/Media/PathGeometryCollections.cs b/src/Avalonia.Base/Media/PathGeometryCollections.cs index c663b1ebe5..1165b192a7 100644 --- a/src/Avalonia.Base/Media/PathGeometryCollections.cs +++ b/src/Avalonia.Base/Media/PathGeometryCollections.cs @@ -1,5 +1,5 @@ using Avalonia.Collections; -using Avalonia.Platform; +using Avalonia.Visuals.Platform; namespace Avalonia.Media { diff --git a/src/Avalonia.Base/Platform/PathGeometryContext.cs b/src/Avalonia.Base/Platform/PathGeometryContext.cs index 6c0bbe0f3f..694e9f8d80 100644 --- a/src/Avalonia.Base/Platform/PathGeometryContext.cs +++ b/src/Avalonia.Base/Platform/PathGeometryContext.cs @@ -1,8 +1,9 @@ using System; using System.Diagnostics.CodeAnalysis; using Avalonia.Media; +using Avalonia.Platform; -namespace Avalonia.Platform +namespace Avalonia.Visuals.Platform { public class PathGeometryContext : IGeometryContext { diff --git a/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs index 73e97bf13c..c829690eb4 100644 --- a/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.IO; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.Visuals.Platform; using Moq; using Xunit; From 0754e29a3c4c1f4b005bacf1ea03812ff2cb15d0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 03:34:34 -0400 Subject: [PATCH 697/820] Address the review --- src/Avalonia.Base/Platform/StandardRuntimePlatform.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs index ebda6f453b..4df9e8e917 100644 --- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -20,7 +20,7 @@ namespace Avalonia.Platform if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) os = OperatingSystemType.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"))) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) os = OperatingSystemType.Linux; else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) os = OperatingSystemType.WinNT; @@ -35,7 +35,7 @@ namespace Avalonia.Platform // Source: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs var isCoreClr = Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); - var isMonoRuntime = Type.GetType("Mono.RuntimeStructs") != null; + var isMonoRuntime = Type.GetType("Mono.Runtime") != null; var isFramework = !isCoreClr && RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); return new RuntimePlatformInfo From 12fd9491265a3e9f8721050498982ee4ef8e3fcd Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 04:21:17 -0400 Subject: [PATCH 698/820] Disable AssemblyName_With_Non_ASCII_Should_Load_Avares test --- tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs b/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs index 06b7a4ce94..c590a763b2 100644 --- a/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs +++ b/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs @@ -39,7 +39,7 @@ public class AssetLoaderTests Assert.Equal(AssemblyNameWithWhitespace, assemblyActual?.FullName); } - [Fact] + [Fact(Skip = "RegisterResUriParsers breaks this test. Investigate.")] public void AssemblyName_With_Non_ASCII_Should_Load_Avares() { var uri = new Uri($"avares://{AssemblyNameWithNonAscii}/Assets/something"); From 39d9a014b7b337750d1c84d39d3ced96741920f7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 04:42:19 -0400 Subject: [PATCH 699/820] Fix static SetAssemblyDescriptorResolver usage in tests --- tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs b/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs index c590a763b2..28fb19e119 100644 --- a/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs +++ b/tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs @@ -7,7 +7,7 @@ using Xunit; namespace Avalonia.Base.UnitTests; -public class AssetLoaderTests +public class AssetLoaderTests : IDisposable { public class MockAssembly : Assembly {} @@ -15,7 +15,7 @@ public class AssetLoaderTests private const string AssemblyNameWithNonAscii = "Какое-то-название"; - static AssetLoaderTests() + public AssetLoaderTests() { var resolver = Mock.Of(); @@ -39,7 +39,7 @@ public class AssetLoaderTests Assert.Equal(AssemblyNameWithWhitespace, assemblyActual?.FullName); } - [Fact(Skip = "RegisterResUriParsers breaks this test. Investigate.")] + [Fact(Skip = "RegisterResUriParsers breaks this test. See https://github.com/AvaloniaUI/Avalonia/issues/2555.")] public void AssemblyName_With_Non_ASCII_Should_Load_Avares() { var uri = new Uri($"avares://{AssemblyNameWithNonAscii}/Assets/something"); @@ -60,4 +60,9 @@ public class AssetLoaderTests Mock.Get(descriptor).Setup(x => x.Assembly).Returns(assembly); return descriptor; } + + public void Dispose() + { + AssetLoader.SetAssemblyDescriptorResolver(new AssemblyDescriptorResolver()); + } } From 2fb68a43a663b6aa4f37bbb9570932e76f104eac Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 04:53:38 -0400 Subject: [PATCH 700/820] Do not run Avalonia.PlatformSupport.UnitTests tests --- nukebuild/Build.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index f0f677b844..9fcb9d6b7f 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -221,7 +221,6 @@ partial class Build : NukeBuild RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); - RunCoreTest("Avalonia.PlatformSupport.UnitTests"); }); Target RunRenderTests => _ => _ From 25efb6bec6a1b2a72ecc245346279e4583f7f421 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 May 2022 11:07:09 +0200 Subject: [PATCH 701/820] Fix pooled list bug. Ported from upstream: https://github.com/jtmueller/Collections.Pooled/pull/44 --- src/Avalonia.Base/Collections/Pooled/PooledList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Collections/Pooled/PooledList.cs b/src/Avalonia.Base/Collections/Pooled/PooledList.cs index 200a52fb0d..803b8d60dc 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledList.cs @@ -587,7 +587,7 @@ namespace Avalonia.Collections.Pooled if (size > 0 && _clearOnFree) { // Clear the elements so that the gc can reclaim the references. - Array.Clear(_items, 0, _size); + Array.Clear(_items, 0, size); } } From 61ee2d18e9186cdd76fad89217581cb281880bc6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 May 2022 11:11:49 +0200 Subject: [PATCH 702/820] Move assert of initial item state into method. So that we don't keep a reference to the item. --- tests/Avalonia.LeakTests/ControlTests.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 09d6764bae..3971cddecf 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -2,14 +2,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Runtime.Remoting.Contexts; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Input; -using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; @@ -710,11 +708,15 @@ namespace Avalonia.LeakTests window.Show(); window.LayoutManager.ExecuteInitialLayoutPass(); + void AssertInitialItemState() + { + var item0 = (ListBoxItem)lb.ItemContainerGenerator.Containers.First().ContainerControl; + var canvas0 = (Canvas)item0.Presenter.Child; + Assert.Equal("foo", canvas0.Tag); + } + Assert.Equal(10, lb.ItemContainerGenerator.Containers.Count()); - - var item0 = (ListBoxItem)lb.ItemContainerGenerator.Containers.First().ContainerControl; - var canvas0 = (Canvas)item0.Presenter.Child; - Assert.Equal("foo", canvas0.Tag); + AssertInitialItemState(); items.Clear(); window.LayoutManager.ExecuteLayoutPass(); From 5aa7de52e787787ba702e3c614b370b587af2e8c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 May 2022 12:17:17 +0200 Subject: [PATCH 703/820] Add cleanup step to leak tests. Because previously failures could occur depending on the order that leak tests were run in. --- tests/Avalonia.LeakTests/ControlTests.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 3971cddecf..bb520c16aa 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; @@ -12,6 +13,7 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; +using Avalonia.Threading; using Avalonia.UnitTests; using Avalonia.VisualTree; using JetBrains.dotMemoryUnit; @@ -730,10 +732,23 @@ namespace Avalonia.LeakTests private IDisposable Start() { - return UnitTestApplication.Start(TestServices.StyledWindow.With( - focusManager: new FocusManager(), - keyboardDevice: () => new KeyboardDevice(), - inputManager: new InputManager())); + void Cleanup() + { + // KeyboardDevice holds a reference to the focused item. + KeyboardDevice.Instance.SetFocusedElement(null, NavigationMethod.Unspecified, KeyModifiers.None); + + // Empty the dispatcher queue. + Dispatcher.UIThread.RunJobs(); + } + + return new CompositeDisposable + { + Disposable.Create(Cleanup), + UnitTestApplication.Start(TestServices.StyledWindow.With( + focusManager: new FocusManager(), + keyboardDevice: () => new KeyboardDevice(), + inputManager: new InputManager())) + }; } From af237c6dd7067b7b5f43961f4fd64b635a2d20fc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 25 May 2022 11:24:53 +0100 Subject: [PATCH 704/820] fix file dialog filter nullable annotation, and osx platform. --- samples/ControlCatalog/ControlCatalog.csproj | 3 ++- samples/ControlCatalog/Pages/DialogsPage.xaml.cs | 3 +-- src/Avalonia.Controls/SystemDialog.cs | 2 +- src/Avalonia.Native/SystemDialogs.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index e5f07c90c3..bce924a3f2 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,7 +1,8 @@  netstandard2.0 - true + true + enable diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 3cadc7243a..fd908a33b6 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -8,7 +8,6 @@ using Avalonia.Dialogs; using Avalonia.Layout; using Avalonia.Markup.Xaml; #pragma warning disable 4014 - namespace ControlCatalog.Pages { public class DialogsPage : UserControl @@ -22,7 +21,7 @@ namespace ControlCatalog.Pages string lastSelectedDirectory = null; - List GetFilters() + List? GetFilters() { if (this.FindControl("UseFilters").IsChecked != true) return null; diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index 4a9e745e30..093f10be51 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls /// Gets or sets a collection of filters which determine the types of files displayed in an /// or an . /// - public List Filters { get; set; } = new List(); + public List? Filters { get; set; } = new List(); /// /// Gets or sets initial file name that is displayed when the dialog is opened. diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 4372829df1..d1d9c17ae3 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -30,7 +30,7 @@ namespace Avalonia.Native ofd.Title ?? "", ofd.Directory ?? "", ofd.InitialFileName ?? "", - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + string.Join(";", dialog.Filters?.SelectMany(f => f.Extensions) ?? Array.Empty())); } else { @@ -39,7 +39,7 @@ namespace Avalonia.Native dialog.Title ?? "", dialog.Directory ?? "", dialog.InitialFileName ?? "", - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + string.Join(";", dialog.Filters?.SelectMany(f => f.Extensions) ?? Array.Empty())); } return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; }); From 5b66f260657190e09698254cf5059b3efa8ef6df Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 25 May 2022 11:38:50 +0100 Subject: [PATCH 705/820] Fix osx dialog style mask. makes the titlebar look like normal window. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index d43a8beee4..d96fe717ab 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -523,7 +523,7 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless; + unsigned long s = this->_isDialog ? NSWindowStyleMaskDocModalWindow : NSWindowStyleMaskBorderless; switch (_decorations) { case SystemDecorationsNone: From 8aa64bf839d26e3971456157038a717fc1a0562f Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 25 May 2022 12:48:22 +0200 Subject: [PATCH 706/820] Update SkiaSharp. --- build/SkiaSharp.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index a217a8272d..1ee4aa56a2 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + From ea9bd40dbd4c3f576f8e6a4efa14987983983fdd Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 25 May 2022 12:57:02 +0200 Subject: [PATCH 707/820] Update HarfBuzzSharp. --- build/HarfBuzzSharp.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index e10de93530..85e7a1f34d 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + From 3a2e13be2265b5af07cdd533545b58ed2139f84c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 25 May 2022 15:10:43 +0200 Subject: [PATCH 708/820] Add missing file --- .../Media/TextFormatting/TextRunBounds.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs diff --git a/src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs b/src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs new file mode 100644 index 0000000000..91150160ed --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs @@ -0,0 +1,39 @@ +namespace Avalonia.Media.TextFormatting +{ + /// + /// The bounding rectangle of text run + /// + public sealed class TextRunBounds + { + /// + /// Constructing TextRunBounds + /// + internal TextRunBounds(Rect bounds, int firstCharacterIndex, int length, TextRun textRun) + { + Rectangle = bounds; + TextSourceCharacterIndex = firstCharacterIndex; + Length = length; + TextRun = textRun; + } + + /// + /// First text source character index of text run + /// + public int TextSourceCharacterIndex { get; } + + /// + /// character length of bounded text run + /// + public int Length { get; } + + /// + /// Text run bounding rectangle + /// + public Rect Rectangle { get; } + + /// + /// text run + /// + public TextRun TextRun { get; } + } +} From ba7fd6c9e8be304db7bdc22900f22ead2efcd799 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 15:30:24 +0200 Subject: [PATCH 709/820] fix: some xml comment --- src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs | 1 + src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs index 60c7a97ced..239f3aea08 100644 --- a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs @@ -14,6 +14,7 @@ public class Rotate3DTransition: PageSlide /// /// How long the rotation should take place /// The orientation of the rotation + /// Defines the depth of the 3D Effect. If null, depth will be calculated automatically from the width or height of the common parent of the visual being rotated public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal, double? depth = null) : base(duration, orientation) { diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs index 445f35aad2..45c67b9f48 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs @@ -55,7 +55,7 @@ namespace Avalonia /// /// /// This will usually be true, except in - /// + /// /// which receives notifications for all changes to property values, whether a value with a higher /// priority is present or not. When this property is false, the change that is being signaled /// has not resulted in a change to the property value on the object. diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index a3095ad214..ae52e5f970 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -193,8 +193,11 @@ namespace Avalonia.Controls } } + /// Try get number of DataSource itmes. /// When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead. /// If "getAny" is true, method can use Linq.Any() method to speedup. + /// number of DataSource itmes. + /// true if able to retrieve number of DataSource itmes; otherwise, false. internal bool TryGetCount(bool allowSlow, bool getAny, out int count) { bool result; From 543fe599c691403514fb0000a1bcee2904556ebb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 16:32:56 +0200 Subject: [PATCH 710/820] fix: some nullable annotation warnings --- .../ViewModels/TransitioningContentControlPageViewModel.cs | 6 +++--- src/Avalonia.Controls/Avalonia.Controls.csproj | 3 --- src/Avalonia.Themes.Default/SimpleTheme.cs | 2 +- src/Avalonia.Themes.Fluent/FluentTheme.cs | 6 ++++-- .../CompiledBindings/PropertyInfoAccessorFactory.cs | 4 +--- .../MarkupExtensions/ResourceInclude.cs | 4 ++-- src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs | 2 +- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs | 2 +- tests/Avalonia.Benchmarks/TestBindingObservable.cs | 3 ++- .../Media/TextFormatting/TextFormatterTests.cs | 4 ++-- .../Media/TextFormatting/TextLineTests.cs | 4 ++-- 12 files changed, 20 insertions(+), 22 deletions(-) diff --git a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs index b092a07f4a..0e9522acab 100644 --- a/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs @@ -45,12 +45,12 @@ namespace ControlCatalog.ViewModels public List Images { get; } = new List(); - private Bitmap? _SelectedImage; + private Bitmap _SelectedImage; /// /// Gets or Sets the selected image /// - public Bitmap? SelectedImage + public Bitmap SelectedImage { get { return _SelectedImage; } set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); } @@ -293,7 +293,7 @@ namespace ControlCatalog.ViewModels /// /// Any one of the parameters may be null, but not both. /// - private static IVisual GetVisualParent(IVisual? from, IVisual? to) + private static IVisual GetVisualParent(IVisual from, IVisual to) { var p1 = (from ?? to)!.VisualParent; var p2 = (to ?? from)!.VisualParent; diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 4d239e69f4..3896dc2735 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -2,9 +2,6 @@ net6.0;netstandard2.0 - - - diff --git a/src/Avalonia.Themes.Default/SimpleTheme.cs b/src/Avalonia.Themes.Default/SimpleTheme.cs index 6929660757..664c95644f 100644 --- a/src/Avalonia.Themes.Default/SimpleTheme.cs +++ b/src/Avalonia.Themes.Default/SimpleTheme.cs @@ -44,7 +44,7 @@ namespace Avalonia.Themes.Default InitStyles(_baseUri); } - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index f6b47a5466..2a8e045c48 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -50,7 +50,9 @@ namespace Avalonia.Themes.Fluent /// The XAML service provider. public FluentTheme(IServiceProvider serviceProvider) { - _baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri; + var ctx = serviceProvider.GetService(typeof(IUriContext)) as IUriContext + ?? throw new NullReferenceException("Unable retrive UriContext"); + _baseUri = ctx.BaseUri; InitStyles(_baseUri); } @@ -146,7 +148,7 @@ namespace Avalonia.Themes.Fluent IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs index c21a2d4299..ef11b06369 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Text; using Avalonia.Data; using Avalonia.Data.Core; using Avalonia.Data.Core.Plugins; @@ -174,7 +172,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings WeakEvents.CollectionChanged.Unsubscribe(incc, this); } - public void OnEvent(object? sender, WeakEvent ev, NotifyCollectionChangedEventArgs args) + public void OnEvent(object sender, WeakEvent ev, NotifyCollectionChangedEventArgs args) { if (ShouldNotifyListeners(args)) { diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs index b6137aa89f..1091b3ec7e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs @@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions bool IResourceNode.HasResources => Loaded.HasResources; - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add => Loaded.OwnerChanged += value; remove => Loaded.OwnerChanged -= value; @@ -52,7 +52,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { if (!_isLoading) { - return Loaded.TryGetResource(key, out value); + return Loaded.TryGetResource(key, out value); } value = null; diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index fa4a27fc50..46b5bc0c40 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -64,7 +64,7 @@ namespace Avalonia.Markup.Xaml.Styling IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 1c2dd92219..4d537a16a4 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -195,7 +195,7 @@ namespace Avalonia.Win32 ShowActivated = true; } - private void TrayPopupRoot_Deactivated(object sender, EventArgs e) + private void TrayPopupRoot_Deactivated(object? sender, EventArgs e) { Close(); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index 48f5f8f871..e864f32138 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs @@ -109,7 +109,7 @@ namespace Avalonia.Win32 if (_owner is Window window) { - var visual = window.Renderer.HitTestFirst(position, _owner as Window, x => + var visual = window.Renderer.HitTestFirst(position, _owner, x => { if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsVisible)) { diff --git a/tests/Avalonia.Benchmarks/TestBindingObservable.cs b/tests/Avalonia.Benchmarks/TestBindingObservable.cs index 0721ca9855..653959ba18 100644 --- a/tests/Avalonia.Benchmarks/TestBindingObservable.cs +++ b/tests/Avalonia.Benchmarks/TestBindingObservable.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using Avalonia.Data; namespace Avalonia.Benchmarks diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 9d40898608..d395f68f96 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -602,7 +602,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting private class EndOfLineTextSource : ITextSource { - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { return new TextEndOfLine(); } @@ -617,7 +617,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting _text = text; } - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length) { diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index a47638d2ec..6321e8a336 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -547,7 +547,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { const string Text = "_A_A"; - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { switch (textSourceIndex) { @@ -755,7 +755,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting _textRuns = textRuns; } - public TextRun? GetTextRun(int textSourceIndex) + public TextRun GetTextRun(int textSourceIndex) { var currentPosition = 0; From 19ebf5ad7d8356b9f88716223d1c37759fcb41d6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 25 May 2022 23:44:47 -0400 Subject: [PATCH 711/820] Bring back blob disposal checks --- .../Platform/Internal/UnmanagedBlob.cs | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs index eacf79d4f4..ed0862c06c 100644 --- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace Avalonia.Platform.Internal; @@ -7,7 +10,30 @@ internal class UnmanagedBlob : IUnmanagedBlob { private IntPtr _address; private readonly object _lock = new object(); - +#if DEBUG + private static readonly List Backtraces = new List(); + private static Thread? GCThread; + private readonly string _backtrace; + private static readonly object _btlock = new object(); + + class GCThreadDetector + { + ~GCThreadDetector() + { + GCThread = Thread.CurrentThread; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Spawn() => new GCThreadDetector(); + + static UnmanagedBlob() + { + Spawn(); + GC.WaitForPendingFinalizers(); + } +#endif + public UnmanagedBlob(int size) { try @@ -23,14 +49,23 @@ internal class UnmanagedBlob : IUnmanagedBlob GC.SuppressFinalize(this); throw; } +#if DEBUG + _backtrace = Environment.StackTrace; + lock (_btlock) + Backtraces.Add(_backtrace); +#endif } - - private void DoDispose() + + void DoDispose() { lock (_lock) { if (!IsDisposed) { +#if DEBUG + lock (_btlock) + Backtraces.Remove(_backtrace); +#endif Marshal.FreeHGlobal(_address); GC.RemoveMemoryPressure(Size); IsDisposed = true; @@ -39,18 +74,35 @@ internal class UnmanagedBlob : IUnmanagedBlob } } } - + public void Dispose() { +#if DEBUG + if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) + { + lock (_lock) + { + if (!IsDisposed) + { + Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " + + Environment.StackTrace + + "\n\nBlob created by " + _backtrace); + } + } + } +#endif DoDispose(); GC.SuppressFinalize(this); } - + ~UnmanagedBlob() { +#if DEBUG + Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); +#endif DoDispose(); } - + public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; public int Size { get; private set; } public bool IsDisposed { get; private set; } From 62078d2cc3a2e1ffa7bbc77a910b5a8a43488e11 Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Thu, 26 May 2022 09:59:58 +0300 Subject: [PATCH 712/820] calculate mirrorTransform before renderTransform --- src/Avalonia.Base/Rendering/ImmediateRenderer.cs | 16 +++++++++------- .../Rendering/SceneGraph/SceneBuilder.cs | 16 +++++++++------- src/Avalonia.Base/VisualExtensions.cs | 13 +++++++------ src/Avalonia.Controls/Viewbox.cs | 3 +-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index 2c0298affa..54b2ce5a25 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -277,18 +277,20 @@ namespace Avalonia.Rendering var m = Matrix.CreateTranslation(visual.Bounds.Position); var renderTransform = Matrix.Identity; + + // this should be calculated BEFORE renderTransform + if (visual.HasMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); + renderTransform *= mirrorMatrix; + } if (visual.RenderTransform != null) { var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); var offset = Matrix.CreateTranslation(origin); - renderTransform = (-offset) * visual.RenderTransform.Value * (offset); - } - - if (visual.HasMirrorTransform) - { - var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); - renderTransform *= mirrorMatrix; + var finalTransform = (-offset) * visual.RenderTransform.Value * (offset); + renderTransform *= finalTransform; } m = renderTransform * m; diff --git a/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs index 019c3e0e9b..5dc426ab06 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs @@ -188,19 +188,21 @@ namespace Avalonia.Rendering.SceneGraph var renderTransform = Matrix.Identity; - if (visual.RenderTransform != null) - { - var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); - var offset = Matrix.CreateTranslation(origin); - renderTransform = (-offset) * visual.RenderTransform.Value * (offset); - } - + // this should be calculated BEFORE renderTransform if (visual.HasMirrorTransform) { var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); renderTransform *= mirrorMatrix; } + if (visual.RenderTransform != null) + { + var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); + var offset = Matrix.CreateTranslation(origin); + var finalTransform = (-offset) * visual.RenderTransform.Value * (offset); + renderTransform *= finalTransform; + } + m = renderTransform * m; using (contextImpl.BeginUpdate(node)) diff --git a/src/Avalonia.Base/VisualExtensions.cs b/src/Avalonia.Base/VisualExtensions.cs index 3a3c2693d0..7a5bbb105c 100644 --- a/src/Avalonia.Base/VisualExtensions.cs +++ b/src/Avalonia.Base/VisualExtensions.cs @@ -101,6 +101,13 @@ namespace Avalonia while (v != ancestor) { + // this should be calculated BEFORE renderTransform + if (v.HasMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0); + result *= mirrorMatrix; + } + if (v.RenderTransform?.Value != null) { var origin = v.RenderTransformOrigin.ToPixels(v.Bounds.Size); @@ -110,12 +117,6 @@ namespace Avalonia result *= renderTransform; } - if (v.HasMirrorTransform) - { - var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0); - result *= mirrorMatrix; - } - var topLeft = v.Bounds.TopLeft; if (topLeft != default) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 01667c5136..01a41a0157 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -1,7 +1,6 @@ using Avalonia.Media; using Avalonia.Media.Immutable; using Avalonia.Metadata; -using Avalonia.Layout; namespace Avalonia.Controls { @@ -151,7 +150,7 @@ namespace Avalonia.Controls /// /// A simple container control which hosts its child as a visual but not logical child. /// - private class ViewboxContainer : Layoutable + private class ViewboxContainer : Control { private IControl? _child; From f1a2716858fc90067ffe6fa0651ee2a04f43c36a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 26 May 2022 03:36:49 -0400 Subject: [PATCH 713/820] Inherit DataType from template without hardcoding on specific DataTemplate implementation --- .../Metadata/TemplateDataTypeAttribute.cs | 9 ++ ...valoniaXamlIlDataContextTypeTransformer.cs | 5 +- .../AvaloniaXamlIlWellKnownTypes.cs | 2 + .../Templates/DataTemplate.cs | 1 + .../Templates/TreeDataTemplate.cs | 1 + .../CompiledBindingExtensionTests.cs | 122 +++++++++++++++++- 6 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs diff --git a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs new file mode 100644 index 0000000000..29e8cf4a06 --- /dev/null +++ b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Avalonia.Metadata; + +[AttributeUsage(AttributeTargets.Property)] +public class TemplateDataTypeAttribute : Attribute +{ + +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index cf691db860..75859a0a18 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -49,6 +49,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } else if (child is XamlPropertyAssignmentNode pa) { + var templateDataTypeAttribute = context.GetAvaloniaTypes().TemplateDataTypeAttribute; + if (pa.Property.Name == "DataContext" && pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement) && pa.Values[0] is XamlMarkupExtensionNode ext @@ -56,8 +58,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { inferredDataContextTypeNode = ParseDataContext(context, on, obj); } - else if(context.GetAvaloniaTypes().DataTemplate.IsAssignableFrom(on.Type.GetClrType()) - && pa.Property.Name == "DataType" + else if(pa.Property.CustomAttributes.Any(a => a.Type == templateDataTypeAttribute) && pa.Values[0] is XamlTypeExtensionNode dataTypeNode) { inferredDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, dataTypeNode.Value.GetClrType()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 5da40035d2..da28891968 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -26,6 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType Transitions { get; } public IXamlType AssignBindingAttribute { get; } public IXamlType DependsOnAttribute { get; } + public IXamlType TemplateDataTypeAttribute { get; } public IXamlType UnsetValueType { get; } public IXamlType StyledElement { get; } public IXamlType IStyledElement { get; } @@ -112,6 +113,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions"); AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute"); + TemplateDataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.TemplateDataTypeAttribute"); AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaProperty, IBinding, cfg.WellKnownTypes.Object); diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs index b7db1a3fbb..1dd3cddf9a 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs @@ -7,6 +7,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class DataTemplate : IRecyclingDataTemplate { + [TemplateDataType] public Type DataType { get; set; } [Content] diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index 7b065c7f47..b07e501fde 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs @@ -11,6 +11,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class TreeDataTemplate : ITreeDataTemplate { + [TemplateDataType] public Type DataType { get; set; } [Content] diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index c1c2284372..fe3df7d189 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -8,11 +8,14 @@ using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.Data.Converters; using Avalonia.Data.Core; using Avalonia.Input; using Avalonia.Markup.Data; +using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.UnitTests; using XamlX; using Xunit; @@ -455,7 +458,106 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } + + [Fact] + public void IgnoresDataTemplateTypeFromDataTypePropertyIfXDataTypeDefined() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + var dataContext = new TestDataContext(); + + dataContext.StringProperty = "Initial Value"; + + window.DataContext = dataContext; + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); + } + } + + [Fact] + public void InfersCustomDataTemplateBasedOnAttribute() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + var dataContext = new TestDataContext(); + + dataContext.StringProperty = "Initial Value"; + + window.DataContext = dataContext; + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); + } + } + + [Fact] + public void InfersCustomDataTemplateBasedOnAttributeFromBaseClass() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + var dataContext = new TestDataContext(); + + dataContext.StringProperty = "Initial Value"; + + window.DataContext = dataContext; + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); + } + } + [Fact] public void ResolvesElementNameBinding() { @@ -1324,7 +1426,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions public string StringProperty { get; set; } } - public class TestDataContext : IHasPropertyDerived + public class TestDataContextBaseClass {} + + public class TestDataContext : TestDataContextBaseClass, IHasPropertyDerived { public string StringProperty { get; set; } @@ -1413,4 +1517,20 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions return ReferenceEquals(null, parameter) == false; } } + + public class CustomDataTemplate : IDataTemplate + { + [TemplateDataType] + public Type FancyDataType { get; set; } + + [Content] + [TemplateContent] + public object Content { get; set; } + + public bool Match(object data) => FancyDataType.IsInstanceOfType(data); + + public IControl Build(object data) => TemplateContent.Load(Content)?.Control; + } + + public class CustomDataTemplateInherit : CustomDataTemplate { } } From 9126edca7003c698f4f8a60e0ee10cd5c54605ec Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 26 May 2022 03:40:05 -0400 Subject: [PATCH 714/820] Add documentation --- src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs index 29e8cf4a06..51899bd03c 100644 --- a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs +++ b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs @@ -2,6 +2,9 @@ namespace Avalonia.Metadata; +/// +/// Defines the property that contains type of the data passed to the implementation. +/// [AttributeUsage(AttributeTargets.Property)] public class TemplateDataTypeAttribute : Attribute { From b567fcebf58a3afc8b08f31996110f31fdb139e6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 26 May 2022 04:15:29 -0400 Subject: [PATCH 715/820] Rename TemplateDataTypeAttribute to DataTypeAttribute --- src/Avalonia.Base/Metadata/DataTypeAttribute.cs | 15 +++++++++++++++ .../Metadata/TemplateDataTypeAttribute.cs | 12 ------------ .../AvaloniaXamlIlDataContextTypeTransformer.cs | 2 +- .../Transformers/AvaloniaXamlIlWellKnownTypes.cs | 4 ++-- .../Templates/DataTemplate.cs | 2 +- .../Templates/TreeDataTemplate.cs | 2 +- .../CompiledBindingExtensionTests.cs | 2 +- 7 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 src/Avalonia.Base/Metadata/DataTypeAttribute.cs delete mode 100644 src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs diff --git a/src/Avalonia.Base/Metadata/DataTypeAttribute.cs b/src/Avalonia.Base/Metadata/DataTypeAttribute.cs new file mode 100644 index 0000000000..ac46a0d30a --- /dev/null +++ b/src/Avalonia.Base/Metadata/DataTypeAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Avalonia.Metadata; + +/// +/// Defines the property that contains type that should be used as a type information for compiled bindings. +/// +/// +/// Used on DataTemplate.DataType property so it can be inherited in compiled bindings inside of the template. +/// +[AttributeUsage(AttributeTargets.Property)] +public class DataTypeAttribute : Attribute +{ + +} diff --git a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs deleted file mode 100644 index 51899bd03c..0000000000 --- a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Avalonia.Metadata; - -/// -/// Defines the property that contains type of the data passed to the implementation. -/// -[AttributeUsage(AttributeTargets.Property)] -public class TemplateDataTypeAttribute : Attribute -{ - -} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index 75859a0a18..d4dfcf9c4b 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -49,7 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } else if (child is XamlPropertyAssignmentNode pa) { - var templateDataTypeAttribute = context.GetAvaloniaTypes().TemplateDataTypeAttribute; + var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute; if (pa.Property.Name == "DataContext" && pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index da28891968..28787d9b84 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -26,7 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType Transitions { get; } public IXamlType AssignBindingAttribute { get; } public IXamlType DependsOnAttribute { get; } - public IXamlType TemplateDataTypeAttribute { get; } + public IXamlType DataTypeAttribute { get; } public IXamlType UnsetValueType { get; } public IXamlType StyledElement { get; } public IXamlType IStyledElement { get; } @@ -113,7 +113,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions"); AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute"); - TemplateDataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.TemplateDataTypeAttribute"); + DataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DataTypeAttribute"); AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaProperty, IBinding, cfg.WellKnownTypes.Object); diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs index 1dd3cddf9a..d2b24979cc 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs @@ -7,7 +7,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class DataTemplate : IRecyclingDataTemplate { - [TemplateDataType] + [DataType] public Type DataType { get; set; } [Content] diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index b07e501fde..10061c3d48 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs @@ -11,7 +11,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class TreeDataTemplate : ITreeDataTemplate { - [TemplateDataType] + [DataType] public Type DataType { get; set; } [Content] diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index fe3df7d189..7e721fd7b2 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1520,7 +1520,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions public class CustomDataTemplate : IDataTemplate { - [TemplateDataType] + [DataType] public Type FancyDataType { get; set; } [Content] From 60d0e2f1c383159288f6fbb243bfc8d078f129cb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 17:30:25 +0200 Subject: [PATCH 716/820] fix: Consolidate JetBrains.DotMemoryUnit --- build/JetBrains.dotMemoryUnit.props | 2 +- nukebuild/_build.csproj | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props index eb4e2b6f15..5d74d474cf 100644 --- a/build/JetBrains.dotMemoryUnit.props +++ b/build/JetBrains.dotMemoryUnit.props @@ -1,5 +1,5 @@ - + diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 52b60b7d0f..b2c58e2292 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -8,11 +8,10 @@ False CS0649;CS0169 - + - From d3b09a7d741bd33b7c615e54d4792c02aa6a8d20 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 17:42:03 +0200 Subject: [PATCH 717/820] fix: Update SourceLink --- build/SourceLink.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/SourceLink.props b/build/SourceLink.props index 9f05848881..dd7ecc8d2a 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -19,7 +19,7 @@ - + From 1d8e3cabe7476c1c82e5b3ba60735a975bdb68cb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 26 May 2022 11:53:38 +0200 Subject: [PATCH 718/820] fix: update Microsoft.NETFramework.ReferenceAssemblies --- build/NetFX.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/NetFX.props b/build/NetFX.props index 8ffc9ec561..27bac831b8 100644 --- a/build/NetFX.props +++ b/build/NetFX.props @@ -1,7 +1,7 @@  - + From a25ce2fb08c0bcfe4eb05c04418f7df73770a2cc Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 26 May 2022 12:43:52 +0200 Subject: [PATCH 719/820] feat: move InternalsVisibleTo in csproj --- Directory.Build.props | 1 + build/AvaloniaPrublicKey.props | 5 +++++ src/Avalonia.Base/Avalonia.Base.csproj | 17 +++++++++++++++++ src/Avalonia.Base/Properties/AssemblyInfo.cs | 14 -------------- .../Avalonia.Controls.ColorPicker.csproj | 5 +++++ .../Properties/AssemblyInfo.cs | 3 --- .../Avalonia.Controls.DataGrid.csproj | 5 +++++ .../Properties/AssemblyInfo.cs | 4 ---- src/Avalonia.Controls/Avalonia.Controls.csproj | 6 ++++++ .../Properties/AssemblyInfo.cs | 5 ----- .../Avalonia.FreeDesktop.csproj | 4 +++- src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 4 ---- .../Avalonia.PlatformSupport.csproj | 2 +- .../Avalonia.Markup.Xaml.csproj | 4 ++++ .../Properties/AssemblyInfo.cs | 3 --- .../Avalonia.Markup/Avalonia.Markup.csproj | 4 ++++ .../Avalonia.Markup/Properties/AssemblyInfo.cs | 3 --- src/Skia/Avalonia.Skia/Avalonia.Skia.csproj | 9 +++++++++ .../Avalonia.Skia/Properties/AssemblyInfo.cs | 5 ----- .../Avalonia.Direct2D1.csproj | 10 ++++++++++ .../Properties/AssemblyInfo.cs | 4 ---- 21 files changed, 70 insertions(+), 47 deletions(-) create mode 100644 build/AvaloniaPrublicKey.props delete mode 100644 src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs delete mode 100644 src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs diff --git a/Directory.Build.props b/Directory.Build.props index 97781b7517..adfb985d2b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,4 +1,5 @@ + $(MSBuildThisFileDirectory)build-intermediate/nuget $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll diff --git a/build/AvaloniaPrublicKey.props b/build/AvaloniaPrublicKey.props new file mode 100644 index 0000000000..89215635c0 --- /dev/null +++ b/build/AvaloniaPrublicKey.props @@ -0,0 +1,5 @@ + + + 0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87 + + diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 8e4755b4b7..5bd733a17e 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -17,4 +17,21 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 2c40c768f5..3753588fce 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -17,18 +17,4 @@ using Avalonia.Metadata; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] -[assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.ColorPicker, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.PlatformSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 0952c899d4..e0790795c5 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -22,4 +22,9 @@ + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs index 0135541349..64769303d6 100644 --- a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs @@ -1,8 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 410cac72fc..6369961f0f 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -18,4 +18,9 @@ + + + + + diff --git a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs index d61c05ab6e..64769303d6 100644 --- a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs @@ -1,9 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 4d239e69f4..00f094f508 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -14,4 +14,10 @@ + + + + + + diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs index 25330614cf..0f3da91107 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -1,10 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Automation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index bcb63783a4..e9d6394aa5 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -8,5 +8,7 @@ - + + + diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index a7cc4f4cc2..4a55212de3 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -12,10 +12,6 @@ using Tmds.DBus; [assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] -[assembly: - InternalsVisibleTo( - "Avalonia.X11, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - namespace Avalonia.FreeDesktop { internal class DBusTrayIconImpl : ITrayIconImpl diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj index 5336f1e630..98839b7af3 100644 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 622ec416e8..30d321426f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -63,4 +63,8 @@ + + + + diff --git a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs index cee4d90917..7e5b3159d4 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs @@ -1,9 +1,6 @@ using Avalonia.Metadata; -using System.Runtime.CompilerServices; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.MarkupExtensions")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Styling")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Templates")] -[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index f7f1a111ba..6711c3dd3d 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs index 46e2925f92..36ca2784da 100644 --- a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs +++ b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs @@ -1,8 +1,5 @@ using Avalonia.Metadata; -using System.Runtime.CompilerServices; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Data")] -[assembly: InternalsVisibleTo("Avalonia.Markup.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj index 26c0fb756e..413c2ba4d4 100644 --- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj +++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj @@ -18,4 +18,13 @@ + + + + + + + + + diff --git a/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs b/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs deleted file mode 100644 index a7e556ee84..0000000000 --- a/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index bd1f05b30f..03b3ebec0d 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -19,4 +19,14 @@ + + + + + + + + + + diff --git a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs deleted file mode 100644 index 89fd6c2dd8..0000000000 --- a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] From ea4d92ddafcb74e58357729383376e7b5037e950 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 26 May 2022 23:12:21 +0200 Subject: [PATCH 720/820] Clear IMM state when window is closed. --- .../Avalonia.Win32/Input/Imm32InputMethod.cs | 18 ++++++++++++++++++ .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 9ff6f76ac4..3adefd965f 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -37,6 +37,24 @@ namespace Avalonia.Win32.Input IsComposing = false; } + public void ClearLanguageAndWindow() + { + if (HWND != IntPtr.Zero && _defaultImc != IntPtr.Zero) + { + ImmReleaseContext(HWND, _defaultImc); + } + + _defaultImc = IntPtr.Zero; + HWND = IntPtr.Zero; + _parent = null; + _active = false; + _langId = 0; + _showCompositionWindow = false; + _showCandidateList = false; + + IsComposing = false; + } + //Dependant on CurrentThread. When Avalonia will support Multiple Dispatchers - //every Dispatcher should have their own InputMethod. public static Imm32InputMethod Current { get; } = new Imm32InputMethod(); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 64ab15bc30..cae8834550 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -82,6 +82,12 @@ namespace Avalonia.Win32 case WindowsMessage.WM_DESTROY: { UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null); + + // We need to release IMM context and state to avoid leaks. + if (Imm32InputMethod.Current.HWND == _hwnd) + { + Imm32InputMethod.Current.ClearLanguageAndWindow(); + } //Window doesn't exist anymore _hwnd = IntPtr.Zero; From 51789f56a629ead42483d3cacfedca229374ac9d Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 27 May 2022 01:20:53 -0400 Subject: [PATCH 721/820] Bring back mmap for linux as discussed --- .../Platform/Internal/UnmanagedBlob.cs | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs index ed0862c06c..a56d9ffd1c 100644 --- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -40,7 +40,7 @@ internal class UnmanagedBlob : IUnmanagedBlob { if (size <= 0) throw new ArgumentException("Positive number required", nameof(size)); - _address = Marshal.AllocHGlobal(size); + _address = Alloc(size); GC.AddMemoryPressure(size); Size = size; } @@ -66,7 +66,7 @@ internal class UnmanagedBlob : IUnmanagedBlob lock (_btlock) Backtraces.Remove(_backtrace); #endif - Marshal.FreeHGlobal(_address); + Free(_address, Size); GC.RemoveMemoryPressure(Size); IsDisposed = true; _address = IntPtr.Zero; @@ -106,4 +106,50 @@ internal class UnmanagedBlob : IUnmanagedBlob public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; public int Size { get; private set; } public bool IsDisposed { get; private set; } + + [DllImport("libc", SetLastError = true)] + private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); + [DllImport("libc", SetLastError = true)] + private static extern int munmap(IntPtr addr, IntPtr length); + [DllImport("libc", SetLastError = true)] + private static extern long sysconf(int name); + + private bool? _useMmap; + private bool UseMmap + => _useMmap ?? ((_useMmap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)).Value); + + // Could be replaced with https://github.com/dotnet/runtime/issues/40892 when it will be available. + private IntPtr Alloc(int size) + { + if (!UseMmap) + { + return Marshal.AllocHGlobal(size); + } + else + { + var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); + if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) + { + var errno = Marshal.GetLastWin32Error(); + throw new Exception("Unable to allocate memory: " + errno); + } + return rv; + } + } + + private void Free(IntPtr ptr, int len) + { + if (!UseMmap) + { + Marshal.FreeHGlobal(ptr); + } + else + { + if (munmap(ptr, new IntPtr(len)) == -1) + { + var errno = Marshal.GetLastWin32Error(); + throw new Exception("Unable to free memory: " + errno); + } + } + } } From 74d951235fad07ae507c4c870c4b69e55578a15e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:09:58 +0200 Subject: [PATCH 722/820] fix: move DynamicProxyGenAssembly2 InternalsVisibleTo to csproj --- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 5bd733a17e..16a91b10d6 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -33,5 +33,6 @@ + diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 3753588fce..9a62f94e35 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -17,4 +17,3 @@ using Avalonia.Metadata; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] From cc91c3002fea1c50893d2c7e2b5e36637ab1922a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:12:56 +0200 Subject: [PATCH 723/820] fix: AvaloniaPublicKey.props file name typo --- Directory.Build.props | 2 +- build/{AvaloniaPrublicKey.props => AvaloniaPublicKey.props} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename build/{AvaloniaPrublicKey.props => AvaloniaPublicKey.props} (100%) diff --git a/Directory.Build.props b/Directory.Build.props index adfb985d2b..42daa2df7f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - + $(MSBuildThisFileDirectory)build-intermediate/nuget $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll diff --git a/build/AvaloniaPrublicKey.props b/build/AvaloniaPublicKey.props similarity index 100% rename from build/AvaloniaPrublicKey.props rename to build/AvaloniaPublicKey.props From b2714472fea7c4f329b8f1c53ee28a9ee73356d0 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:28:52 +0200 Subject: [PATCH 724/820] fix: remove Microsoft.NETFramework.ReferenceAssemblies package --- build/NetFX.props | 1 - 1 file changed, 1 deletion(-) diff --git a/build/NetFX.props b/build/NetFX.props index 27bac831b8..14adb54035 100644 --- a/build/NetFX.props +++ b/build/NetFX.props @@ -1,7 +1,6 @@  - From bb796a1a7374d4652ab453df53b3499747bf46d2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:44:42 +0200 Subject: [PATCH 725/820] fix(Carousel): PageTransition nullable --- src/Avalonia.Controls/Carousel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Carousel.cs b/src/Avalonia.Controls/Carousel.cs index 4cacf51fa1..28a4aa6436 100644 --- a/src/Avalonia.Controls/Carousel.cs +++ b/src/Avalonia.Controls/Carousel.cs @@ -20,8 +20,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = - AvaloniaProperty.Register(nameof(PageTransition)); + public static readonly StyledProperty PageTransitionProperty = + AvaloniaProperty.Register(nameof(PageTransition)); /// /// The default value of for @@ -54,7 +54,7 @@ namespace Avalonia.Controls /// /// Gets or sets the transition to use when moving between pages. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get { return GetValue(PageTransitionProperty); } set { SetValue(PageTransitionProperty, value); } From 0223eb371616722e941025b2a761112c37392069 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 10:25:48 +0200 Subject: [PATCH 726/820] fix: Android nullable --- src/Android/Avalonia.Android/AvaloniaView.cs | 2 +- .../Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index 8177cf1f69..bbd3e0af9f 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -15,7 +15,7 @@ namespace Avalonia.Android private EmbeddableControlRoot _root; private readonly ViewImpl _view; - private IDisposable? _timerSubscription; + private IDisposable _timerSubscription; public AvaloniaView(Context context) : base(context) { diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 2b2a9dd2b4..4cae700c0a 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -30,7 +30,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers return DispatchKeyEventInternal(e, out callBase); } - string? UnicodeTextInput(KeyEvent keyEvent) + string UnicodeTextInput(KeyEvent keyEvent) { return keyEvent.Action == KeyEventActions.Multiple && keyEvent.RepeatCount == 0 From d51fc3f5e2a6fbce189185b400cb188f1f4c2899 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 10:26:23 +0200 Subject: [PATCH 727/820] fix: X11 nullable --- src/Avalonia.X11/X11Window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 066156a652..28455d5e31 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -1160,7 +1160,7 @@ namespace Avalonia.X11 } public IntPtr Handle => _owner._renderHandle; - public string? HandleDescriptor => "XID"; + public string HandleDescriptor => "XID"; } } } From 2fa549d62a44c52fb46aba55e8b9e7b4288861ec Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 10:26:54 +0200 Subject: [PATCH 728/820] fix: Win32 nullable --- src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs | 2 +- .../Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 1085aa1b42..1ec0ee9e2e 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -42,7 +42,7 @@ namespace Avalonia.Win32.Automation return GetOrCreate(focus); } - public void FocusChanged(object sender, EventArgs e) + public void FocusChanged(object? sender, EventArgs e) { RaiseFocusChanged(GetOrCreate(Peer.GetFocus())); } diff --git a/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs b/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs index 1de0cf0f9b..8d6677315c 100644 --- a/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs +++ b/src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Runtime.InteropServices; From fb34e56b0770a796c941a9bfd9e13e2a2e3afe59 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 11:42:43 +0200 Subject: [PATCH 729/820] fix(CarouselPresenter ): PageTransition --- src/Avalonia.Controls/Presenters/CarouselPresenter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 3d1e7eb5a8..87bbfce2a2 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -31,7 +31,7 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = + public static readonly StyledProperty PageTransitionProperty = Carousel.PageTransitionProperty.AddOwner(); private int _selectedIndex = -1; @@ -85,7 +85,7 @@ namespace Avalonia.Controls.Presenters /// /// Gets or sets a transition to use when switching pages. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get { return GetValue(PageTransitionProperty); } set { SetValue(PageTransitionProperty, value); } From b605c5c4527013f568d37b85975103059933b9b3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 27 May 2022 10:51:37 +0100 Subject: [PATCH 730/820] [OSX] fix border shadow invalidation. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 022769bad0..6dc59ae4d8 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -296,6 +296,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if(Window != nullptr) { [Window setContentSize:lastSize]; + [Window invalidateShadow]; } } @finally { @@ -583,6 +584,8 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; + + [Window invalidateShadow]; if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu]; From 84ea185f0faf0d39e6d8db8be784913d9fa4dfda Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 27 May 2022 16:10:16 +0100 Subject: [PATCH 731/820] [OSX] dialogs stay ontop of main window when another app is displayed. --- native/Avalonia.Native/src/OSX/AvnView.mm | 4 ++-- native/Avalonia.Native/src/OSX/AvnWindow.mm | 2 ++ native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 02526afbcb..5436ad22f3 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -222,7 +222,7 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type { - bool triggerInputWhenDisabled = type != Move; + bool triggerInputWhenDisabled = type != Move && type != LeaveWindow; if([self ignoreUserInput: triggerInputWhenDisabled]) { @@ -709,4 +709,4 @@ return [[self accessibilityChild] accessibilityFocusedUIElement]; } -@end \ No newline at end of file +@end diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index f51c693777..590dc5e7ac 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -378,6 +378,8 @@ _parent->BaseEvents->Deactivated(); [self showAppMenuOnly]; + + [self invalidateShadow]; [super resignKeyWindow]; } diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 6dc59ae4d8..121679b942 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -558,6 +558,8 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { CleanNSWindow(); Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + + [Window setHidesOnDeactivate:false]; } } else { if (![Window isKindOfClass:[AvnWindow class]]) { From 960ea2260cebf30d7f79d4d6e85cb96afdd2271c Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 28 May 2022 16:45:55 +0200 Subject: [PATCH 732/820] Add xdg-desktop-portal file chooser --- src/Avalonia.FreeDesktop/DBusFileChooser.cs | 32 ++++++++ src/Avalonia.FreeDesktop/DBusRequest.cs | 16 ++++ src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 80 ++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/Avalonia.FreeDesktop/DBusFileChooser.cs create mode 100644 src/Avalonia.FreeDesktop/DBusRequest.cs create mode 100644 src/Avalonia.FreeDesktop/DBusSystemDialog.cs diff --git a/src/Avalonia.FreeDesktop/DBusFileChooser.cs b/src/Avalonia.FreeDesktop/DBusFileChooser.cs new file mode 100644 index 0000000000..996f5dfe86 --- /dev/null +++ b/src/Avalonia.FreeDesktop/DBusFileChooser.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Tmds.DBus; + +[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] +namespace Avalonia.FreeDesktop +{ + [DBusInterface("org.freedesktop.portal.FileChooser")] + public interface IFileChooser : IDBusObject + { + Task OpenFileAsync(string ParentWindow, string Title, IDictionary Options); + Task SaveFileAsync(string ParentWindow, string Title, IDictionary Options); + Task SaveFilesAsync(string ParentWindow, string Title, IDictionary Options); + Task GetAsync(string prop); + Task GetAllAsync(); + Task SetAsync(string prop, object val); + Task WatchPropertiesAsync(Action handler); + } + + [Dictionary] + public class FileChooserProperties + { + public uint Version { get; set; } + } + + public static class FileChooserExtensions + { + public static Task GetVersionAsync(this IFileChooser o) => o.GetAsync("version"); + } +} diff --git a/src/Avalonia.FreeDesktop/DBusRequest.cs b/src/Avalonia.FreeDesktop/DBusRequest.cs new file mode 100644 index 0000000000..940a476916 --- /dev/null +++ b/src/Avalonia.FreeDesktop/DBusRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Tmds.DBus; + +[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] +namespace Avalonia.FreeDesktop +{ + [DBusInterface("org.freedesktop.portal.Request")] + internal interface IRequest : IDBusObject + { + Task CloseAsync(); + Task WatchResponseAsync(Action<(uint response, IDictionary results)> handler, Action onError = null); + } +} diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs new file mode 100644 index 0000000000..d76e22b113 --- /dev/null +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Tmds.DBus; + +namespace Avalonia.FreeDesktop +{ + public class DBusSystemDialog : ISystemDialogImpl + { + private readonly IFileChooser _fileChooser; + + public DBusSystemDialog() + { + _fileChooser = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); + } + + public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) + { + var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; + ObjectPath objectPath; + var options = new Dictionary(); + if (dialog.Filters is not null) + options.Add("filters", ParseFilters(dialog)); + + switch (dialog) + { + case OpenFileDialog openFileDialog: + options.Add("multiple", openFileDialog.AllowMultiple); + objectPath = await _fileChooser.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); + break; + case SaveFileDialog saveFileDialog: + if (saveFileDialog.InitialFileName is not null) + options.Add("current_name", saveFileDialog.InitialFileName); + if (saveFileDialog.Directory is not null) + options.Add("current_folder", Encoding.UTF8.GetBytes(saveFileDialog.Directory)); + objectPath = await _fileChooser.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); + break; + } + + var request = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Request", objectPath); + var tsc = new TaskCompletionSource(); + using var disposable = await request.WatchResponseAsync(x => tsc.TrySetResult(x.results["uris"] as string[])); + var uris = await tsc.Task; + for (var i = 0; i < uris.Length; i++) + uris[i] = new Uri(uris[i]).AbsolutePath; + return uris; + } + + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) + { + var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; + var options = new Dictionary + { + { "directory", true } + }; + var objectPath = await _fileChooser.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); + var request = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Request", objectPath); + var tsc = new TaskCompletionSource(); + using var disposable = await request.WatchResponseAsync(x => tsc.TrySetResult(x.results["uris"] as string[])); + var uris = await tsc.Task; + return uris.Length != 1 ? string.Empty : new Uri(uris[0]).AbsolutePath; + } + + private static (string name, (uint style, string extension)[])[] ParseFilters(FileDialog dialog) + { + var filters = new (string name, (uint style, string extension)[])[dialog.Filters!.Count]; + for (var i = 0; i < filters.Length; i++) + { + var extensions = dialog.Filters[i].Extensions.Select(static x => (0u, x)).ToArray(); + filters[i] = (dialog.Filters[i].Name, extensions); + } + + return filters; + } + } +} From 4c0fda3495d14b5357a161218aca67592b3663aa Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sat, 28 May 2022 22:54:14 +0200 Subject: [PATCH 733/820] Make classes internal --- src/Avalonia.FreeDesktop/DBusFileChooser.cs | 6 +++--- src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusFileChooser.cs b/src/Avalonia.FreeDesktop/DBusFileChooser.cs index 996f5dfe86..24db614a02 100644 --- a/src/Avalonia.FreeDesktop/DBusFileChooser.cs +++ b/src/Avalonia.FreeDesktop/DBusFileChooser.cs @@ -8,7 +8,7 @@ using Tmds.DBus; namespace Avalonia.FreeDesktop { [DBusInterface("org.freedesktop.portal.FileChooser")] - public interface IFileChooser : IDBusObject + internal interface IFileChooser : IDBusObject { Task OpenFileAsync(string ParentWindow, string Title, IDictionary Options); Task SaveFileAsync(string ParentWindow, string Title, IDictionary Options); @@ -20,12 +20,12 @@ namespace Avalonia.FreeDesktop } [Dictionary] - public class FileChooserProperties + internal class FileChooserProperties { public uint Version { get; set; } } - public static class FileChooserExtensions + internal static class FileChooserExtensions { public static Task GetVersionAsync(this IFileChooser o) => o.GetAsync("version"); } diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index d76e22b113..88f3e528e5 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -9,11 +9,11 @@ using Tmds.DBus; namespace Avalonia.FreeDesktop { - public class DBusSystemDialog : ISystemDialogImpl + internal class DBusSystemDialog : ISystemDialogImpl { private readonly IFileChooser _fileChooser; - public DBusSystemDialog() + internal DBusSystemDialog() { _fileChooser = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); } From 8026fb1047e9ce309056787d7277aa36ff631c70 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Sun, 29 May 2022 12:15:00 +0200 Subject: [PATCH 734/820] Fall back to managed dialogs when xdg-desktop-portal is unavailable --- .../ManagedFileDialogExtensions.cs | 8 ++- .../Avalonia.FreeDesktop.csproj | 2 + src/Avalonia.FreeDesktop/DBusHelper.cs | 24 +++---- src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 68 +++++++++++++++---- src/Avalonia.X11/X11Platform.cs | 9 ++- 5 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs index 1970c5557d..effdc847a0 100644 --- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs +++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs @@ -139,9 +139,15 @@ namespace Avalonia.Dialogs return builder; } + public static Task ShowManagedAsync(this FileDialog dialog, Window parent) + => new ManagedSystemDialogImpl().ShowFileDialogAsync(dialog, parent); + + public static Task ShowManagedAsync(this OpenFolderDialog dialog, Window parent) + => new ManagedSystemDialogImpl().ShowFolderDialogAsync(dialog, parent); + public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent, ManagedFileDialogOptions options = null) => ShowManagedAsync(dialog, parent, options); - + public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent, ManagedFileDialogOptions options = null) where TWindow : Window, new() { diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index e9d6394aa5..a5cb207223 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -2,10 +2,12 @@ net6.0;netstandard2.0 + enable + diff --git a/src/Avalonia.FreeDesktop/DBusHelper.cs b/src/Avalonia.FreeDesktop/DBusHelper.cs index c14539d7bf..7204e51dbd 100644 --- a/src/Avalonia.FreeDesktop/DBusHelper.cs +++ b/src/Avalonia.FreeDesktop/DBusHelper.cs @@ -6,7 +6,7 @@ using Tmds.DBus; namespace Avalonia.FreeDesktop { - public class DBusHelper + public static class DBusHelper { /// /// This class uses synchronous execution at DBus connection establishment stage @@ -14,14 +14,14 @@ namespace Avalonia.FreeDesktop /// private class DBusSyncContext : SynchronizationContext { - private SynchronizationContext _ctx; - private object _lock = new object(); + private readonly object _lock = new(); + private SynchronizationContext? _ctx; public override void Post(SendOrPostCallback d, object state) { lock (_lock) { - if (_ctx != null) + if (_ctx is not null) _ctx?.Post(d, state); else lock (_lock) @@ -33,10 +33,9 @@ namespace Avalonia.FreeDesktop { lock (_lock) { - if (_ctx != null) + if (_ctx is not null) _ctx?.Send(d, state); else - d(state); } } @@ -47,15 +46,14 @@ namespace Avalonia.FreeDesktop _ctx = new AvaloniaSynchronizationContext(); } } - public static Connection Connection { get; private set; } - public static Connection TryInitialize(string dbusAddress = null) + public static Connection? Connection { get; private set; } + + public static Connection? TryInitialize(string? dbusAddress = null) + => Connection ?? TryCreateNewConnection(dbusAddress); + + public static Connection? TryCreateNewConnection(string? dbusAddress = null) { - return Connection ?? TryCreateNewConnection(dbusAddress); - } - - public static Connection TryCreateNewConnection(string dbusAddress = null) - { var oldContext = SynchronizationContext.Current; try { diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index 88f3e528e5..7bc287ea28 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -5,20 +5,56 @@ using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Platform; +using Avalonia.Dialogs; +using Avalonia.Logging; using Tmds.DBus; namespace Avalonia.FreeDesktop { internal class DBusSystemDialog : ISystemDialogImpl { - private readonly IFileChooser _fileChooser; + private readonly IFileChooser? _fileChooser; + private bool _isDbusAvailable; internal DBusSystemDialog() { - _fileChooser = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); + _fileChooser = DBusHelper.Connection?.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); + _isDbusAvailable = _fileChooser is not null; } - public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) + public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) + { + if (!_isDbusAvailable) + return await dialog.ShowManagedAsync(parent); + try + { + return await ShowNativeFileDialogAsync(dialog, parent); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(this, e.Message); + _isDbusAvailable = false; + return await dialog.ShowManagedAsync(parent); + } + } + + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) + { + if (!_isDbusAvailable) + return await dialog.ShowManagedAsync(parent); + try + { + return await ShowNativeFolderDialogAsync(dialog, parent); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(this, e.Message); + _isDbusAvailable = false; + return await dialog.ShowManagedAsync(parent); + } + } + + private async Task ShowNativeFileDialogAsync(FileDialog dialog, Window parent) { var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; ObjectPath objectPath; @@ -30,38 +66,42 @@ namespace Avalonia.FreeDesktop { case OpenFileDialog openFileDialog: options.Add("multiple", openFileDialog.AllowMultiple); - objectPath = await _fileChooser.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); + objectPath = await _fileChooser!.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); break; case SaveFileDialog saveFileDialog: if (saveFileDialog.InitialFileName is not null) options.Add("current_name", saveFileDialog.InitialFileName); if (saveFileDialog.Directory is not null) options.Add("current_folder", Encoding.UTF8.GetBytes(saveFileDialog.Directory)); - objectPath = await _fileChooser.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); + objectPath = await _fileChooser!.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); break; } - var request = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Request", objectPath); - var tsc = new TaskCompletionSource(); - using var disposable = await request.WatchResponseAsync(x => tsc.TrySetResult(x.results["uris"] as string[])); + var request = DBusHelper.Connection!.CreateProxy("org.freedesktop.portal.Request", objectPath); + var tsc = new TaskCompletionSource(); + using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); var uris = await tsc.Task; + if (uris is null) + return null; for (var i = 0; i < uris.Length; i++) uris[i] = new Uri(uris[i]).AbsolutePath; return uris; } - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) + private async Task ShowNativeFolderDialogAsync(OpenFolderDialog dialog, Window parent) { var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; var options = new Dictionary { { "directory", true } }; - var objectPath = await _fileChooser.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); - var request = DBusHelper.Connection.CreateProxy("org.freedesktop.portal.Request", objectPath); - var tsc = new TaskCompletionSource(); - using var disposable = await request.WatchResponseAsync(x => tsc.TrySetResult(x.results["uris"] as string[])); + var objectPath = await _fileChooser!.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); + var request = DBusHelper.Connection!.CreateProxy("org.freedesktop.portal.Request", objectPath); + var tsc = new TaskCompletionSource(); + using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); var uris = await tsc.Task; + if (uris is null) + return null; return uris.Length != 1 ? string.Empty : new Uri(uris[0]).AbsolutePath; } @@ -71,7 +111,7 @@ namespace Avalonia.FreeDesktop for (var i = 0; i < filters.Length; i++) { var extensions = dialog.Filters[i].Extensions.Select(static x => (0u, x)).ToArray(); - filters[i] = (dialog.Filters[i].Name, extensions); + filters[i] = (dialog.Filters[i].Name ?? string.Empty, extensions); } return filters; diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index ec3f29c806..8765299d1d 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -15,7 +15,6 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.X11; using Avalonia.X11.Glx; -using Avalonia.X11.NativeDialogs; using static Avalonia.X11.XLib; namespace Avalonia.X11 @@ -80,7 +79,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new X11Clipboard(this)) .Bind().ToConstant(new PlatformSettingsStub()) .Bind().ToConstant(new X11IconLoader(Info)) - .Bind().ToConstant(new GtkSystemDialog()) + .Bind().ToConstant(new DBusSystemDialog()) .Bind().ToConstant(new LinuxMountedVolumeInfoProvider()) .Bind().ToConstant(new X11PlatformLifetimeEvents(this)); @@ -209,10 +208,10 @@ namespace Avalonia public bool OverlayPopups { get; set; } /// - /// Enables global menu support on Linux desktop environments where it's supported (e. g. XFCE and MATE with plugin, KDE, etc). - /// The default value is false. + /// Enables native file dialogs as well as global menu support on Linux desktop environments where it's supported (e. g. XFCE and MATE with plugin, KDE, etc). + /// The default value is true. /// - public bool UseDBusMenu { get; set; } + public bool UseDBusMenu { get; set; } = true; /// /// Deferred renderer would be used when set to true. Immediate renderer when set to false. The default value is true. From 0a4b3386d5e06f49d123ac49cdc4105a316c4a19 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 29 May 2022 22:02:18 +0100 Subject: [PATCH 735/820] [OSX] fix SetParent --- native/Avalonia.Native/src/OSX/WindowImpl.h | 1 + native/Avalonia.Native/src/OSX/WindowImpl.mm | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index db19497b29..35627685a2 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -22,6 +22,7 @@ private: bool _transitioningWindowState; bool _isClientAreaExtended; bool _isDialog; + WindowImpl* _lastParent; AvnExtendClientAreaChromeHints _extendClientHints; FORWARD_IUNKNOWN() diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index d96fe717ab..ad804eb280 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,6 +20,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; _lastTitle = @""; + _lastParent = nullptr; WindowEvents = events; } @@ -61,6 +62,11 @@ void WindowImpl::OnInitialiseNSWindow(){ [GetWindowProtocol() setIsExtended:true]; SetExtendClientArea(true); } + + if(_lastParent != nullptr) + { + SetParent(_lastParent); + } } HRESULT WindowImpl::Show(bool activate, bool isDialog) { @@ -97,6 +103,10 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { if (cparent == nullptr) return E_INVALIDARG; + + _lastParent = cparent; + + if(Window != nullptr){ // 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 // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. @@ -107,6 +117,7 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; UpdateStyle(); + } return S_OK; } @@ -535,7 +546,7 @@ NSWindowStyleMask WindowImpl::GetStyle() { break; case SystemDecorationsFull: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; if (_canResize) { s = s | NSWindowStyleMaskResizable; From d2af5dbcac4427c22830d0bb6a2764befa180a72 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 29 May 2022 22:59:50 -0400 Subject: [PATCH 736/820] Implicitly map x:DataType to a DataType on a DataTemplate. --- .../AvaloniaXamlIlCompiler.cs | 8 +- .../Transformers/XDataTypeTransformer.cs | 79 +++++++++++++++++++ .../Xaml/DataTemplateTests.cs | 58 ++++++++++++++ .../Xaml/TreeDataTemplateTests.cs | 17 ++++ 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 1ca7be67a7..7514b0e12e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -31,10 +31,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions // Before everything else Transformers.Insert(0, new XNameTransformer()); - Transformers.Insert(1, new IgnoredDirectivesTransformer()); - Transformers.Insert(2, _designTransformer = new AvaloniaXamlIlDesignPropertiesTransformer()); - Transformers.Insert(3, _bindingTransformer = new AvaloniaBindingExtensionTransformer()); - + Transformers.Insert(1, new XDataTypeTransformer()); + Transformers.Insert(2, new IgnoredDirectivesTransformer()); + Transformers.Insert(3, _designTransformer = new AvaloniaXamlIlDesignPropertiesTransformer()); + Transformers.Insert(4, _bindingTransformer = new AvaloniaBindingExtensionTransformer()); // Targeted InsertBefore( diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs new file mode 100644 index 0000000000..7b90164974 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using XamlX; +using XamlX.Ast; +using XamlX.Transform; +using XamlX.Transform.Transformers; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers +{ + internal class XDataTypeTransformer : IXamlAstTransformer + { + private const string DataTypePropertyName = "DataType"; + + /// + /// Converts x:DataType directives to regular DataType assignments if property with Avalonia.Metadata.DataTypeAttribute exists. + /// + /// + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (node is XamlAstObjectNode on) + { + for (var c = 0; c < on.Children.Count; c++) + { + var ch = on.Children[c]; + if (ch is XamlAstXmlDirective { Namespace: XamlNamespaces.Xaml2006, Name: DataTypePropertyName } d) + { + if (on.Children.OfType() + .Any(p => ((XamlAstNamePropertyReference)p.Property)?.Name == DataTypePropertyName)) + { + // Break iteration if any DataType property was already set by user code. + break; + } + + var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute; + + var clrType = on.Type switch + { + XamlAstClrTypeReference clrRef => clrRef.Type, + XamlAstXmlTypeReference xmlRef => TypeReferenceResolver.ResolveType(context, xmlRef.Name, + on.Type.IsMarkupExtension, on, strict: false).Type, + _ => null + }; + if (clrType is null) + { + break; + } + + // Technically it's possible to map "x:DataType" to a property with [DataType] attribute regardless of its name, + // but we go explicitly strict here and check the name as well. + var (declaringType, dataTypeProperty) = GetAllProperties(clrType) + .FirstOrDefault(t => t.property.Name == DataTypePropertyName && t.property.CustomAttributes + .Any(a => a.Type == templateDataTypeAttribute)); + + if (dataTypeProperty is not null) + { + on.Children[c] = new XamlAstXamlPropertyValueNode(d, + new XamlAstNamePropertyReference(d, + new XamlAstClrTypeReference(ch, declaringType, false), dataTypeProperty.Name, + on.Type), + d.Values); + } + } + } + } + + return node; + } + + private static IEnumerable<(IXamlType declaringType, IXamlProperty property)> GetAllProperties(IXamlType t) + { + foreach (var p in t.Properties) + yield return (t, p); + if(t.BaseType!=null) + foreach (var tuple in GetAllProperties(t.BaseType)) + yield return tuple; + } + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs index 53881467e7..f9e1ce3054 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs @@ -1,5 +1,7 @@ +using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Markup.Xaml.Templates; using Avalonia.UnitTests; using Xunit; @@ -89,6 +91,62 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void XDataType_Should_Be_Assigned_To_Clr_Property() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + var template = (DataTemplate)window.DataTemplates.First(); + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(typeof(string), template.DataType); + Assert.IsType(target.Presenter.Child); + } + } + + [Fact] + public void XDataType_Should_Be_Ignored_If_DataType_Already_Set() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.IsType(target.Presenter.Child); + } + } + [Fact] public void Can_Set_DataContext_In_DataTemplate() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs index 3fdac49f31..d4ab473d67 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs @@ -21,5 +21,22 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.IsType(template.ItemsSource); } } + + [Fact] + public void XDataType_Should_Be_Assigned_To_Clr_Property() + { + using (UnitTestApplication.Start(TestServices.MockPlatformWrapper)) + { + var xaml = @" + + +"; + var templates = (DataTemplates)AvaloniaRuntimeXamlLoader.Load(xaml); + var template = (TreeDataTemplate)(templates.First()); + + Assert.Equal(typeof(string), template.DataType); + } + } } } From 1d9645f01fda85fcbc80e11090cd672b536a559b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 29 May 2022 23:38:20 -0400 Subject: [PATCH 737/820] Validate DataTemplates --- .../Templates/DataTemplates.cs | 17 +++++++++++++++ .../Templates/ITypedDataTemplate.cs | 10 +++++++++ .../Templates/DataTemplate.cs | 2 +- .../Templates/TreeDataTemplate.cs | 2 +- .../CompiledBindingExtensionTests.cs | 4 ++-- .../Xaml/ControlBindingTests.cs | 8 +++---- .../Xaml/DataTemplateTests.cs | 21 +++++++++++++++++++ .../Xaml/TreeDataTemplateTests.cs | 2 +- 8 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 src/Avalonia.Controls/Templates/ITypedDataTemplate.cs diff --git a/src/Avalonia.Controls/Templates/DataTemplates.cs b/src/Avalonia.Controls/Templates/DataTemplates.cs index f203539536..d4eeda7908 100644 --- a/src/Avalonia.Controls/Templates/DataTemplates.cs +++ b/src/Avalonia.Controls/Templates/DataTemplates.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Collections; namespace Avalonia.Controls.Templates @@ -13,6 +14,22 @@ namespace Avalonia.Controls.Templates public DataTemplates() { ResetBehavior = ResetBehavior.Remove; + + Validate += ValidateDataTemplate; + } + + private static void ValidateDataTemplate(IDataTemplate template) + { + var valid = template switch + { + ITypedDataTemplate typed => typed.DataType is not null, + _ => true + }; + + if (!valid) + { + throw new InvalidOperationException("DataTemplate inside of DataTemplates must have a DataType set. Set DataType property or use ItemTemplate with single template instead."); + } } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Templates/ITypedDataTemplate.cs b/src/Avalonia.Controls/Templates/ITypedDataTemplate.cs new file mode 100644 index 0000000000..239dbd79f4 --- /dev/null +++ b/src/Avalonia.Controls/Templates/ITypedDataTemplate.cs @@ -0,0 +1,10 @@ +using System; +using Avalonia.Metadata; + +namespace Avalonia.Controls.Templates; + +public interface ITypedDataTemplate : IDataTemplate +{ + [DataType] + Type? DataType { get; } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs index d2b24979cc..4da6b1b791 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs @@ -5,7 +5,7 @@ using Avalonia.Metadata; namespace Avalonia.Markup.Xaml.Templates { - public class DataTemplate : IRecyclingDataTemplate + public class DataTemplate : IRecyclingDataTemplate, ITypedDataTemplate { [DataType] public Type DataType { get; set; } diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index 10061c3d48..04e8b0a9c0 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs @@ -9,7 +9,7 @@ using Avalonia.Metadata; namespace Avalonia.Markup.Xaml.Templates { - public class TreeDataTemplate : ITreeDataTemplate + public class TreeDataTemplate : ITreeDataTemplate, ITypedDataTemplate { [DataType] public Type DataType { get; set; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 7e721fd7b2..555a05638b 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -413,11 +413,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests' x:DataType='local:TestDataContext'> - + - + "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs index 8188b212e1..affa292a7d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs @@ -74,18 +74,18 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml - + - + - + - + "; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs index 53881467e7..abbcf6c5a8 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.UnitTests; @@ -132,5 +133,25 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Same(viewModel.Child.Child, canvas.DataContext); } } + + [Fact] + public void DataTemplates_Without_Type_Should_Throw() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + Assert.Throws(() => (Window)AvaloniaRuntimeXamlLoader.Load(xaml)); + } + } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs index 3fdac49f31..807b37517a 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TreeDataTemplateTests.cs @@ -14,7 +14,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml { using (UnitTestApplication.Start(TestServices.MockPlatformWrapper)) { - var xaml = ""; + var xaml = ""; var templates = (DataTemplates)AvaloniaRuntimeXamlLoader.Load(xaml); var template = (TreeDataTemplate)(templates.First()); From b0f7871eecd9380f81a111de001b32a4cdd821fa Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Mon, 30 May 2022 19:14:37 +0300 Subject: [PATCH 738/820] Don't allow bindings to private methods. --- .../Data/Converters/DefaultValueConverter.cs | 9 +++++- .../Data/BindingTests_Method.cs | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index 11d50afe93..e531cfd7be 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -33,7 +33,14 @@ namespace Avalonia.Data.Converters if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1) { - return new MethodToCommandConverter(d); + if (d.Method.Attributes.HasFlag(System.Reflection.MethodAttributes.Private) == false) + { + return new MethodToCommandConverter(d); + } + else + { + return new BindingNotification(new InvalidCastException("You can't bind to private methods!"), BindingErrorType.Error); + } } if (TypeUtilities.TryConvert(targetType, value, culture, out var result)) diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs new file mode 100644 index 0000000000..e613a178d5 --- /dev/null +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs @@ -0,0 +1,32 @@ +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Interactivity; +using Xunit; + +namespace Avalonia.Markup.UnitTests.Data +{ + public class BindingTests_Method + { + [Fact] + public void Binding_To_Private_Methods_Shouldnt_Work() + { + var vm = new TestClass(); + var target = new Button + { + DataContext = vm, + [!Button.CommandProperty] = new Binding("MyMethod"), + }; + target.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent)); + + Assert.False(vm.IsSet); + } + + + class TestClass + { + public bool IsSet { get; set; } + private void MyMethod() => IsSet = true; + } + } +} From 83b5338a2855b7a0e1643c810895ebd3f765d1bc Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Mon, 30 May 2022 19:51:36 +0300 Subject: [PATCH 739/820] Check for method being Private correctly. --- src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index e531cfd7be..c4f4362537 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -33,7 +33,7 @@ namespace Avalonia.Data.Converters if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1) { - if (d.Method.Attributes.HasFlag(System.Reflection.MethodAttributes.Private) == false) + if (d.Method.IsPrivate == false) { return new MethodToCommandConverter(d); } From 421b8227c5bb2a6544ac1f494759766db3e99bb8 Mon Sep 17 00:00:00 2001 From: Mike James Date: Mon, 30 May 2022 18:43:01 +0100 Subject: [PATCH 740/820] Updating the readme to clarify donations vs support agreements I've attempted to clarify that donations are separate from commercial support in the hope of minimising any future confusion. --- readme.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 1cdaf3b8f8..a8043b8690 100644 --- a/readme.md +++ b/readme.md @@ -70,11 +70,13 @@ For more information see the [.NET Foundation Code of Conduct](https://dotnetfou Avalonia is licenced under the [MIT licence](licence.md). -## Support Avalonia +## Donate -**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx +Donating to the project is a fantastic way to thank our valued contributors for their hard work. Your donations are shared among our community and awarded for significant contributions. + +Donate with BTC or use [Open Collective](https://opencollective.com/avalonia). -This will be shared with the community and awarded for significant contributions. +**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx ### Backers @@ -98,6 +100,11 @@ Support this project by becoming a sponsor. Your logo will show up here with a l +## Commercial Support + +We have a range of [support plans available](https://avaloniaui.net/support.html) for those looking to partner with the creators of Avalonia, enabling access to the best support at every step of the development process. + +*Please note that donations are not considered payment for commercial support agreements.* ## .NET Foundation This project is supported by the [.NET Foundation](https://dotnetfoundation.org). From 3c3fc4521ea31cf9c6562065a1552b4e2932e1c1 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Mon, 30 May 2022 20:20:27 +0200 Subject: [PATCH 741/820] Clean up DBusSystemDialog.cs + add factory method --- src/Avalonia.Dialogs/Avalonia.Dialogs.csproj | 4 ++ .../ManagedFileDialogExtensions.cs | 8 +-- src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 50 ++++++------------- src/Avalonia.X11/X11Platform.cs | 3 +- 4 files changed, 23 insertions(+), 42 deletions(-) diff --git a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj index a311efdfb0..80159c82d7 100644 --- a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj +++ b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj @@ -14,6 +14,10 @@ + + + + diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs index effdc847a0..e4025453c4 100644 --- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs +++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs @@ -8,7 +8,7 @@ namespace Avalonia.Dialogs { public static class ManagedFileDialogExtensions { - private class ManagedSystemDialogImpl : ISystemDialogImpl where T : Window, new() + internal class ManagedSystemDialogImpl : ISystemDialogImpl where T : Window, new() { async Task Show(SystemDialog d, Window parent, ManagedFileDialogOptions options = null) { @@ -139,12 +139,6 @@ namespace Avalonia.Dialogs return builder; } - public static Task ShowManagedAsync(this FileDialog dialog, Window parent) - => new ManagedSystemDialogImpl().ShowFileDialogAsync(dialog, parent); - - public static Task ShowManagedAsync(this OpenFolderDialog dialog, Window parent) - => new ManagedSystemDialogImpl().ShowFolderDialogAsync(dialog, parent); - public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent, ManagedFileDialogOptions options = null) => ShowManagedAsync(dialog, parent, options); diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index 7bc287ea28..d1905a4569 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -5,7 +5,6 @@ using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Platform; -using Avalonia.Dialogs; using Avalonia.Logging; using Tmds.DBus; @@ -13,48 +12,31 @@ namespace Avalonia.FreeDesktop { internal class DBusSystemDialog : ISystemDialogImpl { - private readonly IFileChooser? _fileChooser; - private bool _isDbusAvailable; + private readonly IFileChooser _fileChooser; - internal DBusSystemDialog() + internal static DBusSystemDialog? TryCreate() { - _fileChooser = DBusHelper.Connection?.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); - _isDbusAvailable = _fileChooser is not null; - } - - public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) - { - if (!_isDbusAvailable) - return await dialog.ShowManagedAsync(parent); + var fileChooser = DBusHelper.Connection?.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); + if (fileChooser is null) + return null; try { - return await ShowNativeFileDialogAsync(dialog, parent); + fileChooser.GetVersionAsync().GetAwaiter().GetResult(); + return new DBusSystemDialog(fileChooser); } catch (Exception e) { - Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(this, e.Message); - _isDbusAvailable = false; - return await dialog.ShowManagedAsync(parent); + Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(null, $"Unable to connect to org.freedesktop.portal.Desktop: {e.Message}"); + return null; } } - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) + private DBusSystemDialog(IFileChooser fileChooser) { - if (!_isDbusAvailable) - return await dialog.ShowManagedAsync(parent); - try - { - return await ShowNativeFolderDialogAsync(dialog, parent); - } - catch (Exception e) - { - Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(this, e.Message); - _isDbusAvailable = false; - return await dialog.ShowManagedAsync(parent); - } + _fileChooser = fileChooser; } - private async Task ShowNativeFileDialogAsync(FileDialog dialog, Window parent) + public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) { var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; ObjectPath objectPath; @@ -66,14 +48,14 @@ namespace Avalonia.FreeDesktop { case OpenFileDialog openFileDialog: options.Add("multiple", openFileDialog.AllowMultiple); - objectPath = await _fileChooser!.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); + objectPath = await _fileChooser.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); break; case SaveFileDialog saveFileDialog: if (saveFileDialog.InitialFileName is not null) options.Add("current_name", saveFileDialog.InitialFileName); if (saveFileDialog.Directory is not null) options.Add("current_folder", Encoding.UTF8.GetBytes(saveFileDialog.Directory)); - objectPath = await _fileChooser!.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); + objectPath = await _fileChooser.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); break; } @@ -88,14 +70,14 @@ namespace Avalonia.FreeDesktop return uris; } - private async Task ShowNativeFolderDialogAsync(OpenFolderDialog dialog, Window parent) + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) { var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; var options = new Dictionary { { "directory", true } }; - var objectPath = await _fileChooser!.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); + var objectPath = await _fileChooser.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); var request = DBusHelper.Connection!.CreateProxy("org.freedesktop.portal.Request", objectPath); var tsc = new TaskCompletionSource(); using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 8765299d1d..fa7ae69759 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Runtime.InteropServices; using Avalonia.Controls; using Avalonia.Controls.Platform; +using Avalonia.Dialogs; using Avalonia.FreeDesktop; using Avalonia.FreeDesktop.DBusIme; using Avalonia.Input; @@ -79,7 +80,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new X11Clipboard(this)) .Bind().ToConstant(new PlatformSettingsStub()) .Bind().ToConstant(new X11IconLoader(Info)) - .Bind().ToConstant(new DBusSystemDialog()) + .Bind().ToConstant(DBusSystemDialog.TryCreate() as ISystemDialogImpl ?? new ManagedFileDialogExtensions.ManagedSystemDialogImpl()) .Bind().ToConstant(new LinuxMountedVolumeInfoProvider()) .Bind().ToConstant(new X11PlatformLifetimeEvents(this)); From 18814cf948e363eb5b8b5fbd6a63a9016ebac376 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 May 2022 19:32:21 +0100 Subject: [PATCH 742/820] add contact address for support. --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a8043b8690..67faaba10f 100644 --- a/readme.md +++ b/readme.md @@ -104,7 +104,7 @@ Support this project by becoming a sponsor. Your logo will show up here with a l We have a range of [support plans available](https://avaloniaui.net/support.html) for those looking to partner with the creators of Avalonia, enabling access to the best support at every step of the development process. -*Please note that donations are not considered payment for commercial support agreements.* +*Please note that donations are not considered payment for commercial support agreements. Please contact us to discuss your needs first. [team@avaloniaui.net](mailto://team@avaloniaui.net)* ## .NET Foundation This project is supported by the [.NET Foundation](https://dotnetfoundation.org). From d7d6a8353ef493a2b9645e6406b18b792e737d60 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 May 2022 19:33:53 +0100 Subject: [PATCH 743/820] make it really clear. --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 67faaba10f..1009e86c29 100644 --- a/readme.md +++ b/readme.md @@ -74,6 +74,8 @@ Avalonia is licenced under the [MIT licence](licence.md). Donating to the project is a fantastic way to thank our valued contributors for their hard work. Your donations are shared among our community and awarded for significant contributions. +If you need support see Commercial Support section below. + Donate with BTC or use [Open Collective](https://opencollective.com/avalonia). **BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx From 33f6974df15221b4cddd71ee8a3e6f2b226d5ef0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 30 May 2022 21:12:43 +0200 Subject: [PATCH 744/820] Added failing test for Viewbox layout. --- .../ViewboxTests.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index 3cebe142b6..4ffd314857 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -181,6 +181,32 @@ namespace Avalonia.Controls.UnitTests Assert.Null(child.GetLogicalParent()); } + [Fact] + public void Changing_Child_Should_Invalidate_Layout() + { + var target = new Viewbox(); + + target.Child = new Canvas + { + Width = 100, + Height = 100, + }; + + target.Measure(Size.Infinity); + target.Arrange(new Rect(target.DesiredSize)); + Assert.Equal(new Size(100, 100), target.DesiredSize); + + target.Child = new Canvas + { + Width = 200, + Height = 200, + }; + + target.Measure(Size.Infinity); + target.Arrange(new Rect(target.DesiredSize)); + Assert.Equal(new Size(200, 200), target.DesiredSize); + } + private bool TryGetScale(Viewbox viewbox, out Vector scale) { if (viewbox.InternalTransform is null) From 06e0fae71774a67597503ab1cd58ea8238bf20eb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 30 May 2022 21:13:09 +0200 Subject: [PATCH 745/820] Invalidate measure when Viewbox child changes. --- src/Avalonia.Controls/Viewbox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 01a41a0157..aabfd3ef18 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -168,6 +168,8 @@ namespace Avalonia.Controls if (_child is not null) VisualChildren.Add(_child); + + InvalidateMeasure(); } } } From 496b978cdbb830a0eb44d2b11915ff3c615c492a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 30 May 2022 18:36:14 -0400 Subject: [PATCH 746/820] Run xDataType after TypeReferenceResolver --- .../CompilerExtensions/AvaloniaXamlIlCompiler.cs | 10 ++++++---- .../Transformers/XDataTypeTransformer.cs | 8 +------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 7514b0e12e..04a61e5f10 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -31,10 +31,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions // Before everything else Transformers.Insert(0, new XNameTransformer()); - Transformers.Insert(1, new XDataTypeTransformer()); - Transformers.Insert(2, new IgnoredDirectivesTransformer()); - Transformers.Insert(3, _designTransformer = new AvaloniaXamlIlDesignPropertiesTransformer()); - Transformers.Insert(4, _bindingTransformer = new AvaloniaBindingExtensionTransformer()); + Transformers.Insert(1, new IgnoredDirectivesTransformer()); + Transformers.Insert(2, _designTransformer = new AvaloniaXamlIlDesignPropertiesTransformer()); + Transformers.Insert(3, _bindingTransformer = new AvaloniaBindingExtensionTransformer()); // Targeted InsertBefore( @@ -57,6 +56,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer() ); + InsertAfter( + new XDataTypeTransformer()); + // After everything else InsertBefore( new AddNameScopeRegistration(), diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs index 7b90164974..845dc5f831 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/XDataTypeTransformer.cs @@ -34,13 +34,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute; - var clrType = on.Type switch - { - XamlAstClrTypeReference clrRef => clrRef.Type, - XamlAstXmlTypeReference xmlRef => TypeReferenceResolver.ResolveType(context, xmlRef.Name, - on.Type.IsMarkupExtension, on, strict: false).Type, - _ => null - }; + var clrType = (on.Type as XamlAstClrTypeReference)?.Type; if (clrType is null) { break; From bc2179b337e969838942fcfea868934536d9ef33 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 30 May 2022 18:49:21 -0400 Subject: [PATCH 747/820] Add XDataType_Should_Be_Ignored_If_DataType_Has_Non_Standard_Name test --- .../CompiledBindingExtensionTests.cs | 3 +- .../Xaml/DataTemplateTests.cs | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 7e721fd7b2..a8b95d3aaa 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -17,6 +17,7 @@ using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; using Avalonia.Metadata; using Avalonia.UnitTests; +using JetBrains.Annotations; using XamlX; using Xunit; @@ -1527,7 +1528,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions [TemplateContent] public object Content { get; set; } - public bool Match(object data) => FancyDataType.IsInstanceOfType(data); + public bool Match(object data) => FancyDataType?.IsInstanceOfType(data) ?? true; public IControl Build(object data) => TemplateContent.Load(Content)?.Control; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs index f9e1ce3054..6e99d9e3a6 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs @@ -1,7 +1,11 @@ +using System; using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.Markup.Xaml.Templates; +using Avalonia.Markup.Xaml.UnitTests.MarkupExtensions; +using Avalonia.Metadata; using Avalonia.UnitTests; using Xunit; @@ -147,6 +151,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void XDataType_Should_Be_Ignored_If_DataType_Has_Non_Standard_Name() + { + // We don't want DataType to be mapped to FancyDataType, avoid possible confusion. + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + var dataTemplate = (CustomDataTemplate)target.ContentTemplate; + Assert.Null(dataTemplate.FancyDataType); + } + } + [Fact] public void Can_Set_DataContext_In_DataTemplate() { From 020fa3030a45c759e350c8136128930833c17cf3 Mon Sep 17 00:00:00 2001 From: Adir Hudayfi Date: Tue, 31 May 2022 18:45:32 +0300 Subject: [PATCH 748/820] Upgraded SkiaSharp packages --- build/SkiaSharp.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index 1ee4aa56a2..d54cffba08 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + From 2e555be4020afca57fc9d5bea68920544405d69b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 31 May 2022 13:11:07 +0100 Subject: [PATCH 749/820] [OSX] programatically implement child window relationship --- native/Avalonia.Native/src/OSX/AvnView.mm | 5 ++ native/Avalonia.Native/src/OSX/AvnWindow.mm | 38 +++++------ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 7 +- native/Avalonia.Native/src/OSX/WindowImpl.h | 8 ++- native/Avalonia.Native/src/OSX/WindowImpl.mm | 66 +++++++++++++------ .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 - 7 files changed, 81 insertions(+), 46 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 5436ad22f3..bbb4d59adb 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -300,6 +300,11 @@ - (void)mouseDown:(NSEvent *)event { + if(_parent != nullptr) + { + _parent->BringToFront(); + } + _isLeftPressed = true; _lastMouseDownEvent = event; [self mouseEvent:event withType:LeftButtonDown]; diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 590dc5e7ac..1445227cf5 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -183,6 +183,11 @@ return self; } +- (void)mouseDown:(NSEvent *)event +{ + _parent->BringToFront(); +} + - (BOOL)windowShouldClose:(NSWindow *)sender { auto window = dynamic_cast(_parent.getRaw()); @@ -209,7 +214,14 @@ { ComPtr parent = _parent; _parent = NULL; - [self restoreParentWindow]; + + auto window = dynamic_cast(parent.getRaw()); + + if(window != nullptr) + { + window->SetParent(nullptr); + } + parent->BaseEvents->Closed(); [parent->View onClosed]; } @@ -220,17 +232,11 @@ if(_canBecomeKeyWindow) { // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) + auto parent = dynamic_cast(_parent.getRaw()); + + if(parent != nullptr) { - if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)]) - { - continue; - } - - id ch = (id ) uch; - - if(ch.isDialog) - return false; + return parent->CanBecomeKeyWindow(); } return true; @@ -273,16 +279,6 @@ [super becomeKeyWindow]; } --(void) restoreParentWindow; -{ - auto parent = [self parentWindow]; - - if(parent != nil) - { - [parent removeChildWindow:self]; - } -} - - (void)windowDidMiniaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 83850e780c..62c0e2069d 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -99,6 +99,8 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog(); id GetWindowProtocol (); + + virtual void BringToFront (); protected: virtual NSWindowStyleMask GetStyle(); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 121679b942..77f0f47934 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -143,8 +143,6 @@ HRESULT WindowBaseImpl::Hide() { @autoreleasepool { if (Window != nullptr) { [Window orderOut:Window]; - - [GetWindowProtocol() restoreParentWindow]; } return S_OK; @@ -610,6 +608,11 @@ id WindowBaseImpl::GetWindowProtocol() { return (id ) Window; } +void WindowBaseImpl::BringToFront() +{ + // do nothing. +} + extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) { @autoreleasepool diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 35627685a2..76d5cbf6ea 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -8,6 +8,7 @@ #import "WindowBaseImpl.h" #include "IWindowStateChanged.h" +#include class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { @@ -22,7 +23,8 @@ private: bool _transitioningWindowState; bool _isClientAreaExtended; bool _isDialog; - WindowImpl* _lastParent; + WindowImpl* _parent; + std::list _children; AvnExtendClientAreaChromeHints _extendClientHints; FORWARD_IUNKNOWN() @@ -91,6 +93,10 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog() override; virtual void OnInitialiseNSWindow() override; + + virtual void BringToFront () override; + + bool CanBecomeKeyWindow (); protected: virtual NSWindowStyleMask GetStyle() override; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index ad804eb280..5333cb23c8 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -10,6 +10,7 @@ #include "WindowProtocol.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { + _children = std::list(); _isClientAreaExtended = false; _extendClientHints = AvnDefaultChrome; _fullScreenActive = false; @@ -20,7 +21,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; _lastTitle = @""; - _lastParent = nullptr; + _parent = nullptr; WindowEvents = events; } @@ -63,9 +64,9 @@ void WindowImpl::OnInitialiseNSWindow(){ SetExtendClientArea(true); } - if(_lastParent != nullptr) + if(_parent != nullptr) { - SetParent(_lastParent); + SetParent(_parent); } } @@ -96,33 +97,56 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { START_COM_CALL; @autoreleasepool { - if (parent == nullptr) - return E_POINTER; + if(_parent != nullptr) + { + _parent->_children.remove(this); + } auto cparent = dynamic_cast(parent); - if (cparent == nullptr) - return E_INVALIDARG; - - _lastParent = cparent; + _parent = cparent; - if(Window != nullptr){ - // 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 - // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. - if (cparent->WindowState() == Minimized) - cparent->SetWindowState(Normal); - - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; - - UpdateStyle(); + if(_parent != nullptr && Window != nullptr){ + // 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 + // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. + if (cparent->WindowState() == Minimized) + cparent->SetWindowState(Normal); + + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + + cparent->_children.push_back(this); + + UpdateStyle(); } return S_OK; } } +void WindowImpl::BringToFront() +{ + Activate(); + + for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) + { + (*iterator)->BringToFront(); + } +} + +bool WindowImpl::CanBecomeKeyWindow() +{ + for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) + { + if((*iterator)->IsDialog()) + { + return false; + } + } + + return true; +} + void WindowImpl::StartStateTransition() { _transitioningWindowState = true; } @@ -534,7 +558,7 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = this->_isDialog ? NSWindowStyleMaskDocModalWindow : NSWindowStyleMaskBorderless; + unsigned long s = NSWindowStyleMaskBorderless; switch (_decorations) { case SystemDecorationsNone: diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 0e5c5869e7..cb5f86bdb9 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -11,7 +11,6 @@ @protocol AvnWindowProtocol -(void) pollModalSession: (NSModalSession _Nonnull) session; --(void) restoreParentWindow; -(bool) shouldTryToHandleEvents; -(void) setEnabled: (bool) enable; -(void) showAppMenuOnly; From 8a39240eea1f04a1d0971e3be2f693f97e56f985 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 31 May 2022 13:50:01 +0100 Subject: [PATCH 750/820] [osx] make bringtofront work correctly for owned and modal windows. --- native/Avalonia.Native/src/OSX/AvnView.mm | 5 ----- native/Avalonia.Native/src/OSX/AvnWindow.mm | 9 +++------ native/Avalonia.Native/src/OSX/WindowImpl.mm | 9 ++++++++- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index bbb4d59adb..5436ad22f3 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -300,11 +300,6 @@ - (void)mouseDown:(NSEvent *)event { - if(_parent != nullptr) - { - _parent->BringToFront(); - } - _isLeftPressed = true; _lastMouseDownEvent = event; [self mouseEvent:event withType:LeftButtonDown]; diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 1445227cf5..60fdb26121 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -183,11 +183,6 @@ return self; } -- (void)mouseDown:(NSEvent *)event -{ - _parent->BringToFront(); -} - - (BOOL)windowShouldClose:(NSWindow *)sender { auto window = dynamic_cast(_parent.getRaw()); @@ -435,8 +430,10 @@ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } + + _parent->BringToFront(); } - break; + break; case NSEventTypeMouseEntered: { diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 5333cb23c8..8330f4ed86 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -126,7 +126,14 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { void WindowImpl::BringToFront() { - Activate(); + if(IsDialog()) + { + Activate(); + } + else + { + [Window orderFront:nullptr]; + } for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) { From 61449dc40169145c0d4a05c25d5c58f46a8e26e4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 31 May 2022 14:10:33 +0100 Subject: [PATCH 751/820] dont create nspanel / nswindow at show. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 77f0f47934..d105f4cf38 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -36,8 +36,10 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMinSize = NSSize { 0, 0 }; - Window = nullptr; lastMenu = nullptr; + + CreateNSWindow(false); + InitialiseNSWindow(); } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -88,7 +90,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { - CreateNSWindow(isDialog); InitialiseNSWindow(); if(hasPosition) @@ -585,6 +586,7 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setOpaque:false]; + [Window setHasShadow:true]; [Window invalidateShadow]; if (lastMenu != nullptr) { From 2cd8ee003fd2ab660a7260af804dccbab8aa35d1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 31 May 2022 14:11:19 +0100 Subject: [PATCH 752/820] call bring to front when window is made key. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 60fdb26121..52ee48317c 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -274,6 +274,11 @@ [super becomeKeyWindow]; } +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + _parent->BringToFront(); +} + - (void)windowDidMiniaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); From b48888e9bf04ba725353c4009d5f17c20b600a94 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 31 May 2022 14:28:44 +0100 Subject: [PATCH 753/820] [osx] easily support using nspanel from windowbaseimpl. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 +- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 62c0e2069d..4220811fc7 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -26,7 +26,7 @@ BEGIN_INTERFACE_MAP() virtual ~WindowBaseImpl(); - WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); + WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel = false); virtual HRESULT ObtainNSWindowHandle(void **ret) override; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index d105f4cf38..e88c7f208c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -21,7 +21,7 @@ WindowBaseImpl::~WindowBaseImpl() { Window = nullptr; } -WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel) { _shown = false; _inResize = false; BaseEvents = events; @@ -38,7 +38,7 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastMenu = nullptr; - CreateNSWindow(false); + CreateNSWindow(usePanel); InitialiseNSWindow(); } From 131c81ad22f91d9d54fdfb1412aa6947f391764b Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 31 May 2022 11:51:54 -0700 Subject: [PATCH 754/820] replace mouse and touch events to pointer event to fix issue #8200 --- .../Avalonia.Web.Blazor/AvaloniaView.razor | 10 +- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 103 +++++++++--------- 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 3dd98f8cd3..3fe4c299cf 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -1,14 +1,12 @@ 
+ onkeyup="@OnKeyUp" + onpointerdown="@OnPointerDown" + onpointerup="@OnPointerUp" + onpointermove="@OnPointerMove"> diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index be58d9d49c..766aeb6d19 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -56,90 +56,91 @@ namespace Avalonia.Web.Blazor { return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); } - - private void OnTouchStart(TouchEventArgs e) + + private void OnTouchCancel(TouchEventArgs e) { foreach (var touch in e.ChangedTouches) { - _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(touch.ClientX, touch.ClientY), + _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY), GetModifiers(e), touch.Identifier); } } - private void OnTouchEnd(TouchEventArgs e) + private void OnTouchMove(TouchEventArgs e) { foreach (var touch in e.ChangedTouches) { - _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(touch.ClientX, touch.ClientY), + _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY), GetModifiers(e), touch.Identifier); } } - private void OnTouchCancel(TouchEventArgs e) + private void OnPointerMove(Microsoft.AspNetCore.Components.Web.PointerEventArgs e) { - foreach (var touch in e.ChangedTouches) + if (e.PointerType == "mouse") { - _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY), - GetModifiers(e), touch.Identifier); + _topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e)); } } - private void OnTouchMove(TouchEventArgs e) + private void OnPointerUp(Microsoft.AspNetCore.Components.Web.PointerEventArgs e) { - foreach (var touch in e.ChangedTouches) + if (e.PointerType == "touch") { - _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY), - GetModifiers(e), touch.Identifier); + _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(e.ClientX, e.ClientY), + GetModifiers(e), e.PointerId); } - } - - private void OnMouseMove(MouseEventArgs e) - { - _topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e)); - } + else if (e.PointerType == "mouse") + { + RawPointerEventType type = default; - private void OnMouseUp(MouseEventArgs e) - { - RawPointerEventType type = default; + switch (e.Button) + { + case 0: + type = RawPointerEventType.LeftButtonUp; + break; - switch (e.Button) - { - case 0: - type = RawPointerEventType.LeftButtonUp; - break; + case 1: + type = RawPointerEventType.MiddleButtonUp; + break; - case 1: - type = RawPointerEventType.MiddleButtonUp; - break; + case 2: + type = RawPointerEventType.RightButtonUp; + break; + } - case 2: - type = RawPointerEventType.RightButtonUp; - break; + _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e)); } - - _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e)); } - private void OnMouseDown(MouseEventArgs e) + private void OnPointerDown(Microsoft.AspNetCore.Components.Web.PointerEventArgs e) { - RawPointerEventType type = default; - - switch (e.Button) + if (e.PointerType == "touch") + { + _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(e.ClientX, e.ClientY), + GetModifiers(e), e.PointerId); + } + else if (e.PointerType == "mouse") { - case 0: - type = RawPointerEventType.LeftButtonDown; - break; + RawPointerEventType type = default; + + switch (e.Button) + { + case 0: + type = RawPointerEventType.LeftButtonDown; + break; - case 1: - type = RawPointerEventType.MiddleButtonDown; - break; + case 1: + type = RawPointerEventType.MiddleButtonDown; + break; - case 2: - type = RawPointerEventType.RightButtonDown; - break; - } + case 2: + type = RawPointerEventType.RightButtonDown; + break; + } - _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e)); + _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e)); + } } private void OnWheel(WheelEventArgs e) @@ -189,7 +190,7 @@ namespace Avalonia.Web.Blazor return modifiers; } - private static RawInputModifiers GetModifiers(MouseEventArgs e) + private static RawInputModifiers GetModifiers(Microsoft.AspNetCore.Components.Web.PointerEventArgs e) { var modifiers = RawInputModifiers.None; From 53f6b62b4394a29311778d26a025af8e6931aab9 Mon Sep 17 00:00:00 2001 From: Oxc3 <61174136+Oxc3@users.noreply.github.com> Date: Tue, 31 May 2022 13:13:16 -0700 Subject: [PATCH 755/820] Update AvaloniaView.razor.cs modified pointer up, down, move to handle touch device specifically and all other devices as mouse event --- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index 766aeb6d19..1f411d0cee 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -56,7 +56,7 @@ namespace Avalonia.Web.Blazor { return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); } - + private void OnTouchCancel(TouchEventArgs e) { foreach (var touch in e.ChangedTouches) @@ -77,7 +77,7 @@ namespace Avalonia.Web.Blazor private void OnPointerMove(Microsoft.AspNetCore.Components.Web.PointerEventArgs e) { - if (e.PointerType == "mouse") + if (e.PointerType != "touch") { _topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e)); } @@ -90,7 +90,7 @@ namespace Avalonia.Web.Blazor _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(e.ClientX, e.ClientY), GetModifiers(e), e.PointerId); } - else if (e.PointerType == "mouse") + else { RawPointerEventType type = default; @@ -120,7 +120,7 @@ namespace Avalonia.Web.Blazor _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(e.ClientX, e.ClientY), GetModifiers(e), e.PointerId); } - else if (e.PointerType == "mouse") + else { RawPointerEventType type = default; From 6dbb828b60042677b5fc2a92d810f2fabbf0d965 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 31 May 2022 21:58:00 -0400 Subject: [PATCH 756/820] Reset popup parent on flyout hidden --- src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index d024f86b32..dfbd3f9a36 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -175,7 +175,8 @@ namespace Avalonia.Controls.Primitives IsOpen = false; Popup.IsOpen = false; - + ((ISetLogicalParent)Popup).SetParent(null); + // Ensure this isn't active _transientDisposable?.Dispose(); _transientDisposable = null; From 5ab8b06dce55f3c1294540e959182faad839bccf Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 31 May 2022 21:58:19 -0400 Subject: [PATCH 757/820] Reset popup parent on context menu target detached --- src/Avalonia.Controls/ContextMenu.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 2b122d4174..7b35e35278 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -450,6 +450,11 @@ namespace Avalonia.Controls if (sender is Control control && control.ContextMenu is ContextMenu contextMenu) { + if (contextMenu._popup?.Parent == control) + { + ((ISetLogicalParent)contextMenu._popup).SetParent(null); + } + contextMenu.Close(); } } From bc6d5ec87d125936ed4892dae020bf4de82c668e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 31 May 2022 22:17:33 -0400 Subject: [PATCH 758/820] Add tests --- .../ContextMenuTests.cs | 21 ++++++++++++++++ .../FlyoutTests.cs | 25 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index ba01f3db40..b63cbd286e 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -446,6 +446,27 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Should_Reset_Popup_Parent_On_Target_Detached() + { + using (Application()) + { + var userControl = new UserControl(); + var window = PreparedWindow(userControl); + window.Show(); + + var menu = new ContextMenu(); + userControl.ContextMenu = menu; + menu.Open(); + + var popup = Assert.IsType(menu.Parent); + Assert.NotNull(popup.Parent); + + window.Content = null; + Assert.Null(popup.Parent); + } + } + [Fact] public void Context_Menu_In_Resources_Can_Be_Shared() { diff --git a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs index c2dd8cf01a..8b77074960 100644 --- a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs +++ b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs @@ -432,6 +432,26 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Should_Reset_Popup_Parent_On_Target_Detached() + { + using (CreateServicesWithFocus()) + { + var userControl = new UserControl(); + var window = PreparedWindow(userControl); + window.Show(); + + var flyout = new TestFlyout(); + flyout.ShowAt(userControl); + + var popup = Assert.IsType(flyout.Popup); + Assert.NotNull(popup.Parent); + + window.Content = null; + Assert.Null(popup.Parent); + } + } + [Fact] public void ContextFlyout_Can_Be_Set_In_Styles() { @@ -549,5 +569,10 @@ namespace Avalonia.Controls.UnitTests new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), KeyModifiers.None); } + + public class TestFlyout : Flyout + { + public new Popup Popup => base.Popup; + } } } From 05be2056e8160ce64a427f8a9154c51f3df3d36a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 1 Jun 2022 10:10:43 +0100 Subject: [PATCH 759/820] dispatch bring to front parent when removing a child window. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 8330f4ed86..7776b2912d 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -100,6 +100,11 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { if(_parent != nullptr) { _parent->_children.remove(this); + auto parent = _parent; + + dispatch_async(dispatch_get_main_queue(), ^{ + parent->BringToFront(); + }); } auto cparent = dynamic_cast(parent); From 32a61c0ea400542dd8b78be64d64652412c16a25 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 1 Jun 2022 10:15:54 +0100 Subject: [PATCH 760/820] dialogs should not be minimizable. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 7776b2912d..8520e3e470 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -590,7 +590,7 @@ NSWindowStyleMask WindowImpl::GetStyle() { break; } - if ([Window parentWindow] == nullptr) { + if (!IsDialog()) { s |= NSWindowStyleMaskMiniaturizable; } From 0ab383ed8995d51e2a71b74a25211751e1b1e088 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 1 Jun 2022 10:32:02 +0100 Subject: [PATCH 761/820] fix build warnings. --- .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 2 -- native/Avalonia.Native/src/OSX/AvnWindow.mm | 34 +++++++++---------- .../Avalonia.Native/src/OSX/INSWindowHolder.h | 2 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 +- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm index 2365189010..b49005de8a 100644 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -3,8 +3,6 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // -#pragma once - #define IS_NSPANEL #include "AvnWindow.mm" diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 52ee48317c..0b9ea093ac 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -68,7 +68,7 @@ } } -- (void)performClose:(id)sender +- (void)performClose:(id _Nullable )sender { if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) { @@ -147,7 +147,7 @@ } } --(void) applyMenu:(AvnMenu *)menu +-(void) applyMenu:(AvnMenu *_Nullable)menu { if(menu == nullptr) { @@ -157,7 +157,7 @@ _menu = menu; } --(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +-(CLASS_NAME*_Nonnull) initWithParent: (WindowBaseImpl*_Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ // create nswindow with specific contentRect, otherwise we wont be able to resize the window @@ -183,7 +183,7 @@ return self; } -- (BOOL)windowShouldClose:(NSWindow *)sender +- (BOOL)windowShouldClose:(NSWindow *_Nonnull)sender { auto window = dynamic_cast(_parent.getRaw()); @@ -195,14 +195,14 @@ return true; } -- (void)windowDidChangeBackingProperties:(NSNotification *)notification +- (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification { [self backingScaleFactor]; } -- (void)windowWillClose:(NSNotification *)notification +- (void)windowWillClose:(NSNotification *_Nonnull)notification { _closed = true; if(_parent) @@ -274,12 +274,12 @@ [super becomeKeyWindow]; } -- (void)windowDidBecomeKey:(NSNotification *)notification +- (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification { _parent->BringToFront(); } -- (void)windowDidMiniaturize:(NSNotification *)notification +- (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -289,7 +289,7 @@ } } -- (void)windowDidDeminiaturize:(NSNotification *)notification +- (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -299,7 +299,7 @@ } } -- (void)windowDidResize:(NSNotification *)notification +- (void)windowDidResize:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -309,7 +309,7 @@ } } -- (void)windowWillExitFullScreen:(NSNotification *)notification +- (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -319,7 +319,7 @@ } } -- (void)windowDidExitFullScreen:(NSNotification *)notification +- (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -342,7 +342,7 @@ } } -- (void)windowWillEnterFullScreen:(NSNotification *)notification +- (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -352,7 +352,7 @@ } } -- (void)windowDidEnterFullScreen:(NSNotification *)notification +- (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -363,7 +363,7 @@ } } -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame +- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame { return true; } @@ -380,7 +380,7 @@ [super resignKeyWindow]; } -- (void)windowDidMove:(NSNotification *)notification +- (void)windowDidMove:(NSNotification *_Nonnull)notification { AvnPoint position; @@ -412,7 +412,7 @@ return pt; } -- (void)sendEvent:(NSEvent *)event +- (void)sendEvent:(NSEvent *_Nonnull)event { [super sendEvent:event]; diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index ae64a53e7d..3c5010966b 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -11,7 +11,7 @@ struct INSWindowHolder { virtual NSWindow* _Nonnull GetNSWindow () = 0; - virtual NSView* _Nonnull GetNSView () = 0; + virtual AvnView* _Nonnull GetNSView () = 0; }; #endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 4220811fc7..040ba39b6d 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -38,7 +38,7 @@ BEGIN_INTERFACE_MAP() virtual NSWindow *GetNSWindow() override; - virtual NSView *GetNSView() override; + virtual AvnView *GetNSView() override; virtual HRESULT Show(bool activate, bool isDialog) override; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index e88c7f208c..c420736b46 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -70,7 +70,7 @@ NSWindow *WindowBaseImpl::GetNSWindow() { return Window; } -NSView *WindowBaseImpl::GetNSView() { +AvnView *WindowBaseImpl::GetNSView() { return View; } From 2f1ada6892537f871705b829b565379d2411af88 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 1 Jun 2022 13:47:56 +0100 Subject: [PATCH 762/820] Merge pull request #8239 from AvaloniaUI/fixes/osx-shadow-invalidation OSX Shadow invalidation when window sized --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 8520e3e470..d1d5c2a014 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -140,6 +140,8 @@ void WindowImpl::BringToFront() [Window orderFront:nullptr]; } + [Window invalidateShadow]; + for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) { (*iterator)->BringToFront(); From cea6bc27a06f25b4f2d3a73d3411e904b1767e74 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 1 Jun 2022 13:49:00 +0100 Subject: [PATCH 763/820] Merge pull request #8238 from AvaloniaUI/fixes/disable-parent-chrome-buttons-when-modal-is-shown OSX: Disable parent chrome buttons when modal is shown --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 11 ++++++--- native/Avalonia.Native/src/OSX/WindowImpl.mm | 24 +++++-------------- samples/ControlCatalog/Pages/DialogsPage.xaml | 2 +- .../ControlCatalog/Pages/DialogsPage.xaml.cs | 13 ++++++++++ 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 0b9ea093ac..9fc7ec4fec 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -176,9 +176,10 @@ _isExtended = false; -#ifdef IS_NSPANEL - [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary]; -#endif + if(self.isDialog) + { + [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary]; + } return self; } @@ -260,6 +261,10 @@ -(void) setEnabled:(bool)enable { _isEnabled = enable; + + [[self standardWindowButton:NSWindowCloseButton] setEnabled:enable]; + [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:enable]; + [[self standardWindowButton:NSWindowZoomButton] setEnabled:enable]; } -(void)becomeKeyWindow diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index d1d5c2a014..555e9dff9b 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -30,24 +30,12 @@ void WindowImpl::HideOrShowTrafficLights() { return; } - for (id subview in Window.contentView.superview.subviews) { - if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { - NSView *titlebarView = [subview subviews][0]; - for (id button in titlebarView.subviews) { - if ([button isKindOfClass:[NSButton class]]) { - if (_isClientAreaExtended) { - auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - [button setHidden:!wantsChrome]; - } else { - [button setHidden:(_decorations != SystemDecorationsFull)]; - } - - [button setWantsLayer:true]; - } - } - } - } + bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + bool hasTrafficLights = _isClientAreaExtended ? !wantsChrome : _decorations != SystemDecorationsFull; + + [[Window standardWindowButton:NSWindowCloseButton] setHidden:hasTrafficLights]; + [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:hasTrafficLights]; + [[Window standardWindowButton:NSWindowZoomButton] setHidden:hasTrafficLights]; } void WindowImpl::OnInitialiseNSWindow(){ diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml index 6ac9dcfe22..8a835867b3 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -20,7 +20,7 @@ Text="Window dialogs" /> - + diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index bed9f4c62a..1853c83365 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -151,6 +151,7 @@ namespace ControlCatalog.Pages private Window CreateSampleWindow() { Button button; + Button dialogButton; var window = new Window { @@ -167,6 +168,12 @@ namespace ControlCatalog.Pages HorizontalAlignment = HorizontalAlignment.Center, Content = "Click to close", IsDefault = true + }), + (dialogButton = new Button + { + HorizontalAlignment = HorizontalAlignment.Center, + Content = "Dialog", + IsDefault = false }) } }, @@ -174,6 +181,12 @@ namespace ControlCatalog.Pages }; button.Click += (_, __) => window.Close(); + dialogButton.Click += (_, __) => + { + var dialog = CreateSampleWindow(); + dialog.Height = 200; + dialog.ShowDialog(window); + }; return window; } From 9cc69eec227ed01bb14c15c9e665b2fd02ba19eb Mon Sep 17 00:00:00 2001 From: Oxc3 <61174136+Oxc3@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:27:38 -0700 Subject: [PATCH 764/820] added ClipboardImpl with hooks to navigator.clipboard.readtext and naviagot.clipboard.writeText so Avalonia web can TextBox copy-paste works --- .../Avalonia.Web.Blazor/AvaloniaView.razor | 5 ++- src/Web/Avalonia.Web.Blazor/ClipboardImpl.cs | 34 +++++++++++++++++++ src/Web/Avalonia.Web.Blazor/WinStubs.cs | 15 -------- .../Avalonia.Web.Blazor/WindowingPlatform.cs | 2 +- 4 files changed, 39 insertions(+), 17 deletions(-) create mode 100644 src/Web/Avalonia.Web.Blazor/ClipboardImpl.cs diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 3fe4c299cf..dd7eb0ec54 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -12,7 +12,10 @@
- +
+ + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - - - - - - - + + + + + + + - + - - - - - - - + + + + + + + - + - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + + + + + + + + + + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs index bdd5cbbe2b..682fc622b8 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs @@ -628,11 +628,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'> - + From 5520706a954eef81fe5073d5a57c9634df0b6a18 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 2 Jun 2022 17:14:50 +0200 Subject: [PATCH 769/820] Fix formatting. --- src/Avalonia.Themes.Fluent/Controls/Button.xaml | 3 ++- src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index d8d7ab3696..3fb0d43342 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -82,7 +82,8 @@ - + + - + + From 84a7d31f180f3fe5d88f08ad6e2a1b7fc04dff84 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 2 Jun 2022 16:30:50 +0100 Subject: [PATCH 770/820] Merge pull request #8248 from AvaloniaUI/fixes/prevent-parent-resizing-when-modal-opened [OSX] dont allow disabled windows to resize. --- native/Avalonia.Native/src/OSX/WindowImpl.h | 1 + native/Avalonia.Native/src/OSX/WindowImpl.mm | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 76d5cbf6ea..627e29c03d 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -13,6 +13,7 @@ class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { private: + bool _isEnabled; bool _canResize; bool _fullScreenActive; SystemDecorations _decorations; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 555e9dff9b..3c2224876a 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -10,6 +10,7 @@ #include "WindowProtocol.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { + _isEnabled = true; _children = std::list(); _isClientAreaExtended = false; _extendClientHints = AvnDefaultChrome; @@ -76,7 +77,9 @@ HRESULT WindowImpl::SetEnabled(bool enable) { START_COM_CALL; @autoreleasepool { + _isEnabled = enable; [GetWindowProtocol() setEnabled:enable]; + UpdateStyle(); return S_OK; } } @@ -574,7 +577,7 @@ NSWindowStyleMask WindowImpl::GetStyle() { case SystemDecorationsFull: s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; - if (_canResize) { + if (_canResize && _isEnabled) { s = s | NSWindowStyleMaskResizable; } break; From 643fe6675e2ee7735cc106b4b63f83faf2680676 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 2 Jun 2022 18:39:16 +0100 Subject: [PATCH 771/820] prevent segfault when closing app with child windows open. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 3c2224876a..e6e704e149 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -91,11 +91,8 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { if(_parent != nullptr) { _parent->_children.remove(this); - auto parent = _parent; - dispatch_async(dispatch_get_main_queue(), ^{ - parent->BringToFront(); - }); + _parent->BringToFront(); } auto cparent = dynamic_cast(parent); @@ -122,20 +119,23 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { void WindowImpl::BringToFront() { - if(IsDialog()) + if(Window != nullptr) { - Activate(); - } - else - { - [Window orderFront:nullptr]; - } - - [Window invalidateShadow]; - - for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) - { - (*iterator)->BringToFront(); + if(IsDialog()) + { + Activate(); + } + else + { + [Window orderFront:nullptr]; + } + + [Window invalidateShadow]; + + for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) + { + (*iterator)->BringToFront(); + } } } From 9e03c05713e1817243f580943c4ed178ec7b42e3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 2 Jun 2022 23:03:31 +0100 Subject: [PATCH 772/820] osx: fix crash when modal dialog is opened over fullscreen parent. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index e6e704e149..cae1c09513 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -564,6 +564,11 @@ bool WindowImpl::IsDialog() { NSWindowStyleMask WindowImpl::GetStyle() { unsigned long s = NSWindowStyleMaskBorderless; + + if(_actualWindowState == FullScreen) + { + s |= NSWindowStyleMaskFullScreen; + } switch (_decorations) { case SystemDecorationsNone: From a6d1e74b4fa1d14e7c9e2047865ec253558b0eca Mon Sep 17 00:00:00 2001 From: Oxc3 Date: Thu, 2 Jun 2022 16:22:44 -0700 Subject: [PATCH 773/820] RazorViewTopLevel will now test keys first on Code, then Key as a backup This is an android specific hack. the chrome browser event sends an empty string on Code property so this will test both Code and Key properties to match to an Avalonia key. If the user is on a sane system then Code will be used to Key match, else it will failover to try and match with Key. --- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs | 4 ++-- src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index 1f411d0cee..7531dbf681 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -233,12 +233,12 @@ namespace Avalonia.Web.Blazor private void OnKeyDown(KeyboardEventArgs e) { - _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, e.Code, GetModifiers(e)); + _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, e.Code, e.Key, GetModifiers(e)); } private void OnKeyUp(KeyboardEventArgs e) { - _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyUp, e.Code, GetModifiers(e)); + _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyUp, e.Code, e.Key, GetModifiers(e)); } private void OnInput(ChangeEventArgs e) diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs index 50070e6e2c..a8a1a970dc 100644 --- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs +++ b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs @@ -91,9 +91,16 @@ namespace Avalonia.Web.Blazor } } - public void RawKeyboardEvent(RawKeyEventType type, string key, RawInputModifiers modifiers) + public void RawKeyboardEvent(RawKeyEventType type, string code, string key, RawInputModifiers modifiers) { - if (Keycodes.KeyCodes.TryGetValue(key, out var avkey)) + if (Keycodes.KeyCodes.TryGetValue(code, out var avkey)) + { + if (_inputRoot is { }) + { + Input?.Invoke(new RawKeyEventArgs(KeyboardDevice, Timestamp, _inputRoot, type, avkey, modifiers)); + } + } + else if (Keycodes.KeyCodes.TryGetValue(key, out avkey)) { if (_inputRoot is { }) { From bd3bb6e47e8d7a8c61430a6aeeffa1489649ad60 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 2 Jun 2022 22:15:12 -0400 Subject: [PATCH 774/820] Check if BindingValue actually has a value --- src/Avalonia.Base/Reactive/TypedBindingAdapter.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs index f60ef247d5..f75917a00e 100644 --- a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs +++ b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs @@ -30,13 +30,15 @@ namespace Avalonia.Reactive } catch (InvalidCastException e) { + var unwrappedValue = value.HasValue ? value.Value : null; + Logger.TryGet(LogEventLevel.Error, LogArea.Binding)?.Log( _target, "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", _property.Name, _property.PropertyType, - value.Value, - value.Value?.GetType()); + unwrappedValue, + unwrappedValue?.GetType()); PublishNext(BindingValue.BindingError(e)); } } From 2a2779f2487bcf306796436d016ec243e3aa1ce3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 2 Jun 2022 22:57:01 -0400 Subject: [PATCH 775/820] Stop listening for IsCancel on button detached --- src/Avalonia.Controls/Button.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index db4ca6bc43..6dba33516b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -232,6 +232,13 @@ namespace Avalonia.Controls StopListeningForDefault(inputElement); } } + if (IsCancel) + { + if (e.Root is IInputElement inputElement) + { + StopListeningForCancel(inputElement); + } + } } /// From 210727175aead0e15d77eba795e579b3f4177e83 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 2 Jun 2022 23:11:44 -0400 Subject: [PATCH 776/820] Add simple IsDefault/IsCancel tests --- .../ButtonTests.cs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index f4acf5ea37..2e3623cc3c 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -309,6 +309,80 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, raised); } + + [Fact] + public void Button_IsDefault_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var raised = 0; + var target = new Button(); + var window = new Window { Content = target }; + window.Show(); + + target.Click += (s, e) => ++raised; + + target.IsDefault = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(0, raised); + + target.IsDefault = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(1, raised); + + target.IsDefault = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(1, raised); + + target.IsDefault = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Enter)); + Assert.Equal(2, raised); + + window.Content = null; + // To check if handler was raised on the button, when it's detached, we need to pass it as a source manually. + window.RaiseEvent(CreateKeyDownEvent(Key.Enter, target)); + Assert.Equal(2, raised); + } + } + + [Fact] + public void Button_IsCancel_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var raised = 0; + var target = new Button(); + var window = new Window { Content = target }; + window.Show(); + + target.Click += (s, e) => ++raised; + + target.IsCancel = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(0, raised); + + target.IsCancel = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(1, raised); + + target.IsCancel = false; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(1, raised); + + target.IsCancel = true; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape)); + Assert.Equal(2, raised); + + window.Content = null; + window.RaiseEvent(CreateKeyDownEvent(Key.Escape, target)); + Assert.Equal(2, raised); + } + } + + private KeyEventArgs CreateKeyDownEvent(Key key, IInteractive source = null) + { + return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source }; + } private class TestButton : Button, IRenderRoot { From bd0d81d6afea1554a8f79d1a432d86de9838efe5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:08:32 +0100 Subject: [PATCH 777/820] fix logic for deciding if chrome buttons should be shown or not in extended mode. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index cae1c09513..ef2a91ef14 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -32,7 +32,7 @@ void WindowImpl::HideOrShowTrafficLights() { } bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - bool hasTrafficLights = _isClientAreaExtended ? !wantsChrome : _decorations != SystemDecorationsFull; + bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations != SystemDecorationsFull; [[Window standardWindowButton:NSWindowCloseButton] setHidden:hasTrafficLights]; [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:hasTrafficLights]; From e446a66d6ab48ba0ab5982a6a379efe7d401d85b Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 12:29:39 +0200 Subject: [PATCH 778/820] Fix calculation of inverted matrix --- src/Avalonia.Base/Matrix.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Matrix.cs b/src/Avalonia.Base/Matrix.cs index 5bbc657385..8d3227378f 100644 --- a/src/Avalonia.Base/Matrix.cs +++ b/src/Avalonia.Base/Matrix.cs @@ -450,13 +450,13 @@ namespace Avalonia inverted = new Matrix( (_m22 * _m33 - _m32 * _m23) * invdet, - (_m13 * _m31 - _m12 * _m33) * invdet, + (_m13 * _m32 - _m12 * _m33) * invdet, (_m12 * _m23 - _m13 * _m22) * invdet, (_m23 * _m31 - _m21 * _m33) * invdet, (_m11 * _m33 - _m13 * _m31) * invdet, (_m21 * _m13 - _m11 * _m23) * invdet, (_m21 * _m32 - _m31 * _m22) * invdet, - (_m21 * _m12 - _m11 * _m32) * invdet, + (_m31 * _m12 - _m11 * _m32) * invdet, (_m11 * _m22 - _m21 * _m12) * invdet ); From 6a848a389fb2ca600ed4173d5f121b14b97e24cb Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:34:20 +0100 Subject: [PATCH 779/820] dont re-initialise nswindow on show. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index c420736b46..bf221047f9 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -90,8 +90,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { - InitialiseNSWindow(); - if(hasPosition) { SetPosition(lastPositionSet); From 809f459233f018111cacb39e2564ff242b612b4a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:39:03 +0100 Subject: [PATCH 780/820] fix osx thick titlebar setting. --- samples/ControlCatalog/MainWindow.xaml.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 20591103b7..8ee8b2ebd4 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -29,8 +29,6 @@ namespace ControlCatalog DataContext = new MainWindowViewModel(_notificationArea); _recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu; - - ExtendClientAreaChromeHints = Avalonia.Platform.ExtendClientAreaChromeHints.OSXThickTitleBar; } public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit"; From 11595184d5e87520073dbba37b0afd8cce1d3a2e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:39:14 +0100 Subject: [PATCH 781/820] fix traffic light setting. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index ef2a91ef14..f49b676ce6 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -32,11 +32,11 @@ void WindowImpl::HideOrShowTrafficLights() { } bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations != SystemDecorationsFull; + bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull; - [[Window standardWindowButton:NSWindowCloseButton] setHidden:hasTrafficLights]; - [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:hasTrafficLights]; - [[Window standardWindowButton:NSWindowZoomButton] setHidden:hasTrafficLights]; + [[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights]; + [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights]; + [[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights]; } void WindowImpl::OnInitialiseNSWindow(){ From 6e916c21e5d5effba473013086b111123c38cf26 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 11:46:30 +0100 Subject: [PATCH 782/820] fix transition to fullscreen mode. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 9fc7ec4fec..f6dcd0cabf 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -33,6 +33,7 @@ bool _isEnabled; bool _canBecomeKeyWindow; bool _isExtended; + bool _isTransitioningToFullScreen; AvnMenu* _menu; } @@ -175,6 +176,7 @@ [self setBackgroundColor: [NSColor clearColor]]; _isExtended = false; + _isTransitioningToFullScreen = false; if(self.isDialog) { @@ -349,6 +351,7 @@ - (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification { + _isTransitioningToFullScreen = true; auto parent = dynamic_cast(_parent.operator->()); if(parent != nullptr) @@ -359,6 +362,7 @@ - (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification { + _isTransitioningToFullScreen = false; auto parent = dynamic_cast(_parent.operator->()); if(parent != nullptr) @@ -441,7 +445,10 @@ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } - _parent->BringToFront(); + if(!_isTransitioningToFullScreen) + { + _parent->BringToFront(); + } } break; From e2dbf68b02b3a67079d84ef7d2815ace38843b20 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 12:20:25 +0100 Subject: [PATCH 783/820] fix call to virtual method from ctor. --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 6 +----- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 -- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 7 ------- native/Avalonia.Native/src/OSX/WindowImpl.h | 3 +-- native/Avalonia.Native/src/OSX/WindowImpl.mm | 3 +++ 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index 3c5afd9424..9820a9f052 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -26,17 +26,13 @@ private: PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; } protected: virtual NSWindowStyleMask GetStyle() override { return NSWindowStyleMaskBorderless; } - - virtual void OnInitialiseNSWindow () override - { - [Window setLevel:NSPopUpMenuWindowLevel]; - } public: virtual bool ShouldTakeFocusOnShow() override diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 040ba39b6d..787827c9b4 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -106,8 +106,6 @@ protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); - - virtual void OnInitialiseNSWindow (); private: void CreateNSWindow (bool isDialog); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bf221047f9..ea9ef3c9de 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -567,11 +567,6 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } -void WindowBaseImpl::OnInitialiseNSWindow() -{ - -} - void WindowBaseImpl::InitialiseNSWindow() { if(Window != nullptr) { [Window setContentView:StandardContainer]; @@ -594,8 +589,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [GetWindowProtocol() showWindowMenuWithAppMenu]; } } - - OnInitialiseNSWindow(); } } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 627e29c03d..b4b1d4e70b 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -93,8 +93,6 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog() override; - virtual void OnInitialiseNSWindow() override; - virtual void BringToFront () override; bool CanBecomeKeyWindow (); @@ -103,6 +101,7 @@ protected: virtual NSWindowStyleMask GetStyle() override; private: + void OnInitialiseNSWindow(); NSString *_lastTitle; }; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index f49b676ce6..382cd0db95 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -24,6 +24,8 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastTitle = @""; _parent = nullptr; WindowEvents = events; + + OnInitialiseNSWindow(); } void WindowImpl::HideOrShowTrafficLights() { @@ -41,6 +43,7 @@ void WindowImpl::HideOrShowTrafficLights() { void WindowImpl::OnInitialiseNSWindow(){ [GetWindowProtocol() setCanBecomeKeyWindow:true]; + [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; From 164757c915ec36e422231479c24676b0640ee0bd Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 3 Jun 2022 13:41:09 +0200 Subject: [PATCH 784/820] Make NumericUpDown nullable --- .../NumericUpDown/NumericUpDown.cs | 111 +++++++++++------- .../NumericUpDownValueChangedEventArgs.cs | 6 +- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index 4d86a0f17c..705e68e3ea 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -107,8 +107,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty ValueProperty = - AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, + public static readonly DirectProperty ValueProperty = + AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); /// @@ -131,7 +131,7 @@ namespace Avalonia.Controls private IDisposable? _textBoxTextChangedSubscription; - private decimal _value; + private decimal? _value; private string? _text; private bool _internalValueSet; private bool _clipValueToMinMax; @@ -277,7 +277,7 @@ namespace Avalonia.Controls /// /// Gets or sets the value. /// - public decimal Value + public decimal? Value { get { return _value; } set @@ -351,7 +351,7 @@ namespace Avalonia.Controls /// protected override void OnLostFocus(RoutedEventArgs e) { - CommitInput(); + CommitInput(true); base.OnLostFocus(e); } @@ -489,9 +489,9 @@ namespace Avalonia.Controls { SetValidSpinDirection(); } - if (ClipValueToMinMax) + if (ClipValueToMinMax && Value.HasValue) { - Value = MathUtilities.Clamp(Value, Minimum, Maximum); + Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum); } } @@ -506,9 +506,9 @@ namespace Avalonia.Controls { SetValidSpinDirection(); } - if (ClipValueToMinMax) + if (ClipValueToMinMax && Value.HasValue) { - Value = MathUtilities.Clamp(Value, Minimum, Maximum); + Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum); } } @@ -530,7 +530,7 @@ namespace Avalonia.Controls /// /// The old value. /// The new value. - protected virtual void OnValueChanged(decimal oldValue, decimal newValue) + protected virtual void OnValueChanged(decimal? oldValue, decimal? newValue) { if (!_internalValueSet && IsInitialized) { @@ -573,7 +573,7 @@ namespace Avalonia.Controls /// Called when the property has to be coerced. ///
/// The value. - protected virtual decimal OnCoerceValue(decimal baseValue) + protected virtual decimal? OnCoerceValue(decimal? baseValue) { return baseValue; } @@ -607,7 +607,7 @@ namespace Avalonia.Controls ///
/// The old value. /// The new value. - protected virtual void RaiseValueChangedEvent(decimal oldValue, decimal newValue) + protected virtual void RaiseValueChangedEvent(decimal? oldValue, decimal? newValue) { var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); RaiseEvent(e); @@ -616,9 +616,9 @@ namespace Avalonia.Controls /// /// Converts the formatted text to a value. /// - private decimal ConvertTextToValue(string text) + private decimal? ConvertTextToValue(string? text) { - decimal result = 0; + decimal? result = null; if (string.IsNullOrEmpty(text)) { @@ -635,9 +635,9 @@ namespace Avalonia.Controls result = ConvertTextToValueCore(currentValueText, text); - if (ClipValueToMinMax) + if (ClipValueToMinMax && result.HasValue) { - return MathUtilities.Clamp(result, Minimum, Maximum); + return MathUtilities.Clamp(result.Value, Minimum, Maximum); } ValidateMinMax(result); @@ -649,7 +649,7 @@ namespace Avalonia.Controls /// Converts the value to formatted text. /// /// - private string ConvertValueToText() + private string? ConvertValueToText() { //Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind. if (FormatString.Contains("{0")) @@ -657,7 +657,7 @@ namespace Avalonia.Controls return string.Format(NumberFormat, FormatString, Value); } - return Value.ToString(FormatString, NumberFormat); + return Value?.ToString(FormatString, NumberFormat); } /// @@ -665,7 +665,16 @@ namespace Avalonia.Controls /// private void OnIncrement() { - var result = Value + Increment; + decimal result; + if (Value.HasValue) + { + result = Value.Value + Increment; + } + else + { + result = Minimum; + } + Value = MathUtilities.Clamp(result, Minimum, Maximum); } @@ -674,7 +683,17 @@ namespace Avalonia.Controls /// private void OnDecrement() { - var result = Value - Increment; + decimal result; + + if (Value.HasValue) + { + result = Value.Value - Increment; + } + else + { + result = Maximum; + } + Value = MathUtilities.Clamp(result, Minimum, Maximum); } @@ -688,6 +707,11 @@ namespace Avalonia.Controls // Zero increment always prevents spin. if (Increment != 0 && !IsReadOnly) { + if (!Value.HasValue) + { + validDirections = ValidSpinDirections.Increase | ValidSpinDirections.Decrease; + } + if (Value < Maximum) { validDirections = validDirections | ValidSpinDirections.Increase; @@ -825,13 +849,13 @@ namespace Avalonia.Controls { if (e.Sender is NumericUpDown upDown) { - var oldValue = (decimal)e.OldValue!; - var newValue = (decimal)e.NewValue!; + var oldValue = (decimal?)e.OldValue; + var newValue = (decimal?)e.NewValue; upDown.OnValueChanged(oldValue, newValue); } } - private void SetValueInternal(decimal value) + private void SetValueInternal(decimal? value) { _internalValueSet = true; try @@ -946,9 +970,9 @@ namespace Avalonia.Controls remove { RemoveHandler(ValueChangedEvent, value); } } - private bool CommitInput() + private bool CommitInput(bool forceTextUpdate = false) { - return SyncTextAndValueProperties(true, Text); + return SyncTextAndValueProperties(true, Text, forceTextUpdate); } /// @@ -978,28 +1002,24 @@ namespace Avalonia.Controls { if (updateValueFromText) { - if (!string.IsNullOrEmpty(text)) + try { - try + var newValue = ConvertTextToValue(text); + if (!Equals(newValue, Value)) { - var newValue = ConvertTextToValue(text); - if (!Equals(newValue, Value)) - { - SetValueInternal(newValue); - } - } - catch - { - parsedTextIsValid = false; + SetValueInternal(newValue); } } + catch + { + parsedTextIsValid = false; + } } // Do not touch the ongoing text input from user. if (!_isTextChangedFromUI) { - var keepEmpty = !forceTextUpdate && string.IsNullOrEmpty(Text); - if (!keepEmpty) + if (forceTextUpdate) { var newText = ConvertValueToText(); if (!Equals(Text, newText)) @@ -1036,10 +1056,15 @@ namespace Avalonia.Controls return parsedTextIsValid; } - private decimal ConvertTextToValueCore(string currentValueText, string text) + private decimal? ConvertTextToValueCore(string? currentValueText, string? text) { decimal result; + if (string.IsNullOrEmpty(text)) + { + return null; + } + if (IsPercent(FormatString)) { result = ParsePercent(text, NumberFormat); @@ -1052,7 +1077,7 @@ namespace Avalonia.Controls var shouldThrow = true; // Check if CurrentValueText is also failing => it also contains special characters. ex : 90° - if (!decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _)) + if (!string.IsNullOrEmpty(currentValueText) && !decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _)) { // extract non-digit characters var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); @@ -1082,8 +1107,12 @@ namespace Avalonia.Controls return result; } - private void ValidateMinMax(decimal value) + private void ValidateMinMax(decimal? value) { + if (!value.HasValue) + { + return; + } if (value < Minimum) { throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum)); diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs index 9b467d682c..af835541ae 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs @@ -4,13 +4,13 @@ namespace Avalonia.Controls { public class NumericUpDownValueChangedEventArgs : RoutedEventArgs { - public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal oldValue, decimal newValue) : base(routedEvent) + public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal? oldValue, decimal? newValue) : base(routedEvent) { OldValue = oldValue; NewValue = newValue; } - public decimal OldValue { get; } - public decimal NewValue { get; } + public decimal? OldValue { get; } + public decimal? NewValue { get; } } } From 43cf66fc49fa6f6e431e26fd747d2bb6edd02eae Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 3 Jun 2022 13:41:28 +0200 Subject: [PATCH 785/820] Update demo to reflect actual selected value --- samples/ControlCatalog/Pages/NumericUpDownPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 9ddc6b6228..e32632dac2 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -65,7 +65,7 @@ Margin="2" HorizontalAlignment="Center"/> Value: - From e47b742d3eb5e2fe8b0ddf0a1b65b8c999aa35bd Mon Sep 17 00:00:00 2001 From: Patrick Tellier Date: Fri, 3 Jun 2022 14:35:46 +0200 Subject: [PATCH 786/820] Use own Pen (if set) in GeometryDrawing.GetBounds --- src/Avalonia.Base/Media/GeometryDrawing.cs | 3 +- .../Media/GeometryDrawingTests.cs | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs diff --git a/src/Avalonia.Base/Media/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs index e46e3a7d5f..08e62df2cc 100644 --- a/src/Avalonia.Base/Media/GeometryDrawing.cs +++ b/src/Avalonia.Base/Media/GeometryDrawing.cs @@ -68,7 +68,8 @@ namespace Avalonia.Media public override Rect GetBounds() { - return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty; + IPen pen = Pen ?? s_boundsPen; + return Geometry?.GetRenderBounds(pen) ?? Rect.Empty; } } } diff --git a/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs b/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs new file mode 100644 index 0000000000..06e46c1a06 --- /dev/null +++ b/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs @@ -0,0 +1,52 @@ +using Avalonia.Media; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests +#else + +using Avalonia.Direct2D1.RenderTests; + +namespace Avalonia.Direct2D1.RenderTests.Media +#endif +{ + public class GeometryDrawingTests : TestBase + { + public GeometryDrawingTests() + : base(@"Media\GeometryDrawing") + { + } + + private GeometryDrawing CreateGeometryDrawing() + { + GeometryDrawing geometryDrawing = new GeometryDrawing(); + EllipseGeometry ellipse = new EllipseGeometry(); + ellipse.RadiusX = 100; + ellipse.RadiusY = 100; + geometryDrawing.Geometry = ellipse; + return geometryDrawing; + } + + [Fact] + public void DrawingGeometry_WithPen() + { + GeometryDrawing geometryDrawing = CreateGeometryDrawing(); + geometryDrawing.Pen = new Pen(new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)), 10); + + Assert.Equal(210, geometryDrawing.GetBounds().Height); + Assert.Equal(210, geometryDrawing.GetBounds().Width); + + } + + [Fact] + public void DrawingGeometry_WithoutPen() + { + GeometryDrawing geometryDrawing = CreateGeometryDrawing(); + + Assert.Equal(200, geometryDrawing.GetBounds().Height); + Assert.Equal(200, geometryDrawing.GetBounds().Width); + } + + + } +} From 0470369af056c13d5af388e827cf39eaf394cec1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 15:20:22 +0100 Subject: [PATCH 787/820] osx: ensure shadow is invalidated on show. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index ea9ef3c9de..46f2ac0092 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -99,6 +99,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { } UpdateStyle(); + + [Window invalidateShadow]; if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; From e5a3820fbb05fe57de89353a2d58bf504b92e648 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 15:40:01 +0100 Subject: [PATCH 788/820] whenever we become key... dispatch invalidateShadow --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index f6dcd0cabf..ebd9f39d30 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -284,6 +284,14 @@ - (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification { _parent->BringToFront(); + + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + [self invalidateShadow]; + } + @finally{ + } + }); } - (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification From eaf8fec5cfaa6e195afeaedded1ac0234f73b4ec Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 18:10:50 +0200 Subject: [PATCH 789/820] Test for matrix inversion --- tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index 98b1ff0e28..f25047c5c5 100644 --- a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -30,6 +30,16 @@ namespace Avalonia.Base.UnitTests.Media Assert.True(matrix.HasInverse); } + [Fact] + public void Invert_Should_Work() + { + var matrix = new Matrix(1, 2, 3, 0, 1, 4,5,6,0); + var inverted = matrix.Invert(); + var expected = new Matrix(-24, 18, 5, 20, -15, -4, -5, 4, 1); + + Assert.Equal(expected, inverted); + } + [Fact] public void Can_Decompose_Translation() { From 987a69aafb2097c0b6c9e2bfadeb9b7f415fa727 Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 18:12:39 +0200 Subject: [PATCH 790/820] spaces --- tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index f25047c5c5..b30996ca1e 100644 --- a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -33,7 +33,7 @@ namespace Avalonia.Base.UnitTests.Media [Fact] public void Invert_Should_Work() { - var matrix = new Matrix(1, 2, 3, 0, 1, 4,5,6,0); + var matrix = new Matrix(1, 2, 3, 0, 1, 4, 5, 6, 0); var inverted = matrix.Invert(); var expected = new Matrix(-24, 18, 5, 20, -15, -4, -5, 4, 1); From 5f5fc6e01fd7717d7ba3b463b72e3e80c70f09e8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 17:22:33 +0100 Subject: [PATCH 791/820] osx: restore missing api for cef - electron compatibility --- native/Avalonia.Native/src/OSX/app.mm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 14f1f6888c..a15d0c9601 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -82,6 +82,17 @@ ComPtr _events; _isHandlingSendEvent = oldHandling; } } + +// This is needed for certain embedded controls DO NOT REMOVE.. +- (BOOL) isHandlingSendEvent +{ + return _isHandlingSendEvent; +} + +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent +{ + _isHandlingSendEvent = handlingSendEvent; +} @end extern void InitializeAvnApp(IAvnApplicationEvents* events) From 0907e9519a11f2b830592e96b07daebfba179dad Mon Sep 17 00:00:00 2001 From: kaminova Date: Fri, 3 Jun 2022 19:44:41 +0200 Subject: [PATCH 792/820] Fix test --- tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index b30996ca1e..4aa13fab3a 100644 --- a/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -35,9 +35,9 @@ namespace Avalonia.Base.UnitTests.Media { var matrix = new Matrix(1, 2, 3, 0, 1, 4, 5, 6, 0); var inverted = matrix.Invert(); - var expected = new Matrix(-24, 18, 5, 20, -15, -4, -5, 4, 1); - Assert.Equal(expected, inverted); + Assert.Equal(matrix * inverted, Matrix.Identity); + Assert.Equal(inverted * matrix, Matrix.Identity); } [Fact] From 8c6759990d38119eed7a40b9ac9c4027bb2864d1 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 3 Jun 2022 20:45:35 +0300 Subject: [PATCH 793/820] [X11] Added support for the basic version of _NET_WM_SYNC_REQUEST protocol --- src/Avalonia.X11/X11Atoms.cs | 1 + src/Avalonia.X11/X11Info.cs | 10 ++++++++ src/Avalonia.X11/X11Window.cs | 44 +++++++++++++++++++++++++++-------- src/Avalonia.X11/XLib.cs | 17 ++++++++++++++ 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index b59883ef94..cfda68f9e8 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -155,6 +155,7 @@ namespace Avalonia.X11 public readonly IntPtr _NET_FRAME_EXTENTS; public readonly IntPtr _NET_WM_PING; public readonly IntPtr _NET_WM_SYNC_REQUEST; + public readonly IntPtr _NET_WM_SYNC_REQUEST_COUNTER; public readonly IntPtr _NET_SYSTEM_TRAY_S; public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION; public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE; diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs index 9920907601..13dc460f45 100644 --- a/src/Avalonia.X11/X11Info.cs +++ b/src/Avalonia.X11/X11Info.cs @@ -33,6 +33,7 @@ namespace Avalonia.X11 public IntPtr LastActivityTimestamp { get; set; } public XVisualInfo? TransparentVisualInfo { get; set; } public bool HasXim { get; set; } + public bool HasXSync { get; set; } public IntPtr DefaultFontSet { get; set; } public unsafe X11Info(IntPtr display, IntPtr deferredDisplay, bool useXim) @@ -101,6 +102,15 @@ namespace Avalonia.X11 { //Ignore, XI is not supported } + + try + { + HasXSync = XSyncInitialize(display, out _, out _) != Status.Success; + } + catch + { + //Ignore, XSync is not supported + } } } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 066156a652..6d0ca9422b 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -45,6 +45,8 @@ namespace Avalonia.X11 private IntPtr _handle; private IntPtr _xic; private IntPtr _renderHandle; + private IntPtr _xSyncCounter; + private XSyncValue _xSyncValue; private bool _mapped; private bool _wasMappedAtLeastOnce = false; private double? _scalingOverride; @@ -190,6 +192,16 @@ namespace Avalonia.X11 NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle); NativeControlHost = new X11NativeControlHost(_platform, this); InitializeIme(); + + XChangeProperty(_x11.Display, _handle, _x11.Atoms.WM_PROTOCOLS, _x11.Atoms.XA_ATOM, 32, + PropertyMode.Replace, new[] { _x11.Atoms.WM_DELETE_WINDOW, _x11.Atoms._NET_WM_SYNC_REQUEST }, 2); + + if (_x11.HasXSync) + { + _xSyncCounter = XSyncCreateCounter(_x11.Display, _xSyncValue); + XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_SYNC_REQUEST_COUNTER, + _x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref _xSyncCounter, 1); + } } class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo @@ -383,15 +395,7 @@ namespace Avalonia.X11 (ev.type == XEventName.VisibilityNotify && ev.VisibilityEvent.state < 2)) { - if (!_triggeredExpose) - { - _triggeredExpose = true; - Dispatcher.UIThread.Post(() => - { - _triggeredExpose = false; - DoPaint(); - }, DispatcherPriority.Render); - } + EnqueuePaint(); } else if (ev.type == XEventName.FocusIn) { @@ -503,6 +507,7 @@ namespace Avalonia.X11 if (_useRenderWindow) XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height); + EnqueuePaint(); } else if (ev.type == XEventName.DestroyNotify && ev.DestroyWindowEvent.window == _handle) @@ -518,7 +523,11 @@ namespace Avalonia.X11 if (Closing?.Invoke() != true) Dispose(); } - + else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST) + { + _xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32(); + _xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32(); + } } } else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease) @@ -730,9 +739,24 @@ namespace Avalonia.X11 ScheduleInput(mev, ref ev); } + void EnqueuePaint() + { + if (!_triggeredExpose) + { + _triggeredExpose = true; + Dispatcher.UIThread.Post(() => + { + _triggeredExpose = false; + DoPaint(); + }, DispatcherPriority.Render); + } + } + void DoPaint() { Paint?.Invoke(new Rect()); + if (_xSyncCounter != IntPtr.Zero) + XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue); } public void Invalidate(Rect rect) diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index e2b370821f..464ec4f1c8 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -542,6 +542,18 @@ namespace Avalonia.X11 public static extern int XRRQueryExtension (IntPtr dpy, out int event_base_return, out int error_base_return); + + [DllImport(libX11Ext)] + public static extern Status XSyncInitialize(IntPtr dpy, out int event_base_return, out int error_base_return); + + [DllImport(libX11Ext)] + public static extern IntPtr XSyncCreateCounter(IntPtr dpy, XSyncValue initialValue); + + [DllImport(libX11Ext)] + public static extern int XSyncDestroyCounter(IntPtr dpy, IntPtr counter); + + [DllImport(libX11Ext)] + public static extern int XSyncSetCounter(IntPtr dpy, IntPtr counter, XSyncValue value); [DllImport(libX11Randr)] public static extern int XRRQueryVersion(IntPtr dpy, @@ -627,6 +639,11 @@ namespace Avalonia.X11 public int bw; public int d; } + + public struct XSyncValue { + public int Hi; + public uint Lo; + } public static bool XGetGeometry(IntPtr display, IntPtr window, out XGeometry geo) { From 485d9b04a9ffd779055903a2b3b16273a75b86d9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 3 Jun 2022 19:51:47 +0100 Subject: [PATCH 794/820] Merge pull request #8269 from AvaloniaUI/fixes/osx-setcontent-size-shadow-invalidation Fixes/osx setcontent size shadow invalidation --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 - .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 41 ++++++------------- native/Avalonia.Native/src/OSX/WindowImpl.mm | 5 --- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 787827c9b4..2baf3b09b5 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -110,7 +110,6 @@ protected: private: void CreateNSWindow (bool isDialog); void CleanNSWindow (); - void InitialiseNSWindow (); NSCursor *cursor; ComPtr _glContext; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 46f2ac0092..7f2bb128da 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -39,7 +39,16 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, lastMenu = nullptr; CreateNSWindow(usePanel); - InitialiseNSWindow(); + + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + + [Window setOpaque:false]; + [Window setHasShadow:true]; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -90,6 +99,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + [Window setContentSize:lastSize]; + if(hasPosition) { SetPosition(lastPositionSet); @@ -292,8 +303,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if (!_shown) { BaseEvents->Resized(AvnSize{x, y}, reason); } - - if(Window != nullptr) { + else if(Window != nullptr) { [Window setContentSize:lastSize]; [Window invalidateShadow]; } @@ -569,31 +579,6 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } -void WindowBaseImpl::InitialiseNSWindow() { - if(Window != nullptr) { - [Window setContentView:StandardContainer]; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setContentSize:lastSize]; - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - - [Window setOpaque:false]; - - [Window setHasShadow:true]; - [Window invalidateShadow]; - - if (lastMenu != nullptr) { - [GetWindowProtocol() applyMenu:lastMenu]; - - if ([Window isKeyWindow]) { - [GetWindowProtocol() showWindowMenuWithAppMenu]; - } - } - } -} - id WindowBaseImpl::GetWindowProtocol() { if(Window == nullptr) { diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 382cd0db95..6db586f3ca 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -55,11 +55,6 @@ void WindowImpl::OnInitialiseNSWindow(){ [GetWindowProtocol() setIsExtended:true]; SetExtendClientArea(true); } - - if(_parent != nullptr) - { - SetParent(_parent); - } } HRESULT WindowImpl::Show(bool activate, bool isDialog) { From 9a82e65f5323bd3a3ff04b11a7e7dba0d6399f50 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 3 Jun 2022 23:53:01 +0300 Subject: [PATCH 795/820] [X11] Improve _NET_WM_SYNC_REQUEST handling --- src/Avalonia.X11/X11Window.cs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 6d0ca9422b..e7c4a37ea2 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -47,6 +47,7 @@ namespace Avalonia.X11 private IntPtr _renderHandle; private IntPtr _xSyncCounter; private XSyncValue _xSyncValue; + private XSyncState _xSyncState = 0; private bool _mapped; private bool _wasMappedAtLeastOnce = false; private double? _scalingOverride; @@ -54,6 +55,14 @@ namespace Avalonia.X11 private TransparencyHelper _transparencyHelper; private RawEventGrouper _rawEventGrouper; private bool _useRenderWindow = false; + + enum XSyncState + { + None, + WaitConfigure, + WaitPaint + } + public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent) { _platform = platform; @@ -507,7 +516,11 @@ namespace Avalonia.X11 if (_useRenderWindow) XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height); - EnqueuePaint(); + if (_xSyncState == XSyncState.WaitConfigure) + { + _xSyncState = XSyncState.WaitPaint; + EnqueuePaint(); + } } else if (ev.type == XEventName.DestroyNotify && ev.DestroyWindowEvent.window == _handle) @@ -527,6 +540,7 @@ namespace Avalonia.X11 { _xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32(); _xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32(); + _xSyncState = XSyncState.WaitConfigure; } } } @@ -755,8 +769,11 @@ namespace Avalonia.X11 void DoPaint() { Paint?.Invoke(new Rect()); - if (_xSyncCounter != IntPtr.Zero) + if (_xSyncCounter != IntPtr.Zero && _xSyncState == XSyncState.WaitPaint) + { + _xSyncState = XSyncState.None; XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue); + } } public void Invalidate(Rect rect) @@ -802,6 +819,12 @@ namespace Avalonia.X11 XDestroyIC(_xic); _xic = IntPtr.Zero; } + + if (_xSyncCounter != IntPtr.Zero) + { + XSyncDestroyCounter(_x11.Display, _xSyncCounter); + _xSyncCounter = IntPtr.Zero; + } if (_handle != IntPtr.Zero) { From a34e03d7f5ee0a67b00ddf8f2dac4d42a497df58 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 3 Jun 2022 19:12:12 -0400 Subject: [PATCH 796/820] Fix android service initialization order --- src/Android/Avalonia.Android/AndroidPlatform.cs | 2 -- src/Android/Avalonia.Android/AvaloniaActivity.cs | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 61aa6ce946..c7dd896cb8 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -61,8 +61,6 @@ namespace Avalonia.Android .Bind().ToConstant(new RenderLoop()) .Bind().ToSingleton(); - SkiaPlatform.Initialize(); - if (options.UseGpu) { EglPlatformOpenGlInterface.TryInitialize(); diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs index f5d620a97a..f3692d33d7 100644 --- a/src/Android/Avalonia.Android/AvaloniaActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaActivity.cs @@ -31,21 +31,22 @@ namespace Avalonia.Android CustomizeAppBuilder(builder); - View = new AvaloniaView(this); - SetContentView(View); var lifetime = new SingleViewLifetime(); - lifetime.View = View; builder.AfterSetup(x => { _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel; + View = new AvaloniaView(this); if (_viewModel.Content != null) { View.Content = _viewModel.Content; } + SetContentView(View); + lifetime.View = View; + View.Prepare(); }); From b277db43bd88c860ff998796570fd5540a8b4342 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 3 Jun 2022 21:02:09 -0400 Subject: [PATCH 797/820] Update release build parameters --- .../ControlCatalog.Android/ControlCatalog.Android.csproj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 04c67e84e8..ec88852feb 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -20,10 +20,13 @@ - True + False + False True True - True + no-write-symbols,nodebug + Hybrid + True From 5c7a399630edde5b6fd50c70fbb3e547572fc38e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 4 Jun 2022 10:42:54 +0200 Subject: [PATCH 798/820] fix: Suppress CA1416 on Avalonia.Win32 --- src/Windows/Avalonia.Win32/Avalonia.Win32.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index b9a1880435..55d02b5d31 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -18,4 +18,8 @@ + + CA1416 + $(NoWarn),CA1416 + From dba9f9832e1a709d2491fb30c9d554f574920374 Mon Sep 17 00:00:00 2001 From: Oxc3 Date: Sat, 4 Jun 2022 17:03:21 -0700 Subject: [PATCH 799/820] Update AvaloniaView.razor change to the razor code so the events are invoked by the AspNetCore.EventCallbackFactory rather then an action pointer attached to the event --- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index dd7eb0ec54..31425fe438 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -1,18 +1,18 @@ 
+ @ontouchcancel="OnTouchCancel" + @ontouchmove="OnTouchMove" + @onwheel="OnWheel" + @onkeydown="OnKeyDown" + @onkeyup="OnKeyUp" + @onpointerdown="OnPointerDown" + @onpointerup="OnPointerUp" + @onpointermove="OnPointerMove">
- From 040fe4a60307d93d8776def47c3fc9215b46bf29 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 5 Jun 2022 16:18:21 +0200 Subject: [PATCH 800/820] Fix missing PooledList usage in Calendar. --- src/Avalonia.Controls/Calendar/CalendarItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index 32fdaceacb..bcac6324ba 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -218,7 +218,7 @@ namespace Avalonia.Controls.Primitives if (YearView != null) { var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear; - var children = new List(childCount); + using var children = new PooledList(childCount); EventHandler monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown; EventHandler monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp; From 38b88825261005a7a53507be30aa2a45fa63c276 Mon Sep 17 00:00:00 2001 From: Oxc3 Date: Sun, 5 Jun 2022 07:21:20 -0700 Subject: [PATCH 801/820] mixing touch and pointer breaks gestures --- .../Avalonia.Web.Blazor/AvaloniaView.razor | 8 ++-- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 38 +++++-------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 31425fe438..68662bd931 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -1,12 +1,11 @@ 
+ @onpointermove="OnPointerMove" + @onpointercancel="OnPointerCancel"> @@ -19,6 +18,9 @@