From c22b71aa50cc0b74848be1db1723376a736f7b3a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 6 Aug 2020 17:41:32 +0800 Subject: [PATCH 01/46] update build.ps1 to put downloaded dotnet in PATH --- build.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.ps1 b/build.ps1 index 57e2f80075..3672e82d3b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -62,6 +62,8 @@ else { } else { ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } + + $env:PATH="$DotNetDirectory;$env:PATH" } Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" From 754da3470ed5bb5d8e5084be88d5877503596672 Mon Sep 17 00:00:00 2001 From: Murdo R Ergeaux Date: Sat, 3 Oct 2020 08:35:19 +0100 Subject: [PATCH 02/46] Hardcode DatePicker month/day to prevent unrepresentable dates --- src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index daee7968eb..a0e8c03195 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -486,9 +486,9 @@ namespace Avalonia.Controls.Primitives switch (panelType) { case DateTimePickerPanelType.Year: - return new DateTime(value, FormatDate.Month, FormatDate.Day).ToString(ItemFormat); + return new DateTime(value, 1, 1).ToString(ItemFormat); case DateTimePickerPanelType.Month: - return new DateTime(FormatDate.Year, value, FormatDate.Day).ToString(ItemFormat); + return new DateTime(FormatDate.Year, value, 1).ToString(ItemFormat); case DateTimePickerPanelType.Day: return new DateTime(FormatDate.Year, FormatDate.Month, value).ToString(ItemFormat); case DateTimePickerPanelType.Hour: From 8bb8f3c79fed45ec03398b56bb4202a06b94745a Mon Sep 17 00:00:00 2001 From: "oliver.holliday@lontra.co.uk" Date: Wed, 7 Oct 2020 20:03:08 +0100 Subject: [PATCH 03/46] Add Skia support for Conic Gradients. Add a place holder implementation for Direct2D using SolidColorBrushImpl. --- .../Media/ConicGradientBrush.cs | 55 ++++++ .../Media/IConicGradientBrush.cs | 19 ++ .../Immutable/ImmutableConicGradientBrush.cs | 47 +++++ src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 17 ++ .../Media/DrawingContextImpl.cs | 6 + .../Media/SolidColorBrushImpl.cs | 17 ++ .../Media/ConicGradientBrushTests.cs | 178 ++++++++++++++++++ .../ConicGradientBrush_RedBlue.expected.png | Bin 0 -> 601 bytes ...cGradientBrush_RedBlue_Center.expected.png | Bin 0 -> 601 bytes ...h_RedBlue_Center_and_Rotation.expected.png | Bin 0 -> 601 bytes ...radientBrush_RedBlue_Rotation.expected.png | Bin 0 -> 601 bytes ...radientBrush_RedBlue_SoftEdge.expected.png | Bin 0 -> 601 bytes .../ConicGradientBrush_Umbrella.expected.png | Bin 0 -> 601 bytes .../ConicGradientBrush_RedBlue.expected.png | Bin 0 -> 7249 bytes ...cGradientBrush_RedBlue_Center.expected.png | Bin 0 -> 6797 bytes ...h_RedBlue_Center_and_Rotation.expected.png | Bin 0 -> 6792 bytes ...radientBrush_RedBlue_Rotation.expected.png | Bin 0 -> 7232 bytes ...radientBrush_RedBlue_SoftEdge.expected.png | Bin 0 -> 8805 bytes .../ConicGradientBrush_Umbrella.expected.png | Bin 0 -> 11408 bytes 19 files changed, 339 insertions(+) create mode 100644 src/Avalonia.Visuals/Media/ConicGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/IConicGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs create mode 100644 tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png diff --git a/src/Avalonia.Visuals/Media/ConicGradientBrush.cs b/src/Avalonia.Visuals/Media/ConicGradientBrush.cs new file mode 100644 index 0000000000..7c1266fa17 --- /dev/null +++ b/src/Avalonia.Visuals/Media/ConicGradientBrush.cs @@ -0,0 +1,55 @@ +using Avalonia.Media.Immutable; + +namespace Avalonia.Media +{ + /// + /// Paints an area with a swept circular gradient. + /// + public sealed class ConicGradientBrush : GradientBrush, IConicGradientBrush + { + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterProperty = + AvaloniaProperty.Register( + nameof(Center), + RelativePoint.Center); + + /// + /// Defines the property. + /// + public static readonly StyledProperty AngleProperty = + AvaloniaProperty.Register( + nameof(Angle), + 0); + + static ConicGradientBrush() + { + AffectsRender(CenterProperty, AngleProperty); + } + + /// + /// Gets or sets the center point of the gradient. + /// + public RelativePoint Center + { + get { return GetValue(CenterProperty); } + set { SetValue(CenterProperty, value); } + } + + /// + /// Gets or sets the angle of the start and end of the sweep, measured from above the center point. + /// + public double Angle + { + get { return GetValue(AngleProperty); } + set { SetValue(AngleProperty, value); } + } + + /// + public override IBrush ToImmutable() + { + return new ImmutableConicGradientBrush(this); + } + } +} diff --git a/src/Avalonia.Visuals/Media/IConicGradientBrush.cs b/src/Avalonia.Visuals/Media/IConicGradientBrush.cs new file mode 100644 index 0000000000..5368dd1851 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IConicGradientBrush.cs @@ -0,0 +1,19 @@ +namespace Avalonia.Media +{ + /// + /// Paints an area with a conic gradient. + /// + public interface IConicGradientBrush : IGradientBrush + { + /// + /// Gets the center point for the gradient. + /// + RelativePoint Center { get; } + + /// + /// Gets the starting angle for the gradient in degrees, measured from + /// the point above the center point. + /// + double Angle { get; } + } +} diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs new file mode 100644 index 0000000000..d3c80dfcad --- /dev/null +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace Avalonia.Media.Immutable +{ + /// + /// A brush that draws with a sweep gradient. + /// + public class ImmutableConicGradientBrush : ImmutableGradientBrush, IConicGradientBrush + { + /// + /// Initializes a new instance of the class. + /// + /// The gradient stops. + /// The opacity 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, + GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, + RelativePoint? center = null, + double angle = 0) + : base(gradientStops, opacity, spreadMethod) + { + Center = center ?? RelativePoint.Center; + Angle = angle; + } + + /// + /// Initializes a new instance of the class. + /// + /// The brush from which this brush's properties should be copied. + public ImmutableConicGradientBrush(ConicGradientBrush source) + : base(source) + { + Center = source.Center; + Angle = source.Angle; + } + + /// + public RelativePoint Center { get; } + + /// + public double Angle { get; } + } +} diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 05a8daaec8..b8c81b3409 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -624,6 +624,23 @@ namespace Avalonia.Skia } } + break; + } + case IConicGradientBrush conicGradient: + { + var center = conicGradient.Center.ToPixels(targetSize).ToSKPoint(); + + // Skia's default is that angle 0 is from the right hand side of the center point + // but we are matching CSS where the vertical point above the center is 0. + var angle = (float)(conicGradient.Angle - 90); + var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y); + + using (var shader = + SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation)) + { + paintWrapper.Paint.Shader = shader; + } + break; } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 258a51db5a..5d20bff1e9 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -424,6 +424,7 @@ namespace Avalonia.Direct2D1.Media var solidColorBrush = brush as ISolidColorBrush; var linearGradientBrush = brush as ILinearGradientBrush; var radialGradientBrush = brush as IRadialGradientBrush; + var conicGradientBrush = brush as IConicGradientBrush; var imageBrush = brush as IImageBrush; var visualBrush = brush as IVisualBrush; @@ -439,6 +440,11 @@ namespace Avalonia.Direct2D1.Media { return new RadialGradientBrushImpl(radialGradientBrush, _deviceContext, destinationSize); } + else if (conicGradientBrush != null) + { + // there is no Direct2D implementation of Conic Gradients so use Radial as a stand-in + return new SolidColorBrushImpl(conicGradientBrush, _deviceContext); + } else if (imageBrush?.Source != null) { return new ImageBrushImpl( diff --git a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs index f93b4a2e08..fea1ca9157 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs @@ -16,5 +16,22 @@ namespace Avalonia.Direct2D1.Media } ); } + + /// + /// Direct2D has no ConicGradient implementation so fall back to a solid colour brush based on + /// the first gradient stop. + /// + public SolidColorBrushImpl(IConicGradientBrush brush, SharpDX.Direct2D1.DeviceContext target) + { + PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush( + target, + brush?.GradientStops[0].Color.ToDirect2D() ?? new SharpDX.Mathematics.Interop.RawColor4(), + new SharpDX.Direct2D1.BrushProperties + { + Opacity = brush != null ? (float)brush.Opacity : 1.0f, + Transform = target.Transform + } + ); + } } } diff --git a/tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs b/tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs new file mode 100644 index 0000000000..db69a0a028 --- /dev/null +++ b/tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs @@ -0,0 +1,178 @@ +using Avalonia.Controls; +using Avalonia.Media; +using System.Threading.Tasks; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests +#else +namespace Avalonia.Direct2D1.RenderTests.Media +#endif +{ + public class ConicGradientBrushTests : TestBase + { + public ConicGradientBrushTests() : base(@"Media\ConicGradientBrush") + { + } + + [Fact] + public async Task ConicGradientBrush_RedBlue() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_Rotation() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + Angle = 90 + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_Center() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + Center = new RelativePoint(0.25, 0.25, RelativeUnit.Relative) + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_Center_and_Rotation() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + Center = new RelativePoint(0.25, 0.25, RelativeUnit.Relative), + Angle = 90 + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_SoftEdge() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 0.5 }, + new GradientStop { Color = Colors.Red, Offset = 1 }, + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_Umbrella() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Yellow, Offset = 0.1667 }, + new GradientStop { Color = Colors.Lime, Offset = 0.3333 }, + new GradientStop { Color = Colors.Aqua, Offset = 0.5000 }, + new GradientStop { Color = Colors.Blue, Offset = 0.6667 }, + new GradientStop { Color = Colors.Magenta, Offset = 0.8333 }, + new GradientStop { Color = Colors.Red, Offset = 1 }, + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + } +} diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..563bbfcc611681e14d54f391c617f12e3ed7e7e1 GIT binary patch literal 7249 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a8719e2e704e51f328ef90998babfdfc4cc5845b GIT binary patch literal 6792 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1c79db413bb90fee0331107c23578aae6089c841 GIT binary patch literal 7232 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf8eddd81ad5984cd8124fecc2bc2ccbea94fad GIT binary patch literal 8805 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 ROvxLe(uF>R)ZDiZ`+qS-*r5Ob literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..98dfda782dcd436656c74249ec05013b7b6c3608 GIT binary patch literal 11408 zcmZ{~c{o(>8$UjSvG2=R8XAl+Mj=GDv5zJDjGGe|`S=UDxlA@Aq8SInOzNoclTFxu5&KU)TM5C0JP)bFhlA0ssIG6O@6? z`Pl!zlbP}ST7`X;b3QQK*E6wWK0h8Y$fM?(%~^pDr!|}^?Ud!-upIbT zs;6apa#Vi0&a8>cZraGV30JUowg_rknUWk*{P zpaY-V-JNl7LsI2SgdC>Yv~T;R1x-yEyU&g}^kw(q%Jd5VU>Bk#@5Q#Fl^_KDi}y@5 zaRm@qeH#xYB%CA#NKIy2h0aof=Di+->(6a7BLY?fg@p*07{XgP(VjaXYV0MAvoYIC z%zHSO(Eh2in?&~|)UC7;ZeN4FZ;fHyG?ZmM^m`p^+%)yCNj+wCSCu~Tvj4r7LbDv} zrxkLW1RM(zfv@k(Eq(0t_MxL3D$aJaq3~Zv-SaJwdyn$wfuWyL7id11-YgE=p=o!$ zYgD5mlzA!MK6T@x={K>ummka|{Kx;&xv-Uye%fouOOqCw zrzxd{(NKSR4YGZQPL;p#5^Qd~HI%KYZeGgDuG3}Y<$@8vhv6HPB{v^d++?}}xGFxT zpkRf6uHN3TZCi;_cXS3_qBQN^WMLg@z5TOmx%UJu<-#4pUB%`CXYvdOKf_W7 zvBBN>coR5um|T$teKS1R^dOTxW-w4v55btkln5Gj&WZ9uIld&N^$`UyC4mpm7BdzDl|>hHQ21`^)qXUo*5AG2>Gblj4EFi&5=Iyml~zu=Pz=FV9$ z6t@q~6%jJig$H1_n5cFXsQ@f%T^ynRT?GM;)K$=-*O!*by5SK&L2raH)Re+jNSF!4 z=<;I4d+PM$SvUUN%pKH<{6O$E6<-`jKu zs@a{O>N`AKV~0(>(+Ux;aS#|l@}5sCM@US!<|?@)Tp!5gkirkMNtNP5y-a60R80jH zDjlnpHbd^84fk~Hpm{3Au*T%xJaQoSZ0Q!^O_GVwgZXGT1|dg7+=P}|k-GiBP0cyc z=ecj9?U4g|uH`16yDQ+?)_t!s_%r6N{*dztPHkdbQ4F}v)BiW$as;z^o zqErHQsQI_mVUZ#catLn5?ellqq_W@gjj{s4*i0X|G1D@OZdw_bN^H$)!s-yvI}AhT|3J=`W+sRM4ee_)dXfnl*g+Z)}AMaG*nCo)o0L z-uR-XJUULdB91|?l4YWm`94<%D^!G2IC0}e^kgLt=I=c96^$y;@O!T~ohg>~XGqK# zqoNIgV#w$LojD$_fP%t=lMP_OGNl6rC3JeZdFEJbE=mw=+wziZo}ff4B1b7tR{WW` zH-3rGu_g7>xIc*0@46Aajz88IG{0r$d<<~EQ_Gy@g*8j>y6<-quUm<9SQ(7k&)^^z zi=h>0l7|!e%~y#E0tx&*Q3lkB{i@^jLq#w>3RmWG}rbtRanfxtfN4C^^-NlOC5!uI2F6YPX`QfoNd{7+ zHfyuO$~?W1fENw;Xhj!loSXh>rqqLb9*%}HnpF~F(9|ewvjQXGEB5GX4&l2C;@%_1 zs256`sT{pZdH4<#K;mLI4S;%%osmk{uLRkqm~(!WHD-KBx}W;HamJPJ(k|^;2TJWI-GjAIRE{k% zBNA{Q=lBMFJS;F=!KATxV;Wx49WvsnUW>T;@QlfAL5fw$N-O!oh2(tV48=Ti0v!WX z_-aCJ7?%C$lNKx45+#f_aWs?{f;q(2?EYJ9Fwiy9FWBT|VC)M(B2Xpoz`FHCBT<;Wl-ASYwgq@aT^_eI_p3ZWgEgKjm% ziL8nc9RG{$J6si2-85QYgWEQ+@QH>RW0Mz`%&gTwAfZYK^|(pmdWOl3udB>XWtuC>`s$x4r|VQ|XX6hrSXK`GeBiqHxlmsnHl1-5FV zSS&S4t$6qiZ5D|cGNo4MR=F4Zzn?c zabwBU*OInR^K&s=9C{N?uO1Y|;D-rKr4j;@dvjSe@C!Nac7^oGU<-!;Ebb8n64NQ6 zJ9g~-W~f~Rmh)6Nu8VwXbv zKA;sxNB3)bfKJNhX=ubaeS(;HLaQhdKiPfZEu{JC zyv?(uM7xrg@St|V3N%o>V@NX^SPT313o1@!Vz=d4c<4RU*EK~whVd}Uic_OEeM=_k zD|TY-Ig(1m91SIKN7#NBJh29%%;&Kaz!42HGE#@DlWn+IdS#AZvp|VIN>0a(m%;Sw zbY?@AN<=SIPSU#y_a4Xio4lOs!rSKRi5@TUY)Q;f=I0(|8nIF9+5bMD0x|$6F$vy>;E1;=~UMg9qP0~LHk~*S_9L8lAK2==F!tidWREc)xmwZ=HfjPbAlO?U4v2(W3 z1YO;iXGTVgN<}1dunUMDx{Xg6xxkg!n@!ou=a?MmZFY$C_8=7iU99kQdmWjs*yqW5 zvg>CWN@#wAcn1@;eZs8u zboN6gF z5DbWro`a00VavH9m$8dTAC*+jH`eY~f4uANLi_;xmd9&9$D_df)*~)J9#W^B!L&xV z9HXIB!vV2>9a0&KMOqkYs*L?jw=(N?jla#yUvhbzovBSJII)<}V6!eU9Hu3u8227p zySr*#W|yV3j4l#`zYxiXnDIvg`DT4sU2lJpnec(4pxE&2ODeN3rpY-Tq02Y8pyr0b zrQuC=1rlBG%~Y1+DL_xuirr|Gb!-6>Z}@SX9Eu_KT1sa=&^!4@tb?QBWgm&|SHXi0 zWsR4hHoD4?;m6nY5lsV9rnQXF`K|i#N-;2Zn|OMTpo4wdtNE;i*jv1b5dx{7jz&ie z>r7o2W@k4%h~JIhWfk4dDut%sa${N=+xT>d^-BY;+Eqv0tE43AKCQJOgH~@<2);lW z!h}>|9o>lSabk#tkJ?tcLjVey1K3u4`Bf{Ba_9AtV}Koc#o8RKHpM4T&8rO998i-K zm=xl%Ff^q@|GbkKRr7B68JdSNq4<*0c6K}F%A{dfekuGztL-SjCBO?s2O0}dKuHL} z_*0|sU2-h%gCkbOgqqF!k0;-}iRwQ^p^D@@p)Pq4&%@>rwdS0FouJtov*!XH-pC_) zhab<81#cR84`qtReL9J?dbIERyC)*dvmU1exwVh>@#XJ*SS7Zyf~(UMWQwZ2P)Dut zmBC^6V#`tdeh$Qjv^OyfF`yF*v97q1&W{uYK!FK;+JH_|2Pww3<*qD~Em>uGZ%)`u zjyd20YW2y}PI28jK&|*J`7Gv!Q-AR&@~Yf0p+}0zh19xOtrU)Q^6ItwDqOB3IRxfC zI!{9Zc>T&$aWogC5>DQ}z{0{-UqE>bl=!AY4yJd1ED9{X>;5CjB3sZy)MDOy;56)3 zn46M`r#KodpOgA-b!CC->7`}_Ta)Z4>i|v)&Psq%!&3+G%VREJ`d)aINmO8FqkF{H zyNYz6rJC(~)~Uwk6l_Wan%rx*0=(Z#$`Z)OOQ_s%!LP~bX?%Bt8AX1G8}Yv(^*~D#5ZA~32$Nw1i8QM`wd;K zAlf}EwAVVCJ(;6j9_k-tEf|vOurqCs$7D+ z>n|M*?Z~}>oK;}3&?tI94?&;2a>6S_z=c@SlwpPXaJ8MX5z|qyEcvor%@LsRb_1{9 zz$3#5MNHi7r`hH%(;jhP{bX4ajkW6+L%Tqn8~Fp=kJljVG2e-k(T*l_t&osNjG)o&cvn;1996d^>>-Q53sv6mtnvCl zvecF&x>$O(0@+3MhStOz-bbmtfE{nh zUuE>v+Mm}M-|_J;|AfZH8GMsCx6--V288mmvqZmea^Xylh&pE8zLwE;GXN4$CDxYk zdXj0}o};)Rbn1ZM^nTE!1zjD7t7Zs9^ z+q#-8c@TiGX2(ps8;L3@d`9$)@1$0oZ&)8!($i1~P5<&=?c6i`ig0kqdV~$9c}7wA zr2}w>?=7e`QCZFDD_!@)i3vdJ@4kAL_J`!<{un!BbhS+HI5*tZD7-DgiZGAsoLnh1nG8F-tUOVt4$glzf*JYwp2*n1>< znb_KT=j-3#u2)YC>AO7RLobKBmh3*yH`ud$O5&S>esMn*e9G zgQIVsTH|ex`EGUm>hwNcE)Qp|}6#}5U)bNoTTE^7?9PAVAFJpr8E!C`*$VtFsw9m!xDia>c9;HQAKho=)9#2F{Q+#v^g0a# z=m7UX#N2b^dnW>W?U_rzz!^2B(Y5ipkBqH4yyz7fe7`YjapTv+Fs!xNhB!N_Xuid4 zuxhDyG1HTb>Y9(0;p@|4=HZF;NTG?>Cg% zA)XCt$3~5vm{wMoik^S0(qUM}-6pl33T%)@)|zo#hy$_i%&Bee+p&FyL)}^04uZ9Is>vF6a;Mt3O@drB zsEl(0{NmSmM7pxi2KaTXvs`(F^aV$^!f_c{V>NI;;#6DMb|LND!HDP^zeSR z)F7DL5l%em!QT3jzXECR0>C-^x76NR#xKx6kTZ@j_=ip~b4H_kkx%pg_qKd7wE0=X z=0UoK>qLeLQvRE#crrWbXlS=uk+jcbp3ffvTABvz`91#!6MhttBe*nqt zKlPHBvy99f$K}Vi^uOf)@wIpV7^O5^GgFq?-np_B6!~MBp0{{%`cv|0VQALxwjtG< z{I18>|MTi=-UJJwg&uqz63ZR@qA>PF&p!p<-U~`@u2u*i*}rN|JcE4VqaC`@bm#yl zAqihr+V^yRov;tc-4YYs$|ZW`3;lr$hWX~P-dNX-v;X1!^ThL8*tz3Ggui|dF3uq# zM-FlQdg7CMk%z5q3Nuc3{0H|5OiO<LF+yHJ{RUceDx|Oo7A}8CQW*p&5${UhGVy$Yp2U&{| zYVSZL56kA>qH|qNpPdn9cW41~sH$wT|nJDMqWONvC{|G*HJcO;b zyvcN#i=4Z1^U;%QAz>pX6W8v@!oV_4@4f=*OFwMVl1}T6&Ch%PG`Swf3uIGuQ`rg} zere{1!hc;)BQ)&awL}oIex3#`o*VLQt{8fZV0skozbE2fdqr1CiJWb0x|?ApNQFox$Mc8T)F&G=W>QKA??f7FMeCtF^h*Y)3P&j=>0ju zpPAt3$CWy+Dbq76@{7v>7N6&BFaK?9qv(j>zqEFfX%UTu1` zDu@7P_h&dd%fRxlx=H!JHOcKGvw4R@tgBUP2b{Kl@AMxE$_ah!9rr4ox_6-pduXRHYMQt z#Ez}fKf%WfS+{J*Z!^&My6Vwf*S|^q6EoPUJ!&E)`xd}O$5#SvpeLdKiY1zUaan%R zB_;awMzt@pPTZcs1mi%t)4g#xSY|n`wZ&O05%h7bj63nzQ$UhF{-Pi*yr_ib2)30;(#4JNnM3cW>YrCv-y9cmA!WR|Tp{3|i zT#$|^ZeJFmwt#aSNN0pmpL>9JzJPy6lm5L$7D-;e;W_O}X)p$|^X_Edw>k+)k>1{q zF)QnZ4!{S-rJdR+pGBS-ieoIl<0q#Xqjd}JbElY@XJ-24H_PHmA@$A| zKjXxDhIFJUec8hVTvrf{^}&K5gVKSKbQ%(`hG$V7_{Y> zZPH%(m>UGia#jMRic^1HqdWd{F+s;VWT&6{s5aNf%hSO>Wnrb+E7EnB{8v^(9alOc z5N*JPuU4n| zB3LS6(5n|GiaDzgNC}13>i-7hm~p$>y!>Fl4+_$r*>|T|A2oS75;{J4w=niZ6^i)P zT2)G4+t0({XeCewHKQ0?w1U#0c)l7k#+IGiQmc-r`Bg+|%c0_etD;osmCb{MbNtQm zUhKZ$%hS@lt$<*UauQjDot?UAyilIrHkME>L(LnJTzSxRRz}VPtbzsKwvlA`G zga^S{k#?6jUT{{u(nE;)D7qGYv2DQBem0L*y3zMZTAB$mO8HJ1qK_$0iHRoFU4mV+ zrl>5D4>&9pA3X28NBKtu#l+f+@E&B~yZz<8{aciFT+Me^p}7|BU`_{5;7H?Th~?;8 zPPGwEeZ8k9pH(1t|1nz~S%>Z<6$_kG6^+Z}K+f5|rOe+MoLg<&Lv%$2LL%DK$Xl`4 zIqR!ReIw*nr8$Jv10w8?qhDZvxC>cyLbD34G%UiBP%Ks{(?1tr$9EvpaNYF3#EI@i zybu&|RF@N>sDPa!sD>MR!V4beJw3i-A@TEDtdl&cov7pzs5Yuqk=o?T7wd=^cD#@_ zDS&4XGJ6(H5(lae1`OJ8l|V63yHeX?knIHyjs#&>MKzk$V<0c6o8au!uVSCN{=V<3 zOrIPQVTC}cmbx3unqY2dUMPY3Rv3el={t1B>YhpRz44OKEXL)dpG6M8$_e_}bU-<= zcvTD)q4sNnQUjC){qi6+eS9E;Xz?u$a^XyYro8dYNIO3@NghK%D4#}P&?)>vQy*06 zdcs$$9zhKPaCH3l=bmP{@MD>%GPDqJgn zs)pMakDYu@d5f#La7U%pk&fJaqXhC;AVz9?`?)9wwGj}iG-x(6X?31b>K5tanauH( zvgAf5f16kHmxaKPxl$_NYn3O-dregQyj3$pQaqkX2xNgy%_u{dhIMCO=(@lQCiOE! znAwsSDROnMVBsd%MmzOaer2becVksj1HT(An0+q1)jgyEj>pbED!ReWxrOn3qI;qu zg#UA0Fm)Moa~C=h)A^O;TFzwqA~Dfmsu&6l&+$oOt{Og11QeftJS3i28^WU<;hO*e zyT<>t0Fh$+w^>~~T}2VL{9Cnb1{xY->g!>8+?g^|h#uuu+)%bt65W7>w3 z1I28l(pnLN1shNuv56k#D6dq)_oiV(&Vpg>vAXvir^*n^iZ5}kv9}$NO-3(ut*xK- z$4PCpsmGU!d@J0ImE(FkUgHz|R}!fdfEds%Xi2}16$if!EHMo5TB9({jd)ag zE|rO}HC9l}UxlkZd0fsyZxo$x>5DOxxWI@q(mjX!&BYh-fZDDsnVv+w;$fx;Ft-!R zy;HaP*^6@VeGBqg+MO-8nwq|MR(EHsnFj27M4Fir9;}%K7fklpq&k6w8Og`|a?dHA zlhJh6)+vdBc6zk!XCS?3UGZ8l?&-roY@i=74b8i8RA;#^L1BgY+WXRd z6*XgL3FrYbnQxpmX5X>XqFeJI+65*%+;;vZ6+q)4J30U8z96Xa{s4V|sk6vCZxf&M zqfmEci`%0W#tZ{?VwSTe^ino_RU&I)p9aoj=7WZLU&4~tUu&#etR7m3Mv?UiqlDPU zJCzSFG}FaTYge4^+c5+L;BqHqwcdtDhTx0aIsPQ$8)0*16t-Xnrf|EyK7=# zDB(9s3PD!FeP6-Zv%fwOn8R6=TzioP6LR`Dwhp{+l}G(l!j(wM%g~{?yP~oS8qN&# zyXL@%RQW;Og3sL?rL+sU<804~?x^B4qP<;r%UJz|^00K8MosfrV)pk?5#DkM5h_hl z0kK9LTc1vw@ zM{tyfhL$Zxer^&=gSNc17Vy2E)`F-~FpI7=&~0$U{xzP=wM}hu(l^ZQ?KdSBO|;Q5 zzKs~;7~MRCrsiQSi>O_$qHm>{Wl^mfRn4D<^W>4r{FuyiG|yHD?YBrWjqqL3Au-VW z34;tEFsQStX$>KOBBaNBxR}q&&ahs{!|w>dUsUmrT_oWFW5?zp?&`FMckYb)Nc?GV z{QlQu%`4p8dhXBd+F0VP@R{Z~!V!{p?IN0@2~}D-@)Hxry+W|XJS5FLP}rRPXxcqi zDB<81&=5YT%@`M_mwe0{Z0N=X%pX(`p|HXnzBxyJhxvmr`RU-BNTr8(F8*1teRtzv8$?YItDVbGnAG>#);C)7nj0jFtv2-t^=(bMB| zMs$v}Z~j*W94I?yZH$29hY1f;d_wl>FBpTKdGLdMJK+$fQor(ekTHm2UDEswSk^c4 z{ynvJ=Y$w?%%!#Qd{+k5V0ie6EjdpAa`#dhT^gEabd_MPg4ep>y3ul11t}q<9gM8| zGrm%vtWwGE4V4($g+>|C^OeWttOi1Z7>*O&n*@Ph%ZvTp0JR-8yB%5z<7S;z*tmUjTaFIo)wTVGE*?4p+yM@B@r{?~rEQ~8CbVOO z6|xD4_Pk3`-8oX_xArE9i!R!5bHr}bzj7IS7_=lpP4qTC0l2uJ;+W`jQvs3`N=?KcQdDwXLpgan z(|}SfpNxkL=NsQ!kuD#!u|pO%J23~rHd&gJH52;u=e6ffS?>Y;?6-1qsn|BX^%uXV zMaIc5Vhia^l)+OYr~J{K2i6puY_`YEdJX!jI)x%&WJ{H}3w)g3_$nidZ3)V-Kw+qP zO`SZMflh%#a-JGfk{F~aB^Ezu-%WMxKPAi38-Y2m6eXH#+(R2GxlsMxwEwpXY{|oo zkI#!%yuGKJ+n-&a8xQ1zQb2#pL%OP-+wqvXFBQfC3IgQ!(l@?IvGbZ4(zuM=7nGl+%S{3=M$2RbqdA^{&n8jn2ZLw z-asuK)e`1fpM=t5FK}`RF&bHt(_>+9e^FO--p6ouFe8zP0(#yY8LVZbX9J9FT*&f` zTJG$m*76%nleX>>JY?gyD`T*}?LMyR*a>@U9xitOcWW~O>06|(>z0dw7-z3pj$=+G zdba2;8*Vn`yVl<)`CQS5DL17yvd+o3)9Z$ZbpbWUa(_SkAe^OsFniue5_7$Zv^&*8 zWjk!qpYW-a{WjM+C$mMk38M4op7TVGapq=eQZk>0b>Qt765Q(|ZZ^T$%{ZfUJBhd< zGh=toT*bep=37I^^FVm?_D0&srU<{KY=N)$U5zLSw)g$swF`kY=wsriSKGGParyFv zTUr50?6neo&b&^F@juY1{l6t~nte{a%hOGRod$UZn=XxV9_8_V_5v|$V&`%>HnNx+ zLV+qnX3AztY<$V8fVy1YA}?C@Z7*vLAycuLPjbaI1Pkz%y-P9FMVy;TCMMXy*dBlw ztM7JN9Y4A8(=_3wC#M8`42UsW?@N9gzRkf<7w$6ER{@zL-2(_5M;A1F0RmPIg)PQy=-+nEa1~;GU zloJQDRSB`*jgV?}r2r1Y1BYap@1L>4tQT6|dP+CFnsgFzl8<$-eG=2PIZC8hC(i({ zl%&H7Md}tWc>D%mBw>hfh-4RSQ{`66lm3hcTIc;d{UN`PzCWYx4L!Z6{$57<&it{w zlf>1_TbhOx?2Z#w$tR73%7z=9gvD&?~o(|u#9Ix-d-%php9(?g- zGpIbu7G#*)=dymcmG!-d?Iot#Ku+~5&%^px_76Da1Z#JiQbOBWKtj<{gStxdTl)f( zHTR2^SK=p1rcW zJVRdJmAWqGRUutVxThqZjviGLzxkyZ0`-lF5Q9k~H*O2|uHoM_M4W3~Ki{`%zCODc zyGxP`)N6X>Kcr6^Z2b Date: Fri, 16 Oct 2020 14:13:47 +0300 Subject: [PATCH 04/46] add failing test for issue #4875 --- .../Selection/InternalSelectionModelTests.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs index b64812e290..bb893bd35e 100644 --- a/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs @@ -205,6 +205,33 @@ namespace Avalonia.Controls.UnitTests.Selection Assert.Equal(2, target.SelectedIndex); } + [Fact] + public void Raises_Selection_Changed_On_Items_Reset() + { + var items = new ResettingCollection(new[] { "foo", "bar", "baz" }); + var target = CreateTarget(source: items); + + target.SelectedIndex = 1; + + var changed = new List(); + + target.PropertyChanged += (s, e) => changed.Add(e.PropertyName); + + var oldSelectedIndex = target.SelectedIndex; + var oldSelectedItem = target.SelectedItem; + + items.Reset(new string[0]); + + Assert.NotEqual(oldSelectedIndex, target.SelectedIndex); + Assert.NotEqual(oldSelectedItem, target.SelectedItem); + + Assert.Equal(-1, target.SelectedIndex); + Assert.Equal(null, target.SelectedItem); + + Assert.Contains(nameof(target.SelectedIndex), changed); + Assert.Contains(nameof(target.SelectedItem), changed); + } + [Fact] public void Preserves_Selection_On_Source_Changed() { @@ -222,7 +249,7 @@ namespace Avalonia.Controls.UnitTests.Selection bool nullSource = false) { source ??= !nullSource ? new[] { "foo", "bar", "baz" } : null; - + var result = new InternalSelectionModel { SingleSelect = singleSelect, From 82d17aa8ae98160dca63a021d2cd3b675341d489 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 14:19:06 +0300 Subject: [PATCH 05/46] fix selected item notify after been changed issue #4875 --- src/Avalonia.Controls/Selection/SelectionModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Selection/SelectionModel.cs b/src/Avalonia.Controls/Selection/SelectionModel.cs index 3b5d57a7b8..054974e4f1 100644 --- a/src/Avalonia.Controls/Selection/SelectionModel.cs +++ b/src/Avalonia.Controls/Selection/SelectionModel.cs @@ -442,7 +442,8 @@ namespace Avalonia.Controls.Selection RaisePropertyChanged(nameof(SelectedIndex)); } - if (e.Action == NotifyCollectionChangedAction.Remove && e.OldStartingIndex <= oldSelectedIndex) + if ((e.Action == NotifyCollectionChangedAction.Remove && e.OldStartingIndex <= oldSelectedIndex) || + e.Action == NotifyCollectionChangedAction.Reset) { RaisePropertyChanged(nameof(SelectedItem)); } From 21fadbedd6734fd74e07fdeb7c2383b546ce6b38 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 12:43:17 +0300 Subject: [PATCH 06/46] add tests for text issue:4866 --- .../Controls/TextBlockTests.cs | 54 ++++++++++++++++++ ...estrictedHeight_VerticalAlign.expected.png | Bin 0 -> 527 bytes ...estrictedHeight_VerticalAlign.expected.png | Bin 0 -> 527 bytes 3 files changed, 54 insertions(+) create mode 100644 tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png create mode 100644 tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png diff --git a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs index ab769e4ff0..53a2959848 100644 --- a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs +++ b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs @@ -40,5 +40,59 @@ namespace Avalonia.Direct2D1.RenderTests.Controls await RenderToFile(target); CompareImages(); } + + + [Win32Fact("Has text")] + public async Task RestrictedHeight_VerticalAlign() + { + IControl text(VerticalAlignment verticalAlingnment, bool clip = true, bool restrictHeight = true) + { + return new Border() + { + BorderBrush = Brushes.Blue, + BorderThickness = new Thickness(1), + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + Height = restrictHeight ? 20 : double.NaN, + Margin = new Thickness(1), + Child = new TextBlock + { + FontFamily = new FontFamily("Courier New"), + Background = Brushes.Red, + FontSize = 24, + Foreground = Brushes.Black, + Text = "L", + VerticalAlignment = verticalAlingnment, + ClipToBounds = clip + } + }; + } + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 180, + Height = 80, + + Child = new StackPanel() + { + Orientation = Orientation.Horizontal, + Children = + { + text(VerticalAlignment.Stretch, restrictHeight: false), + text(VerticalAlignment.Center), + text(VerticalAlignment.Stretch), + text(VerticalAlignment.Top), + text(VerticalAlignment.Bottom), + text(VerticalAlignment.Center, clip:false), + text(VerticalAlignment.Stretch, clip:false), + text(VerticalAlignment.Top, clip:false), + text(VerticalAlignment.Bottom, clip:false), + } + } + }; + + await RenderToFile(target); + CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a0a14e52616d76ebd7926248e785e0c6b42296 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^TYxx#gAGV-kceLfq*#ibJVQ8upoSx*gMoqZn5T}U;+IFz$-)-2Qso+}Sug{{q>EZbaGG{*& z^BrhqNHVFa5_;F;p$aq=4P+N;Sm#Ql+4(;Fe8A?um%ofx_5WVyv^uebmhOAP3E}(H zCG9Noq|YTwv?afjs6ErKopYshQv6nS-E$W{D;@W4TQ~J+@8*An?jwXI=Mg#)U&G&X+8f+7-6YKb)<8(jSY;J4LA* zLS7lie%u!OY5#?jJ6KrO-q@BLY*L~g?)mTQydzD%YtOHe-W9g&{YH-6{+~*gZH#}j z(QdKPmrGYpeTj~h269&aYirvscK^brXV0%Z-@4hfWUbTo6SKC?Njp1RW&h{GZ);p@ z!i5)L7|fua=M~7lo_83w~vya^yjSMfh`j0J+JDUAFAD)x=QqT z_~xVcen?B5^9CAdVlq2uPwsR5y=Y<0z|in+9^)@(Q)>rl$*mwsPgg&ebxsLQ0Q>~$ ATmS$7 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a0a14e52616d76ebd7926248e785e0c6b42296 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^TYxx#gAGV-kceLfq*#ibJVQ8upoSx*gMoqZn5T}U;+IFz$-)-2Qso+}Sug{{q>EZbaGG{*& z^BrhqNHVFa5_;F;p$aq=4P+N;Sm#Ql+4(;Fe8A?um%ofx_5WVyv^uebmhOAP3E}(H zCG9Noq|YTwv?afjs6ErKopYshQv6nS-E$W{D;@W4TQ~J+@8*An?jwXI=Mg#)U&G&X+8f+7-6YKb)<8(jSY;J4LA* zLS7lie%u!OY5#?jJ6KrO-q@BLY*L~g?)mTQydzD%YtOHe-W9g&{YH-6{+~*gZH#}j z(QdKPmrGYpeTj~h269&aYirvscK^brXV0%Z-@4hfWUbTo6SKC?Njp1RW&h{GZ);p@ z!i5)L7|fua=M~7lo_83w~vya^yjSMfh`j0J+JDUAFAD)x=QqT z_~xVcen?B5^9CAdVlq2uPwsR5y=Y<0z|in+9^)@(Q)>rl$*mwsPgg&ebxsLQ0Q>~$ ATmS$7 literal 0 HcmV?d00001 From dda966676c62932cc3d92a53a0430d03cb3296eb Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 15:38:43 +0300 Subject: [PATCH 07/46] fix Textblock text alignment with restricted height #4866 --- src/Avalonia.Controls/TextBlock.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index d61519e697..31517ba59d 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -3,6 +3,7 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; +using Avalonia.Layout; namespace Avalonia.Controls { @@ -427,14 +428,32 @@ namespace Avalonia.Controls case TextAlignment.Center: offsetX = (width - TextLayout.Size.Width) / 2; break; + case TextAlignment.Right: - offsetX = width - TextLayout.Size.Width; + offsetX = width - TextLayout.Size.Width; break; } var padding = Padding; - using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top))) + var top = padding.Top; + var textSize = TextLayout.Size; + + if (Bounds.Height < textSize.Height) + { + switch (VerticalAlignment) + { + case VerticalAlignment.Center: + top += (Bounds.Height - textSize.Height) / 2; + break; + + case VerticalAlignment.Bottom: + top += (Bounds.Height - textSize.Height); + break; + } + } + + using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, top))) { TextLayout.Draw(context); } From 1dc2b0b12a12542a95bfc40373cf28d44e8fc688 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 15:39:06 +0300 Subject: [PATCH 08/46] fix TextPresenter text alignment with restricted height #4866 --- .../Presenters/TextPresenter.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 6a6d37605d..078d8050bf 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -4,6 +4,7 @@ using Avalonia.Media; using Avalonia.Metadata; using Avalonia.Threading; using Avalonia.VisualTree; +using Avalonia.Layout; namespace Avalonia.Controls.Presenters { @@ -312,7 +313,24 @@ namespace Avalonia.Controls.Presenters context.FillRectangle(background, new Rect(Bounds.Size)); } - context.DrawText(Foreground, new Point(), FormattedText); + double top = 0; + var textSize = FormattedText.Bounds.Size; + + if (Bounds.Height < textSize.Height) + { + switch (VerticalAlignment) + { + case VerticalAlignment.Center: + top += (Bounds.Height - textSize.Height) / 2; + break; + + case VerticalAlignment.Bottom: + top += (Bounds.Height - textSize.Height); + break; + } + } + + context.DrawText(Foreground, new Point(0, top), FormattedText); } public override void Render(DrawingContext context) From 5f3d81bbfcd33ad5bca32b188135355dcbde08c3 Mon Sep 17 00:00:00 2001 From: Mikhail Poliudov Date: Wed, 21 Oct 2020 19:10:25 +0700 Subject: [PATCH 09/46] 1. implemented Label control (again) 2. changed implementation of ControlPresenter to be same as WPF one. Added additional prebuild DataTemplate (for ContentPresenter) 3. added default style for Label control (both Default and Fluent themes) 4. Added sample to TextBox page (not sure needed other sample) 5. Added styles for Label in ControlCatalog/App.xaml --- samples/ControlCatalog/App.xaml | 10 +++ samples/ControlCatalog/Pages/TextBoxPage.xaml | 36 ++++----- src/Avalonia.Controls/Label.cs | 74 +++++++++++++++++++ .../Presenters/ContentPresenter.cs | 25 ++++++- .../Templates/FuncDataTemplate.cs | 28 ++++++- src/Avalonia.Themes.Default/DefaultTheme.xaml | 3 +- src/Avalonia.Themes.Default/Label.xaml | 17 +++++ src/Avalonia.Themes.Fluent/FluentTheme.xaml | 3 +- src/Avalonia.Themes.Fluent/Label.xaml | 17 +++++ 9 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 src/Avalonia.Controls/Label.cs create mode 100644 src/Avalonia.Themes.Default/Label.xaml create mode 100644 src/Avalonia.Themes.Fluent/Label.xaml diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 9bac320c79..6aad44c0d5 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -12,6 +12,16 @@ + + + diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 481a459159..8b07ac3f85 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -2,8 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.TextBoxPage"> - TextBox - A control into which the user can input text + + - + - - resm fonts - - - - - - - - res fonts - - - - - + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls/Label.cs b/src/Avalonia.Controls/Label.cs new file mode 100644 index 0000000000..76708b8f00 --- /dev/null +++ b/src/Avalonia.Controls/Label.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace Avalonia.Controls +{ + /// + /// Label control. Focuses on pointer click or access key press (Alt + accessKey) + /// + public class Label : ContentControl + { + /// + /// Defines the Direct property + /// + public static readonly DirectProperty TargetProperty = + AvaloniaProperty.RegisterDirect(nameof(Target), lbl => lbl.Target, (lbl, inp) => lbl.Target = inp); + + /// + /// Label focus target storage field + /// + private IInputElement _target; + + /// + /// Label focus Target + /// + public IInputElement Target + { + get => _target; + set => SetAndRaise(TargetProperty, ref _target, value); + } + + static Label() + { + AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler