From 6a1dbaea6587a7800180cb1986916b1a591b2155 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 19 Apr 2021 19:23:15 +0100 Subject: [PATCH 01/26] initial implementation for skia. --- .../Media/Imaging/WriteableBitmap.cs | 45 +++++++++++++ .../Platform/IPlatformRenderInterface.cs | 32 +++++++++ .../Avalonia.Skia/PlatformRenderInterface.cs | 25 +++++++ src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 65 +++++++++++++++++++ 4 files changed, 167 insertions(+) diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index b86444bc2f..d10c743894 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -1,5 +1,8 @@ using System; +using System.IO; +using System.Threading.Tasks; using Avalonia.Platform; +using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Imaging { @@ -34,8 +37,50 @@ namespace Avalonia.Media.Imaging { } + private WriteableBitmap(IWriteableBitmapImpl impl) : base(impl) + { + + } + public ILockedFramebuffer Lock() => ((IWriteableBitmapImpl) PlatformImpl.Item).Lock(); + public static WriteableBitmap Decode(Stream stream) + { + var ri = AvaloniaLocator.Current.GetService(); + + return new WriteableBitmap(ri.LoadWriteableBitmap(stream)); + } + + /// + /// Loads a WriteableBitmap from a stream and decodes at the desired width. Aspect ratio is maintained. + /// This is more efficient than loading and then resizing. + /// + /// The stream to read the bitmap from. This can be any supported image format. + /// The desired width of the resulting bitmap. + /// The to use should any scaling be required. + /// An instance of the class. + public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + var ri = AvaloniaLocator.Current.GetService(); + + return new WriteableBitmap(ri.LoadWriteableBitmapToWidth(stream, width, interpolationMode)); + } + + /// + /// Loads a Bitmap from a stream and decodes at the desired height. Aspect ratio is maintained. + /// This is more efficient than loading and then resizing. + /// + /// The stream to read the bitmap from. This can be any supported image format. + /// The desired height of the resulting bitmap. + /// The to use should any scaling be required. + /// An instance of the class. + public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + var ri = AvaloniaLocator.Current.GetService(); + + return new WriteableBitmap(ri.LoadWriteableBitmapToHeight(stream, height, interpolationMode)); + } + private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat) { var ri = AvaloniaLocator.Current.GetService(); diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index fb4a4427b7..c141f2024f 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -100,6 +100,38 @@ namespace Avalonia.Platform /// An . IBitmapImpl LoadBitmap(Stream stream); + /// + /// Loads a WriteableBitmap implementation from a stream to a specified width maintaining aspect ratio. + /// + /// The stream to read the bitmap from. + /// The desired width of the resulting bitmap. + /// The to use should resizing be required. + /// An . + IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality); + + /// + /// Loads a WriteableBitmap implementation from a stream to a specified height maintaining aspect ratio. + /// + /// The stream to read the bitmap from. + /// The desired height of the resulting bitmap. + /// The to use should resizing be required. + /// An . + IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality); + + /// + /// Loads a WriteableBitmap implementation from a file.. + /// + /// The filename of the bitmap. + /// An . + IWriteableBitmapImpl LoadWriteableBitmap(string fileName); + + /// + /// Loads a WriteableBitmap implementation from a file.. + /// + /// The stream to read the bitmap from. + /// An . + IWriteableBitmapImpl LoadWriteableBitmap(Stream stream); + /// /// Loads a bitmap implementation from a stream to a specified width maintaining aspect ratio. /// diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 6d0be9f64d..2ed4948d41 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -77,6 +77,31 @@ namespace Avalonia.Skia return new ImmutableBitmap(stream); } + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + return new WriteableBitmapImpl(stream, width, true, interpolationMode); + } + + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + return new WriteableBitmapImpl(stream, height, false, interpolationMode); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + using (var stream = File.OpenRead(fileName)) + { + return LoadWriteableBitmap(stream); + } + } + + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + { + return new WriteableBitmapImpl(stream); + } + /// public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index d48e7d10e6..c06bd0d057 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -1,8 +1,10 @@ using System; using System.IO; using System.Threading; +using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Skia.Helpers; +using Avalonia.Visuals.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia @@ -15,7 +17,70 @@ namespace Avalonia.Skia private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc; private readonly SKBitmap _bitmap; private readonly object _lock = new object(); + + /// + /// Create immutable bitmap from given stream. + /// + /// Stream containing encoded data. + public WriteableBitmapImpl(Stream stream) + { + using (var skiaStream = new SKManagedStream(stream)) + { + _bitmap = SKBitmap.Decode(skiaStream); + + if (_bitmap == null) + { + throw new ArgumentException("Unable to load bitmap from provided data"); + } + + PixelSize = new PixelSize(_bitmap.Width, _bitmap.Height); + Dpi = new Vector(96, 96); + } + } + + public WriteableBitmapImpl(Stream stream, int decodeSize, bool horizontal, BitmapInterpolationMode interpolationMode) + { + using (var skStream = new SKManagedStream(stream)) + using (var codec = SKCodec.Create(skStream)) + { + var info = codec.Info; + // get the scale that is nearest to what we want (eg: jpg returned 512) + var supportedScale = codec.GetScaledDimensions(horizontal ? ((float)decodeSize / info.Width) : ((float)decodeSize / info.Height)); + + // decode the bitmap at the nearest size + var nearest = new SKImageInfo(supportedScale.Width, supportedScale.Height); + var bmp = SKBitmap.Decode(codec, nearest); + + // now scale that to the size that we want + var realScale = horizontal ? ((double)info.Height / info.Width) : ((double)info.Width / info.Height); + + SKImageInfo desired; + + + if (horizontal) + { + desired = new SKImageInfo(decodeSize, (int)(realScale * decodeSize)); + } + else + { + desired = new SKImageInfo((int)(realScale * decodeSize), decodeSize); + } + + if (bmp.Width != desired.Width || bmp.Height != desired.Height) + { + var scaledBmp = bmp.Resize(desired, interpolationMode.ToSKFilterQuality()); + bmp.Dispose(); + bmp = scaledBmp; + } + + _bitmap = bmp; + + PixelSize = new PixelSize(bmp.Width, bmp.Height); + Dpi = new Vector(96, 96); + } + } + /// /// Create new writeable bitmap. /// From a4ebe5dbb51d5a1370724c41a6913e1eeafcdb8e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 19 Apr 2021 20:32:48 +0100 Subject: [PATCH 02/26] add direct2d implementation. --- .../HeadlessPlatformRenderInterface.cs | 22 +++++++++++++++++++ .../Avalonia.Direct2D1/Direct2D1Platform.cs | 22 +++++++++++++++++++ .../Media/Imaging/WriteableWicBitmapImpl.cs | 17 ++++++++++++++ .../MockPlatformRenderInterface.cs | 22 +++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 62cac378d7..7b68a0250f 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -70,6 +70,28 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); } + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); + } + + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + { + return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); + } + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 6ae27870e8..e8757bbb34 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -188,6 +188,28 @@ namespace Avalonia.Direct2D1 return new WicBitmapImpl(stream); } + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + return new WriteableWicBitmapImpl(stream, width, true, interpolationMode); + } + + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + return new WriteableWicBitmapImpl(stream, height, false, interpolationMode); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + return new WriteableWicBitmapImpl(fileName); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + { + return new WriteableWicBitmapImpl(stream); + } + /// public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs index 3261c45f15..fef6a9aa82 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Avalonia.Platform; using SharpDX.WIC; using PixelFormat = Avalonia.Platform.PixelFormat; @@ -7,11 +8,27 @@ namespace Avalonia.Direct2D1.Media.Imaging { class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl { + public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal, + Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + : base(stream, decodeSize, horizontal, interpolationMode) + { + } + public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat, AlphaFormat? alphaFormat) : base(size, dpi, pixelFormat, alphaFormat) { } + public WriteableWicBitmapImpl(Stream stream) + : base(stream) + { + } + + public WriteableWicBitmapImpl(string fileName) + : base(fileName) + { + } + class LockedBitmap : ILockedFramebuffer { private readonly WriteableWicBitmapImpl _parent; diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index e73a76357a..b7f9cc34ca 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -66,6 +66,28 @@ namespace Avalonia.UnitTests return Mock.Of(); } + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + { + throw new NotImplementedException(); + } + public IBitmapImpl LoadBitmap(string fileName) { return Mock.Of(); From f9173594aeb884f19af3ffc80694950c0dede021 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 19 Apr 2021 20:39:11 +0100 Subject: [PATCH 03/26] add api diff. --- src/Avalonia.Controls/ApiCompatBaseline.txt | 2 +- src/Avalonia.Visuals/ApiCompatBaseline.txt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 3a6810eed9..613858056c 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -7,4 +7,4 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -Total Issues: 7 +Total Issues: 8 diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 805d1955ea..a39a8600eb 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -6,4 +6,8 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avaloni 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. -Total Issues: 7 +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. +Total Issues: 11 From 79d96ca891fdc38be519255a5985f71354c65c67 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 19 Apr 2021 20:45:35 +0100 Subject: [PATCH 04/26] implement missing stubs. --- .../NullRenderingPlatform.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index 1570205456..eb5c031fd1 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -61,6 +61,28 @@ namespace Avalonia.Benchmarks throw new NotImplementedException(); } + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + { + throw new NotImplementedException(); + } + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { throw new NotImplementedException(); From 57cbf67879dc8db19f440846679cce249efddf99 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 19 Apr 2021 20:46:46 +0100 Subject: [PATCH 05/26] more missing stubs. --- .../VisualTree/MockRenderInterface.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs index 6d0683e699..6ce7f16d40 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs @@ -42,6 +42,28 @@ namespace Avalonia.Visuals.UnitTests.VisualTree throw new NotImplementedException(); } + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, + BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + throw new NotImplementedException(); + } + + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + { + throw new NotImplementedException(); + } + public IBitmapImpl LoadBitmap(string fileName) { throw new NotImplementedException(); From 4340c69412211ce5675ec88a284dba36e407935f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 10 May 2021 21:57:13 +0100 Subject: [PATCH 06/26] fix comment --- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index c06bd0d057..85f9d8db91 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -19,7 +19,7 @@ namespace Avalonia.Skia private readonly object _lock = new object(); /// - /// Create immutable bitmap from given stream. + /// Create a WriteableBitmap from given stream. /// /// Stream containing encoded data. public WriteableBitmapImpl(Stream stream) From fec55f3f2792589b71e21e9a56f66fe899e6619b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 11 May 2021 22:24:41 +0100 Subject: [PATCH 07/26] fix nits. --- src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs | 4 ++-- src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs | 6 +++--- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index d10c743894..b723002ce0 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -59,7 +59,7 @@ namespace Avalonia.Media.Imaging /// The desired width of the resulting bitmap. /// The to use should any scaling be required. /// An instance of the class. - public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + public new static WriteableBitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { var ri = AvaloniaLocator.Current.GetService(); @@ -74,7 +74,7 @@ namespace Avalonia.Media.Imaging /// The desired height of the resulting bitmap. /// The to use should any scaling be required. /// An instance of the class. - public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + public new static WriteableBitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { var ri = AvaloniaLocator.Current.GetService(); diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index 41f726ac96..de67aca5a8 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -106,7 +106,7 @@ namespace Avalonia.Platform /// The stream to read the bitmap from. /// The desired width of the resulting bitmap. /// The to use should resizing be required. - /// An . + /// An . IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality); /// @@ -119,14 +119,14 @@ namespace Avalonia.Platform IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality); /// - /// Loads a WriteableBitmap implementation from a file.. + /// Loads a WriteableBitmap implementation from a file. /// /// The filename of the bitmap. /// An . IWriteableBitmapImpl LoadWriteableBitmap(string fileName); /// - /// Loads a WriteableBitmap implementation from a file.. + /// Loads a WriteableBitmap implementation from a file. /// /// The stream to read the bitmap from. /// An . diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 85f9d8db91..63a1e8f869 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -34,7 +34,7 @@ namespace Avalonia.Skia } PixelSize = new PixelSize(_bitmap.Width, _bitmap.Height); - Dpi = new Vector(96, 96); + Dpi = SkiaPlatform.DefaultDpi; } } @@ -77,7 +77,7 @@ namespace Avalonia.Skia _bitmap = bmp; PixelSize = new PixelSize(bmp.Width, bmp.Height); - Dpi = new Vector(96, 96); + Dpi = SkiaPlatform.DefaultDpi; } } From 6fc5af3a68dd22130de3d6c6565ec8003d282589 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 12 May 2021 20:24:13 +0100 Subject: [PATCH 08/26] use datatemplates instead of itemstemplate for datavalidationerrors. --- src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index b513fd5eae..b1c8650292 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -28,13 +28,13 @@ - + - + From b0d5e3197c1580af8aff283ce9ef9778eb4ef9bc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 13 May 2021 01:00:34 -0400 Subject: [PATCH 09/26] Include inner exception when throwing from BindingEntry --- src/Avalonia.Base/PropertyStore/BindingEntry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index 1b29338f07..362736eb06 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs @@ -79,7 +79,7 @@ namespace Avalonia.PropertyStore public void OnError(Exception error) { - throw new NotImplementedException(); + throw new NotImplementedException("BindingEntry.OnError is not implemented", error); } public void OnNext(BindingValue value) From 2043ed3a30ca9feab071f7683b403b1bf059d912 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 13 May 2021 10:26:22 +0100 Subject: [PATCH 10/26] dont provide any templates for datavalidation errors. --- .../Controls/DataValidationErrors.xaml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index b1c8650292..53c5d4eda6 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -27,14 +27,12 @@ + From b3e2b05d1ffefe52fa5f3a69ab316b6e11f55fe0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 13:28:20 +0200 Subject: [PATCH 11/26] Implement StretchDirection for Viewbox. Cleanup Viewbox. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 28 ++++++- .../ControlCatalog/Pages/ViewboxPage.xaml.cs | 31 ++++++- src/Avalonia.Controls/Image.cs | 4 +- src/Avalonia.Controls/Viewbox.cs | 81 +++++++------------ .../ViewboxTests.cs | 56 +++++++++++++ 5 files changed, 141 insertions(+), 59 deletions(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index e78cf2bc22..f8b4a62290 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -22,14 +22,38 @@ - + Viewbox A control used to scale single child. + + + + + + + + + + + + + + + + + + + + + + + None Fill diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs index 1b5f4bc7f4..7a2bceb8f2 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs @@ -1,18 +1,47 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Media; namespace ControlCatalog.Pages { public class ViewboxPage : UserControl { + private readonly Viewbox _viewbox; + private readonly ComboBox _stretchSelector; + public ViewboxPage() { - this.InitializeComponent(); + InitializeComponent(); + + _viewbox = this.FindControl("Viewbox"); + + _stretchSelector = this.FindControl("StretchSelector"); + + _stretchSelector.Items = new[] + { + Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None + }; + + _stretchSelector.SelectedIndex = 0; + + var stretchDirectionSelector = this.FindControl("StretchDirectionSelector"); + + stretchDirectionSelector.Items = new[] + { + StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly + }; + + stretchDirectionSelector.SelectedIndex = 0; } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } + + private void StretchSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + _viewbox.Stretch = (Stretch) _stretchSelector.SelectedItem!; + } } } diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 247b62d3cf..c448729643 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -31,8 +31,8 @@ namespace Avalonia.Controls static Image() { - AffectsRender(SourceProperty, StretchProperty); - AffectsMeasure(SourceProperty, StretchProperty); + AffectsRender(SourceProperty, StretchProperty, StretchDirectionProperty); + AffectsMeasure(SourceProperty, StretchProperty, StretchDirectionProperty); } /// diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 781c93bcbe..b65f4b31d8 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -1,40 +1,50 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; namespace Avalonia.Controls { /// - /// Viewbox is used to scale single child. + /// Viewbox is used to scale single child to fit in the available space. /// /// public class Viewbox : Decorator { /// - /// The stretch property + /// Defines the property. /// public static readonly AvaloniaProperty StretchProperty = - AvaloniaProperty.RegisterDirect(nameof(Stretch), - v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform); + AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); + + /// + /// Defines the property. + /// + public static readonly StyledProperty StretchDirectionProperty = + AvaloniaProperty.Register(nameof(StretchDirection), StretchDirection.Both); private Stretch _stretch = Stretch.Uniform; + static Viewbox() + { + ClipToBoundsProperty.OverrideDefaultValue(true); + AffectsMeasure(StretchProperty, StretchDirectionProperty); + } + /// /// Gets or sets the stretch mode, /// which determines how child fits into the available space. /// - /// - /// The stretch. - /// public Stretch Stretch { get => _stretch; set => SetAndRaise(StretchProperty, ref _stretch, value); } - static Viewbox() + /// + /// Gets or sets a value controlling in what direction contents will be stretched. + /// + public StretchDirection StretchDirection { - ClipToBoundsProperty.OverrideDefaultValue(true); - AffectsMeasure(StretchProperty); + get => GetValue(StretchDirectionProperty); + set => SetValue(StretchDirectionProperty, value); } protected override Size MeasureOverride(Size availableSize) @@ -47,9 +57,9 @@ namespace Avalonia.Controls var childSize = child.DesiredSize; - var scale = GetScale(availableSize, childSize, Stretch); + var size = Stretch.CalculateSize(availableSize, childSize, StretchDirection); - return (childSize * scale).Constrain(availableSize); + return size.Constrain(availableSize); } return new Size(); @@ -62,7 +72,9 @@ namespace Avalonia.Controls if (child != null) { var childSize = child.DesiredSize; - var scale = GetScale(finalSize, childSize, Stretch); + 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) @@ -81,44 +93,5 @@ namespace Avalonia.Controls return new Size(); } - - private static Vector GetScale(Size availableSize, Size childSize, Stretch stretch) - { - double scaleX = 1.0; - double scaleY = 1.0; - - bool validWidth = !double.IsPositiveInfinity(availableSize.Width); - bool validHeight = !double.IsPositiveInfinity(availableSize.Height); - - if (stretch != Stretch.None && (validWidth || validHeight)) - { - scaleX = childSize.Width <= 0.0 ? 0.0 : availableSize.Width / childSize.Width; - scaleY = childSize.Height <= 0.0 ? 0.0 : availableSize.Height / childSize.Height; - - if (!validWidth) - { - scaleX = scaleY; - } - else if (!validHeight) - { - scaleY = scaleX; - } - else - { - switch (stretch) - { - case Stretch.Uniform: - scaleX = scaleY = Math.Min(scaleX, scaleY); - break; - - case Stretch.UniformToFill: - scaleX = scaleY = Math.Max(scaleX, scaleY); - break; - } - } - } - - return new Vector(scaleX, scaleY); - } } } diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index e005bafbf9..7eaec35506 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -114,5 +114,61 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(2.0, scaleTransform.ScaleX); Assert.Equal(2.0, scaleTransform.ScaleY); } + + [Theory] + [InlineData(50, 100, 50, 100, 50, 100, 1)] + [InlineData(50, 100, 150, 150, 50, 100, 1)] + [InlineData(50, 100, 25, 50, 25, 50, 0.5)] + public void Viewbox_Should_Return_Correct_SizeAndScale_StretchDirection_DownOnly( + double childWidth, double childHeight, + double viewboxWidth, double viewboxHeight, + double expectedWidth, double expectedHeight, + double expectedScale) + { + var target = new Viewbox + { + Child = new Control { Width = childWidth, Height = childHeight }, + StretchDirection = StretchDirection.DownOnly + }; + + target.Measure(new Size(viewboxWidth, viewboxHeight)); + target.Arrange(new Rect(default, target.DesiredSize)); + + Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); + + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(expectedScale, scaleTransform.ScaleX); + Assert.Equal(expectedScale, scaleTransform.ScaleY); + } + + [Theory] + [InlineData(50, 100, 50, 100, 50, 100, 1)] + [InlineData(50, 100, 25, 50, 25, 50, 1)] + [InlineData(50, 100, 150, 150, 75, 150, 1.5)] + public void Viewbox_Should_Return_Correct_SizeAndScale_StretchDirection_UpOnly( + double childWidth, double childHeight, + double viewboxWidth, double viewboxHeight, + double expectedWidth, double expectedHeight, + double expectedScale) + { + var target = new Viewbox + { + Child = new Control { Width = childWidth, Height = childHeight }, + StretchDirection = StretchDirection.UpOnly + }; + + target.Measure(new Size(viewboxWidth, viewboxHeight)); + target.Arrange(new Rect(default, target.DesiredSize)); + + Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); + + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(expectedScale, scaleTransform.ScaleX); + Assert.Equal(expectedScale, scaleTransform.ScaleY); + } } } From 6e2384deb804da5cf3d453933f88ba6bce0cc126 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 14:57:36 +0200 Subject: [PATCH 12/26] Use an ellipse instead. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index f8b4a62290..f9e9c983d5 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -34,7 +34,7 @@ - + From f775f9d3f0af3eb7fa3aaa6be43659c7783767b0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 16:00:14 +0200 Subject: [PATCH 13/26] Remove old viewbox samples. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index f9e9c983d5..6e649b39a5 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -1,26 +1,6 @@ - - - F1 M 16.6309,18.6563C 17.1309, - 8.15625 29.8809,14.1563 29.8809, - 14.1563C 30.8809,11.1563 34.1308, - 11.4063 34.1308,11.4063C 33.5,12 - 34.6309,13.1563 34.6309,13.1563C - 32.1309,13.1562 31.1309,14.9062 - 31.1309,14.9062C 41.1309,23.9062 - 32.6309,27.9063 32.6309,27.9062C - 24.6309,24.9063 21.1309,22.1562 - 16.6309,18.6563 Z M 16.6309,19.9063C - 21.6309,24.1563 25.1309,26.1562 - 31.6309,28.6562C 31.6309,28.6562 - 26.3809,39.1562 18.3809,36.1563C - 18.3809,36.1563 18,38 16.3809,36.9063C - 15,36 16.3809,34.9063 16.3809,34.9063C - 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z - - @@ -51,40 +31,5 @@ - - None - Fill - Uniform - UniformToFill - - - Hello World! - - - Hello World! - - - Hello World! - - - Hello World! - - - - - - - - - - - - - - - From 6d0ac9a5417bdc01048b5e86f9e4a985f58ca091 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 16:33:49 +0200 Subject: [PATCH 14/26] Fix property type on Stretch. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 7 ++++--- samples/ControlCatalog/Pages/ViewboxPage.xaml.cs | 16 +++------------- src/Avalonia.Controls/Viewbox.cs | 3 ++- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index 6e649b39a5..ef802db33e 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -12,8 +12,9 @@ - + @@ -25,7 +26,7 @@ - + diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs index 7a2bceb8f2..94b3f3ea14 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs @@ -6,23 +6,18 @@ namespace ControlCatalog.Pages { public class ViewboxPage : UserControl { - private readonly Viewbox _viewbox; - private readonly ComboBox _stretchSelector; - public ViewboxPage() { InitializeComponent(); - _viewbox = this.FindControl("Viewbox"); - - _stretchSelector = this.FindControl("StretchSelector"); + var stretchSelector = this.FindControl("StretchSelector"); - _stretchSelector.Items = new[] + stretchSelector.Items = new[] { Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None }; - _stretchSelector.SelectedIndex = 0; + stretchSelector.SelectedIndex = 0; var stretchDirectionSelector = this.FindControl("StretchDirectionSelector"); @@ -38,10 +33,5 @@ namespace ControlCatalog.Pages { AvaloniaXamlLoader.Load(this); } - - private void StretchSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e) - { - _viewbox.Stretch = (Stretch) _stretchSelector.SelectedItem!; - } } } diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index b65f4b31d8..15ca070de2 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -12,7 +12,8 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly AvaloniaProperty StretchProperty = - AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); + AvaloniaProperty.RegisterDirect(nameof(Stretch), + v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform); /// /// Defines the property. From 59d51d6a56d1a6b62051e656be331b7cea39f668 Mon Sep 17 00:00:00 2001 From: Splitwirez <34009058+Splitwirez@users.noreply.github.com> Date: Fri, 14 May 2021 00:10:50 -0700 Subject: [PATCH 15/26] New Fluent expander style (#5573) * Updated Fluent theme Expander style. closes #5358 * Fixed padding mismatch between header and content * Fixed CornerRadii, unleashed the c o l o u r e s * Use CornerRadiusFilterConverter instead of expander specific converters * Formatting * Fix default theme in ControlCatalog Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com> Co-authored-by: Max Katz --- samples/ControlCatalog/App.xaml.cs | 8 + src/Avalonia.Themes.Fluent/Accents/Base.xaml | 8 +- .../Controls/Expander.xaml | 317 +++++++++++------- 3 files changed, 219 insertions(+), 114 deletions(-) diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 020fb2fff3..f3ec7b48aa 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -39,6 +39,10 @@ namespace ControlCatalog public static Styles DefaultLight = new Styles { + new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") + }, new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") @@ -60,6 +64,10 @@ namespace ControlCatalog public static Styles DefaultDark = new Styles { + new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") + }, new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") diff --git a/src/Avalonia.Themes.Fluent/Accents/Base.xaml b/src/Avalonia.Themes.Fluent/Accents/Base.xaml index 8597c76998..1e2acf736d 100644 --- a/src/Avalonia.Themes.Fluent/Accents/Base.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/Base.xaml @@ -1,6 +1,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index a6371a5be9..3f70939953 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -1,141 +1,232 @@ - + + + + + + + Expanded content + + + + + Expanded content + + + + + Expanded content + + + + + Expanded content + + + + + + + 16 + 16 + + 1 + + 1,1,0,1 + 1,1,1,0 + 0,1,1,1 + 1,0,1,1 + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + From 4db1f41f72ccec0f076015513795ca7e9089987d Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 14 May 2021 22:58:51 +0200 Subject: [PATCH 16/26] Change Viewbox.Stretch to a styled property --- src/Avalonia.Controls/ApiCompatBaseline.txt | 3 ++- src/Avalonia.Controls/Viewbox.cs | 11 ++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 7199c15d21..a79b3b4d7b 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -4,10 +4,11 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not 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. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist 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 void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -Total Issues: 11 +Total Issues: 12 diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 15ca070de2..624c61bb82 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -11,9 +11,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly AvaloniaProperty StretchProperty = - AvaloniaProperty.RegisterDirect(nameof(Stretch), - v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform); + public static readonly StyledProperty StretchProperty = + AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); /// /// Defines the property. @@ -21,8 +20,6 @@ namespace Avalonia.Controls public static readonly StyledProperty StretchDirectionProperty = AvaloniaProperty.Register(nameof(StretchDirection), StretchDirection.Both); - private Stretch _stretch = Stretch.Uniform; - static Viewbox() { ClipToBoundsProperty.OverrideDefaultValue(true); @@ -35,8 +32,8 @@ namespace Avalonia.Controls /// public Stretch Stretch { - get => _stretch; - set => SetAndRaise(StretchProperty, ref _stretch, value); + get => GetValue(StretchProperty); + set => SetValue(StretchProperty, value); } /// From 752412933047cb74f0e2ad5a9dd2dcc6c295d036 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 15 May 2021 00:39:11 +0200 Subject: [PATCH 17/26] Cleanup layout visualizer and improve UX. --- .../ViewModels/ControlLayoutViewModel.cs | 29 ++- .../Diagnostics/Views/ControlDetailsView.xaml | 185 +++++++++++------- .../Diagnostics/Views/ThicknessEditor.cs | 21 +- 3 files changed, 144 insertions(+), 91 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs index fd2e4c3355..ef227f7374 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs @@ -1,4 +1,6 @@ +using System; using System.ComponentModel; +using System.Text; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.VisualTree; @@ -95,12 +97,27 @@ namespace Avalonia.Diagnostics.ViewModels { string CreateConstraintInfo(StyledProperty minProperty, StyledProperty maxProperty) { - if (ao.IsSet(minProperty) || ao.IsSet(maxProperty)) + bool hasMin = ao.IsSet(minProperty); + bool hasMax = ao.IsSet(maxProperty); + + if (hasMin || hasMax) { - var minValue = ao.GetValue(minProperty); - var maxValue = ao.GetValue(maxProperty); + var builder = new StringBuilder(); + + if (hasMin) + { + var minValue = ao.GetValue(minProperty); + builder.AppendFormat("Min: {0}", Math.Round(minValue, 2)); + builder.AppendLine(); + } + + if (hasMax) + { + var maxValue = ao.GetValue(maxProperty); + builder.AppendFormat("Max: {0}", Math.Round(maxValue, 2)); + } - return $"{minValue} < size < {maxValue}"; + return builder.ToString(); } return null; @@ -183,8 +200,8 @@ namespace Avalonia.Diagnostics.ViewModels { var size = _control.Bounds; - Width = size.Width; - Height = size.Height; + Width = Math.Round(size.Width, 2); + Height = Math.Round(size.Height, 2); } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index 9ba576c826..22193985b4 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -8,11 +8,16 @@ + + + + + @@ -24,43 +29,60 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -110,47 +132,66 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs index c7611c8c46..2f03a23fdf 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ThicknessEditor.cs @@ -1,21 +1,9 @@ using Avalonia.Controls; using Avalonia.Data; -using Avalonia.Data.Converters; using Avalonia.Media; namespace Avalonia.Diagnostics.Views { - internal static class Converters - { - public static IValueConverter HasConstraintConverter = - new FuncValueConverter(ConvertToDecoration); - - private static TextDecorationCollection ConvertToDecoration(object arg) - { - return arg != null ? TextDecorations.Underline : null; - } - } - internal class ThicknessEditor : ContentControl { public static readonly DirectProperty ThicknessProperty = @@ -44,6 +32,14 @@ namespace Avalonia.Diagnostics.Views AvaloniaProperty.RegisterDirect(nameof(Bottom), o => o.Bottom, (o, v) => o.Bottom = v); + public static readonly StyledProperty HighlightProperty = + AvaloniaProperty.Register(nameof(Highlight)); + + public IBrush Highlight + { + get => GetValue(HighlightProperty); + set => SetValue(HighlightProperty, value); + } private Thickness _thickness; private string _header; @@ -52,7 +48,6 @@ namespace Avalonia.Diagnostics.Views private double _top; private double _right; private double _bottom; - private bool _isUpdatingThickness; public Thickness Thickness From b059a8a178a9617abac851803ea7d5cc89cb6460 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 15 May 2021 13:26:11 +0200 Subject: [PATCH 18/26] Fix highlight disappearing over guidelines. --- .../Diagnostics/Views/ControlDetailsView.xaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index 22193985b4..50534e77b8 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -183,6 +183,7 @@ + From 4d4ca6806ce9b90ae84cb4c3f311be275ed39667 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 15 May 2021 15:53:42 +0200 Subject: [PATCH 19/26] Avoid allocating lists in Classes. --- src/Avalonia.Styling/Controls/Classes.cs | 39 ++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Styling/Controls/Classes.cs b/src/Avalonia.Styling/Controls/Classes.cs index 4e2783d4ec..50605661fa 100644 --- a/src/Avalonia.Styling/Controls/Classes.cs +++ b/src/Avalonia.Styling/Controls/Classes.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using Avalonia.Collections; +#nullable enable + namespace Avalonia.Controls { /// @@ -90,7 +92,7 @@ namespace Avalonia.Controls } /// - /// Remvoes all non-pseudoclasses from the collection. + /// Removes all non-pseudoclasses from the collection. /// public override void Clear() { @@ -135,7 +137,7 @@ namespace Avalonia.Controls /// public override void InsertRange(int index, IEnumerable names) { - var c = new List(); + List? toInsert = null; foreach (var name in names) { @@ -143,11 +145,16 @@ namespace Avalonia.Controls if (!Contains(name)) { - c.Add(name); + toInsert ??= new List(); + + toInsert.Add(name); } } - base.InsertRange(index, c); + if (toInsert != null) + { + base.InsertRange(index, toInsert); + } } /// @@ -176,19 +183,21 @@ namespace Avalonia.Controls /// public override void RemoveAll(IEnumerable names) { - var c = new List(); + List? toRemove = null; foreach (var name in names) { ThrowIfPseudoclass(name, "removed"); - if (Contains(name)) - { - c.Add(name); - } + toRemove ??= new List(); + + toRemove.Add(name); } - base.RemoveAll(c); + if (toRemove != null) + { + base.RemoveAll(toRemove); + } } /// @@ -223,7 +232,7 @@ namespace Avalonia.Controls /// The new contents of the collection. public void Replace(IList source) { - var toRemove = new List(); + List? toRemove = null; foreach (var name in source) { @@ -234,11 +243,17 @@ namespace Avalonia.Controls { if (!name.StartsWith(":")) { + toRemove ??= new List(); + toRemove.Add(name); } } - base.RemoveAll(toRemove); + if (toRemove != null) + { + base.RemoveAll(toRemove); + } + base.AddRange(source); } From 08382e5bf554381a327e215b191d901ab4b16663 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 15 May 2021 20:52:59 -0400 Subject: [PATCH 20/26] Fix SolidColorBrushAnimator NRE --- .../Animation/Animators/SolidColorBrushAnimator.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index cec96fecf8..a56cc1de8c 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -12,6 +12,11 @@ namespace Avalonia.Animation.Animators { public override ISolidColorBrush Interpolate(double progress, ISolidColorBrush oldValue, ISolidColorBrush newValue) { + if (oldValue is null || newValue is null) + { + return oldValue; + } + return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color)); } @@ -26,6 +31,11 @@ namespace Avalonia.Animation.Animators { public override SolidColorBrush Interpolate(double progress, SolidColorBrush oldValue, SolidColorBrush newValue) { + if (oldValue is null || newValue is null) + { + return oldValue; + } + return new SolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color)); } } From 34505c4c8b41c9f98553e4b2f500a8462be5811f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 15 May 2021 20:53:28 -0400 Subject: [PATCH 21/26] Reuse Animator.Interpolate in transitions code --- .../Transitions/DoubleTransition.cs | 10 ++++----- .../Transitions/FloatTransition.cs | 7 +++++-- .../Transitions/IntegerTransition.cs | 7 +++++-- .../Transitions/CornerRadiusTransition.cs | 21 +++++-------------- .../Animation/Transitions/PointTransition.cs | 10 ++++----- .../Animation/Transitions/SizeTransition.cs | 10 ++++----- .../Transitions/ThicknessTransition.cs | 10 ++++----- .../Animation/Transitions/VectorTransition.cs | 10 ++++----- 8 files changed, 40 insertions(+), 45 deletions(-) diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs index 8cae1e1f81..d5bb1aac20 100644 --- a/src/Avalonia.Animation/Transitions/DoubleTransition.cs +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,15 +10,13 @@ namespace Avalonia.Animation /// public class DoubleTransition : Transition { + private static readonly DoubleAnimator s_animator = new DoubleAnimator(); + /// public override IObservable DoTransition(IObservable progress, double oldValue, double newValue) { return progress - .Select(p => - { - var f = Easing.Ease(p); - return ((newValue - oldValue) * f) + oldValue; - }); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Animation/Transitions/FloatTransition.cs b/src/Avalonia.Animation/Transitions/FloatTransition.cs index 427563e559..37b644fa96 100644 --- a/src/Avalonia.Animation/Transitions/FloatTransition.cs +++ b/src/Avalonia.Animation/Transitions/FloatTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,12 +10,13 @@ namespace Avalonia.Animation /// public class FloatTransition : Transition { + private static readonly FloatAnimator s_animator = new FloatAnimator(); + /// public override IObservable DoTransition(IObservable progress, float oldValue, float newValue) { - var delta = newValue - oldValue; return progress - .Select(p => (float)Easing.Ease(p) * delta + oldValue); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Animation/Transitions/IntegerTransition.cs b/src/Avalonia.Animation/Transitions/IntegerTransition.cs index 7a85bd75dc..223b2ba531 100644 --- a/src/Avalonia.Animation/Transitions/IntegerTransition.cs +++ b/src/Avalonia.Animation/Transitions/IntegerTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,12 +10,13 @@ namespace Avalonia.Animation /// public class IntegerTransition : Transition { + private static readonly Int32Animator s_animator = new Int32Animator(); + /// public override IObservable DoTransition(IObservable progress, int oldValue, int newValue) { - var delta = newValue - oldValue; return progress - .Select(p => (int)(Easing.Ease(p) * delta + oldValue)); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs index 0b0f04ca94..9ffdf53694 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,26 +10,13 @@ namespace Avalonia.Animation /// public class CornerRadiusTransition : Transition { + private static readonly CornerRadiusAnimator s_animator = new CornerRadiusAnimator(); + /// public override IObservable DoTransition(IObservable progress, CornerRadius oldValue, CornerRadius newValue) { return progress - .Select(p => - { - var f = Easing.Ease(p); - - var deltaTL = newValue.TopLeft - oldValue.TopLeft; - var deltaTR = newValue.TopRight - oldValue.TopRight; - var deltaBR = newValue.BottomRight - oldValue.BottomRight; - var deltaBL = newValue.BottomLeft - oldValue.BottomLeft; - - var nTL = f * deltaTL + oldValue.TopLeft; - var nTR = f * deltaTR + oldValue.TopRight; - var nBR = f * deltaBR + oldValue.BottomRight; - var nBL = f * deltaBL + oldValue.BottomLeft; - - return new CornerRadius(nTL, nTR, nBR, nBL); - }); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs index 29db5fc868..fbe24c6d55 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,15 +10,13 @@ namespace Avalonia.Animation /// public class PointTransition : Transition { + private static readonly PointAnimator s_animator = new PointAnimator(); + /// public override IObservable DoTransition(IObservable progress, Point oldValue, Point newValue) { return progress - .Select(p => - { - var f = Easing.Ease(p); - return ((newValue - oldValue) * f) + oldValue; - }); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs index b40e789915..464f83bec7 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,15 +10,13 @@ namespace Avalonia.Animation /// public class SizeTransition : Transition { + private static readonly SizeAnimator s_animator = new SizeAnimator(); + /// public override IObservable DoTransition(IObservable progress, Size oldValue, Size newValue) { return progress - .Select(p => - { - var f = Easing.Ease(p); - return ((newValue - oldValue) * f) + oldValue; - }); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs index 28d4ea067f..9fb3380780 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,15 +10,13 @@ namespace Avalonia.Animation /// public class ThicknessTransition : Transition { + private static readonly ThicknessAnimator s_animator = new ThicknessAnimator(); + /// public override IObservable DoTransition(IObservable progress, Thickness oldValue, Thickness newValue) { return progress - .Select(p => - { - var f = Easing.Ease(p); - return ((newValue - oldValue) * f) + oldValue; - }); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } diff --git a/src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs index c073e8e192..5038117faa 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; +using Avalonia.Animation.Animators; + namespace Avalonia.Animation { /// @@ -8,15 +10,13 @@ namespace Avalonia.Animation /// public class VectorTransition : Transition { + private static readonly VectorAnimator s_animator = new VectorAnimator(); + /// public override IObservable DoTransition(IObservable progress, Vector oldValue, Vector newValue) { return progress - .Select(p => - { - var f = Easing.Ease(p); - return ((newValue - oldValue) * f) + oldValue; - }); + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); } } } From f29d050e73c0144909757c085bf6bcd5a156f7ae Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 15 May 2021 20:53:45 -0400 Subject: [PATCH 22/26] Add BoxShadows transition --- .../Transitions/BoxShadowsTransition.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs new file mode 100644 index 0000000000..008613fb40 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs @@ -0,0 +1,23 @@ +using System; +using System.Reactive.Linq; + +using Avalonia.Animation.Animators; +using Avalonia.Media; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with type. + /// + public class BoxShadowsTransition : Transition + { + private static readonly BoxShadowsAnimator s_animator = new BoxShadowsAnimator(); + + /// + public override IObservable DoTransition(IObservable progress, BoxShadows oldValue, BoxShadows newValue) + { + return progress + .Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue)); + } + } +} From f5a01afc6de7ac794d9dfb1a16792cf7f6296ee0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 15 May 2021 22:05:01 -0400 Subject: [PATCH 23/26] Update RenderDemo pages --- samples/RenderDemo/Pages/AnimationsPage.xaml | 7 +- samples/RenderDemo/Pages/TransitionsPage.xaml | 65 +++++++++++++++++-- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index 12fb31ea59..21c7d68b5d 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -1,7 +1,8 @@ + x:Class="RenderDemo.Pages.AnimationsPage" + MaxWidth="600"> @@ -167,8 +168,8 @@ - - Hover to activate Transform Keyframe Animations. + + Hover to activate Keyframe Animations.