diff --git a/.editorconfig b/.editorconfig
index 7995062f9f..ff7ac5d69e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -140,6 +140,8 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = suggestion
+# CS0162: Remove unreachable code
+dotnet_diagnostic.CS0162.severity = error
# CA1304: Specify CultureInfo
dotnet_diagnostic.CA1304.severity = warning
# CA1802: Use literals where appropriate
diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml b/samples/ControlCatalog/Pages/CompositionPage.axaml
index 403f45b8eb..602b9b768d 100644
--- a/samples/ControlCatalog/Pages/CompositionPage.axaml
+++ b/samples/ControlCatalog/Pages/CompositionPage.axaml
@@ -2,44 +2,57 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="using:ControlCatalog.Pages"
x:Class="ControlCatalog.Pages.CompositionPage">
-
- Implicit animations
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Resize me
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Resize me
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
index c70675b606..8b12a2d663 100644
--- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
+++ b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
@@ -1,28 +1,39 @@
using System;
using System.Collections.Generic;
+using System.Numerics;
+using System.Threading;
using Avalonia;
+using Avalonia.Animation;
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
+using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Animations;
using Avalonia.VisualTree;
+using Math = System.Math;
namespace ControlCatalog.Pages;
public partial class CompositionPage : UserControl
{
private ImplicitAnimationCollection? _implicitAnimations;
+ private CompositionCustomVisual? _customVisual;
+ private CompositionSolidColorVisual? _solidVisual;
public CompositionPage()
{
AvaloniaXamlLoader.Load(this);
+ AttachAnimatedSolidVisual(this.FindControl("SolidVisualHost")!);
+ AttachCustomVisual(this.FindControl("CustomVisualHost")!);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
this.Get("Items").Items = CreateColorItems();
+
}
private static List CreateColorItems()
@@ -126,6 +137,167 @@ public partial class CompositionPage : UserControl
compositionVisual.ImplicitAnimations = page._implicitAnimations;
}
}
+
+ void AttachAnimatedSolidVisual(Visual v)
+ {
+ void Update()
+ {
+ if(_solidVisual == null)
+ return;
+ _solidVisual.Size = new Vector2((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3);
+ _solidVisual.Offset = new Vector3((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3, 0);
+ }
+ v.AttachedToVisualTree += delegate
+ {
+ var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+ if(compositor == null || _solidVisual?.Compositor == compositor)
+ return;
+ _solidVisual = compositor.CreateSolidColorVisual();
+ ElementComposition.SetElementChildVisual(v, _solidVisual);
+ _solidVisual.Color = Colors.Red;
+ var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation();
+ animation.InsertKeyFrame(0, Colors.Red);
+ animation.InsertKeyFrame(0.5f, Colors.Blue);
+ animation.InsertKeyFrame(1, Colors.Green);
+ animation.Duration = TimeSpan.FromSeconds(5);
+ animation.IterationBehavior = AnimationIterationBehavior.Forever;
+ animation.Direction = PlaybackDirection.Alternate;
+ _solidVisual.StartAnimation("Color", animation);
+
+ _solidVisual.AnchorPoint = new Vector2(0, 0);
+
+ var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
+ scale.Duration = TimeSpan.FromSeconds(5);
+ scale.IterationBehavior = AnimationIterationBehavior.Forever;
+ scale.InsertKeyFrame(0, new Vector3(1, 1, 0));
+ scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0));
+ scale.InsertKeyFrame(1, new Vector3(1, 1, 0));
+
+ _solidVisual.StartAnimation("Scale", scale);
+
+ var center =
+ _solidVisual.Compositor.CreateExpressionAnimation(
+ "Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)");
+ _solidVisual.StartAnimation("CenterPoint", center);
+ Update();
+ };
+ v.PropertyChanged += (_, a) =>
+ {
+ if (a.Property == BoundsProperty)
+ Update();
+ };
+ }
+
+ void AttachCustomVisual(Visual v)
+ {
+ void Update()
+ {
+ if (_customVisual == null)
+ return;
+ var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
+ _customVisual.Size = new Vector2((float)v.Bounds.Width, h);
+ _customVisual.Offset = new Vector3(0, (float)(v.Bounds.Height - h) / 2, 0);
+ }
+ v.AttachedToVisualTree += delegate
+ {
+ var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+ if(compositor == null || _customVisual?.Compositor == compositor)
+ return;
+ _customVisual = compositor.CreateCustomVisual(new CustomVisualHandler());
+ ElementComposition.SetElementChildVisual(v, _customVisual);
+ _customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage);
+ Update();
+ };
+
+ v.PropertyChanged += (_, a) =>
+ {
+ if (a.Property == BoundsProperty)
+ Update();
+ };
+ }
+
+ class CustomVisualHandler : CompositionCustomVisualHandler
+ {
+ private TimeSpan _animationElapsed;
+ private TimeSpan? _lastServerTime;
+ private bool _running;
+
+ public static readonly object StopMessage = new(), StartMessage = new();
+
+ public override void OnRender(ImmediateDrawingContext drawingContext)
+ {
+ if (_running)
+ {
+ if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value);
+ _lastServerTime = CompositionNow;
+ }
+
+ const int cnt = 20;
+ var maxPointSizeX = EffectiveSize.X / (cnt * 1.6);
+ var maxPointSizeY = EffectiveSize.Y / 4;
+ var pointSize = Math.Min(maxPointSizeX, maxPointSizeY);
+ var animationLength = TimeSpan.FromSeconds(4);
+ var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds;
+
+ var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5;
+
+ for (var c = 0; c < cnt; c++)
+ {
+ var stage = (animationStage + (double)c / cnt) % 1;
+ var colorStage =
+ (animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1;
+ var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize;
+ var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2;
+ var opacity = Math.Sin(stage * 3.14);
+
+
+ drawingContext.DrawEllipse(new ImmutableSolidColorBrush(Color.FromArgb(
+ 255,
+ (byte)(255 - 255 * colorStage),
+ (byte)(255 * Math.Abs(0.5 - colorStage) * 2),
+ (byte)(255 * colorStage)
+ ), opacity), null,
+ new Point(posX, posY), pointSize / 2, pointSize / 2);
+ }
+
+ }
+
+ public override void OnMessage(object message)
+ {
+ if (message == StartMessage)
+ {
+ _running = true;
+ _lastServerTime = null;
+ RegisterForNextAnimationFrameUpdate();
+ }
+ else if (message == StopMessage)
+ _running = false;
+ }
+
+ public override void OnAnimationFrameUpdate()
+ {
+ if (_running)
+ {
+ Invalidate();
+ RegisterForNextAnimationFrameUpdate();
+ }
+ }
+ }
+
+ private void ButtonThreadSleep(object? sender, RoutedEventArgs e)
+ {
+ Thread.Sleep(10000);
+ }
+
+ private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e)
+ {
+ _customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage);
+ }
+
+ private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e)
+ {
+ _customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage);
+ }
}
public class CompositionPageColorItem
diff --git a/src/Avalonia.Base/Input/KeyEventArgs.cs b/src/Avalonia.Base/Input/KeyEventArgs.cs
index 39c9766105..35fa549995 100644
--- a/src/Avalonia.Base/Input/KeyEventArgs.cs
+++ b/src/Avalonia.Base/Input/KeyEventArgs.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Input
{
public class KeyEventArgs : RoutedEventArgs
{
- internal KeyEventArgs()
+ public KeyEventArgs()
{
}
diff --git a/src/Avalonia.Base/Input/TextInputEventArgs.cs b/src/Avalonia.Base/Input/TextInputEventArgs.cs
index 787bf1abd3..a027bec0c6 100644
--- a/src/Avalonia.Base/Input/TextInputEventArgs.cs
+++ b/src/Avalonia.Base/Input/TextInputEventArgs.cs
@@ -4,7 +4,7 @@ namespace Avalonia.Input
{
public class TextInputEventArgs : RoutedEventArgs
{
- internal TextInputEventArgs()
+ public TextInputEventArgs()
{
}
diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs
index 8d531e9394..4138f1c891 100644
--- a/src/Avalonia.Base/Media/Brush.cs
+++ b/src/Avalonia.Base/Media/Brush.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Media
/// Describes how an area is painted.
///
[TypeConverter(typeof(BrushConverter))]
- public abstract class Brush : Animatable, IMutableBrush
+ public abstract class Brush : Animatable
{
///
/// Defines the property.
@@ -92,9 +92,6 @@ namespace Avalonia.Media
throw new FormatException($"Invalid brush string: '{s}'.");
}
- ///
- public abstract IBrush ToImmutable();
-
///
/// Marks a property as affecting the brush's visual representation.
///
diff --git a/src/Avalonia.Base/Media/BrushExtensions.cs b/src/Avalonia.Base/Media/BrushExtensions.cs
index 2fc8778a5e..d0208c187a 100644
--- a/src/Avalonia.Base/Media/BrushExtensions.cs
+++ b/src/Avalonia.Base/Media/BrushExtensions.cs
@@ -16,11 +16,11 @@ namespace Avalonia.Media
/// The result of calling if the brush is mutable,
/// otherwise .
///
- public static IBrush ToImmutable(this IBrush brush)
+ public static IImmutableBrush ToImmutable(this IBrush brush)
{
_ = brush ?? throw new ArgumentNullException(nameof(brush));
- return (brush as IMutableBrush)?.ToImmutable() ?? brush;
+ return (brush as IMutableBrush)?.ToImmutable() ?? (IImmutableBrush)brush;
}
///
diff --git a/src/Avalonia.Base/Media/Brushes.cs b/src/Avalonia.Base/Media/Brushes.cs
index 5957775f39..7ecba6df3e 100644
--- a/src/Avalonia.Base/Media/Brushes.cs
+++ b/src/Avalonia.Base/Media/Brushes.cs
@@ -8,706 +8,706 @@ namespace Avalonia.Media
///
/// Gets an colored brush.
///
- public static ISolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
+ public static IImmutableSolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
+ public static IImmutableSolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
+ public static IImmutableSolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
+ public static IImmutableSolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Azure => KnownColor.Azure.ToBrush();
+ public static IImmutableSolidColorBrush Azure => KnownColor.Azure.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Beige => KnownColor.Beige.ToBrush();
+ public static IImmutableSolidColorBrush Beige => KnownColor.Beige.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
+ public static IImmutableSolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Black => KnownColor.Black.ToBrush();
+ public static IImmutableSolidColorBrush Black => KnownColor.Black.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
+ public static IImmutableSolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Blue => KnownColor.Blue.ToBrush();
+ public static IImmutableSolidColorBrush Blue => KnownColor.Blue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
+ public static IImmutableSolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Brown => KnownColor.Brown.ToBrush();
+ public static IImmutableSolidColorBrush Brown => KnownColor.Brown.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
+ public static IImmutableSolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
+ public static IImmutableSolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
+ public static IImmutableSolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
+ public static IImmutableSolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Coral => KnownColor.Coral.ToBrush();
+ public static IImmutableSolidColorBrush Coral => KnownColor.Coral.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
+ public static IImmutableSolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
+ public static IImmutableSolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
+ public static IImmutableSolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
+ public static IImmutableSolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
+ public static IImmutableSolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
+ public static IImmutableSolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
+ public static IImmutableSolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
+ public static IImmutableSolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
+ public static IImmutableSolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
+ public static IImmutableSolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
+ public static IImmutableSolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
+ public static IImmutableSolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
+ public static IImmutableSolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
+ public static IImmutableSolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
+ public static IImmutableSolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
+ public static IImmutableSolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
+ public static IImmutableSolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
+ public static IImmutableSolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
+ public static IImmutableSolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
+ public static IImmutableSolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
+ public static IImmutableSolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
+ public static IImmutableSolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
+ public static IImmutableSolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
+ public static IImmutableSolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
+ public static IImmutableSolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
+ public static IImmutableSolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
+ public static IImmutableSolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
+ public static IImmutableSolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
+ public static IImmutableSolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
+ public static IImmutableSolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
+ public static IImmutableSolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Gold => KnownColor.Gold.ToBrush();
+ public static IImmutableSolidColorBrush Gold => KnownColor.Gold.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
+ public static IImmutableSolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Gray => KnownColor.Gray.ToBrush();
+ public static IImmutableSolidColorBrush Gray => KnownColor.Gray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Green => KnownColor.Green.ToBrush();
+ public static IImmutableSolidColorBrush Green => KnownColor.Green.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
+ public static IImmutableSolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
+ public static IImmutableSolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
+ public static IImmutableSolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
+ public static IImmutableSolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
+ public static IImmutableSolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
+ public static IImmutableSolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
+ public static IImmutableSolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
+ public static IImmutableSolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
+ public static IImmutableSolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
+ public static IImmutableSolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
+ public static IImmutableSolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
+ public static IImmutableSolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
+ public static IImmutableSolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
+ public static IImmutableSolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
+ public static IImmutableSolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
+ public static IImmutableSolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
+ public static IImmutableSolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
+ public static IImmutableSolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
+ public static IImmutableSolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
+ public static IImmutableSolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
+ public static IImmutableSolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
+ public static IImmutableSolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
+ public static IImmutableSolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
+ public static IImmutableSolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Lime => KnownColor.Lime.ToBrush();
+ public static IImmutableSolidColorBrush Lime => KnownColor.Lime.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
+ public static IImmutableSolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Linen => KnownColor.Linen.ToBrush();
+ public static IImmutableSolidColorBrush Linen => KnownColor.Linen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
+ public static IImmutableSolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
+ public static IImmutableSolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
+ public static IImmutableSolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
+ public static IImmutableSolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
+ public static IImmutableSolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
+ public static IImmutableSolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
+ public static IImmutableSolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
+ public static IImmutableSolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
+ public static IImmutableSolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
+ public static IImmutableSolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
+ public static IImmutableSolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
+ public static IImmutableSolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
+ public static IImmutableSolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
+ public static IImmutableSolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
+ public static IImmutableSolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
+ public static IImmutableSolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Navy => KnownColor.Navy.ToBrush();
+ public static IImmutableSolidColorBrush Navy => KnownColor.Navy.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
+ public static IImmutableSolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Olive => KnownColor.Olive.ToBrush();
+ public static IImmutableSolidColorBrush Olive => KnownColor.Olive.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
+ public static IImmutableSolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Orange => KnownColor.Orange.ToBrush();
+ public static IImmutableSolidColorBrush Orange => KnownColor.Orange.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
+ public static IImmutableSolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
+ public static IImmutableSolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
+ public static IImmutableSolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
+ public static IImmutableSolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
+ public static IImmutableSolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
+ public static IImmutableSolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
+ public static IImmutableSolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
+ public static IImmutableSolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Peru => KnownColor.Peru.ToBrush();
+ public static IImmutableSolidColorBrush Peru => KnownColor.Peru.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Pink => KnownColor.Pink.ToBrush();
+ public static IImmutableSolidColorBrush Pink => KnownColor.Pink.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Plum => KnownColor.Plum.ToBrush();
+ public static IImmutableSolidColorBrush Plum => KnownColor.Plum.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
+ public static IImmutableSolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Purple => KnownColor.Purple.ToBrush();
+ public static IImmutableSolidColorBrush Purple => KnownColor.Purple.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Red => KnownColor.Red.ToBrush();
+ public static IImmutableSolidColorBrush Red => KnownColor.Red.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
+ public static IImmutableSolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
+ public static IImmutableSolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
+ public static IImmutableSolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
+ public static IImmutableSolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
+ public static IImmutableSolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
+ public static IImmutableSolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
+ public static IImmutableSolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
+ public static IImmutableSolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Silver => KnownColor.Silver.ToBrush();
+ public static IImmutableSolidColorBrush Silver => KnownColor.Silver.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
+ public static IImmutableSolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
+ public static IImmutableSolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
+ public static IImmutableSolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Snow => KnownColor.Snow.ToBrush();
+ public static IImmutableSolidColorBrush Snow => KnownColor.Snow.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
+ public static IImmutableSolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
+ public static IImmutableSolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Tan => KnownColor.Tan.ToBrush();
+ public static IImmutableSolidColorBrush Tan => KnownColor.Tan.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Teal => KnownColor.Teal.ToBrush();
+ public static IImmutableSolidColorBrush Teal => KnownColor.Teal.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
+ public static IImmutableSolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
+ public static IImmutableSolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
+ public static IImmutableSolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
+ public static IImmutableSolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Violet => KnownColor.Violet.ToBrush();
+ public static IImmutableSolidColorBrush Violet => KnownColor.Violet.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
+ public static IImmutableSolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush White => KnownColor.White.ToBrush();
+ public static IImmutableSolidColorBrush White => KnownColor.White.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
+ public static IImmutableSolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
+ public static IImmutableSolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
///
/// Gets an colored brush.
///
- public static ISolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
+ public static IImmutableSolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
}
}
diff --git a/src/Avalonia.Base/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs
index 4b50019ddc..bce7a5af19 100644
--- a/src/Avalonia.Base/Media/ConicGradientBrush.cs
+++ b/src/Avalonia.Base/Media/ConicGradientBrush.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Media
}
///
- public override IBrush ToImmutable()
+ public override IImmutableBrush ToImmutable()
{
return new ImmutableConicGradientBrush(this);
}
diff --git a/src/Avalonia.Base/Media/DashStyle.cs b/src/Avalonia.Base/Media/DashStyle.cs
index 3a30b2d32f..9c30b6f872 100644
--- a/src/Avalonia.Base/Media/DashStyle.cs
+++ b/src/Avalonia.Base/Media/DashStyle.cs
@@ -17,8 +17,8 @@ namespace Avalonia.Media
///
/// Defines the property.
///
- public static readonly StyledProperty> DashesProperty =
- AvaloniaProperty.Register>(nameof(Dashes));
+ public static readonly StyledProperty?> DashesProperty =
+ AvaloniaProperty.Register?>(nameof(Dashes));
///
/// Defines the property.
@@ -83,7 +83,7 @@ namespace Avalonia.Media
///
/// Gets or sets the length of alternating dashes and gaps.
///
- public AvaloniaList Dashes
+ public AvaloniaList? Dashes
{
get => GetValue(DashesProperty);
set => SetValue(DashesProperty, value);
@@ -98,7 +98,7 @@ namespace Avalonia.Media
set => SetValue(OffsetProperty, value);
}
- IReadOnlyList IDashStyle.Dashes => Dashes;
+ IReadOnlyList? IDashStyle.Dashes => Dashes;
///
/// Raised when the dash style changes.
diff --git a/src/Avalonia.Base/Media/GradientBrush.cs b/src/Avalonia.Base/Media/GradientBrush.cs
index c84413ecbb..83cdaa1694 100644
--- a/src/Avalonia.Base/Media/GradientBrush.cs
+++ b/src/Avalonia.Base/Media/GradientBrush.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Media
///
/// Base class for brushes that draw with a gradient.
///
- public abstract class GradientBrush : Brush, IGradientBrush
+ public abstract class GradientBrush : Brush, IGradientBrush, IMutableBrush
{
///
/// Defines the property.
@@ -92,5 +92,7 @@ namespace Avalonia.Media
{
RaiseInvalidated(EventArgs.Empty);
}
+
+ public abstract IImmutableBrush ToImmutable();
}
}
diff --git a/src/Avalonia.Base/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs
index 7208216603..b988ad210a 100644
--- a/src/Avalonia.Base/Media/IDashStyle.cs
+++ b/src/Avalonia.Base/Media/IDashStyle.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Media
///
/// Gets or sets the length of alternating dashes and gaps.
///
- IReadOnlyList Dashes { get; }
+ IReadOnlyList? Dashes { get; }
///
/// Gets or sets how far in the dash sequence the stroke will start.
diff --git a/src/Avalonia.Base/Media/IImmutableBrush.cs b/src/Avalonia.Base/Media/IImmutableBrush.cs
new file mode 100644
index 0000000000..5781b0117a
--- /dev/null
+++ b/src/Avalonia.Base/Media/IImmutableBrush.cs
@@ -0,0 +1,9 @@
+namespace Avalonia.Media;
+
+///
+/// Represents an immutable brush which can be safely used with various threading contexts
+///
+public interface IImmutableBrush : IBrush
+{
+
+}
\ No newline at end of file
diff --git a/src/Avalonia.Base/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs
index fef124ba36..f128aba4df 100644
--- a/src/Avalonia.Base/Media/IMutableBrush.cs
+++ b/src/Avalonia.Base/Media/IMutableBrush.cs
@@ -7,12 +7,12 @@ namespace Avalonia.Media
/// Represents a mutable brush which can return an immutable clone of itself.
///
[NotClientImplementable]
- public interface IMutableBrush : IBrush, IAffectsRender
+ internal interface IMutableBrush : IBrush, IAffectsRender
{
///
/// Creates an immutable clone of the brush.
///
/// The immutable clone.
- IBrush ToImmutable();
+ internal IImmutableBrush ToImmutable();
}
}
diff --git a/src/Avalonia.Base/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs
index 29e11210f1..f58768ef09 100644
--- a/src/Avalonia.Base/Media/ISolidColorBrush.cs
+++ b/src/Avalonia.Base/Media/ISolidColorBrush.cs
@@ -13,4 +13,13 @@ namespace Avalonia.Media
///
Color Color { get; }
}
+
+ ///
+ /// Fills an area with a solid color.
+ ///
+ [NotClientImplementable]
+ public interface IImmutableSolidColorBrush : ISolidColorBrush, IImmutableBrush
+ {
+
+ }
}
diff --git a/src/Avalonia.Base/Media/ImageBrush.cs b/src/Avalonia.Base/Media/ImageBrush.cs
index 19dcf00901..2f2a0fb627 100644
--- a/src/Avalonia.Base/Media/ImageBrush.cs
+++ b/src/Avalonia.Base/Media/ImageBrush.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Media
///
/// Paints an area with an .
///
- public class ImageBrush : TileBrush, IImageBrush
+ public class ImageBrush : TileBrush, IImageBrush, IMutableBrush
{
///
/// Defines the property.
@@ -45,7 +45,7 @@ namespace Avalonia.Media
}
///
- public override IBrush ToImmutable()
+ public IImmutableBrush ToImmutable()
{
return new ImmutableImageBrush(this);
}
diff --git a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
new file mode 100644
index 0000000000..1e1a73437d
--- /dev/null
+++ b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
@@ -0,0 +1,373 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Platform;
+using Avalonia.Rendering.SceneGraph;
+using Avalonia.Threading;
+using Avalonia.Utilities;
+using Avalonia.Media.Imaging;
+using Avalonia.Media.Immutable;
+
+namespace Avalonia.Media
+{
+ public sealed class ImmediateDrawingContext : IDisposable, IOptionalFeatureProvider
+ {
+ private readonly bool _ownsImpl;
+ private int _currentLevel;
+
+ private static ThreadSafeObjectPool> StateStackPool { get; } =
+ ThreadSafeObjectPool>.Default;
+
+ private static ThreadSafeObjectPool> TransformStackPool { get; } =
+ ThreadSafeObjectPool>.Default;
+
+ private Stack? _states = StateStackPool.Get();
+
+ private Stack? _transformContainers = TransformStackPool.Get();
+
+ readonly struct TransformContainer
+ {
+ public readonly Matrix LocalTransform;
+ public readonly Matrix ContainerTransform;
+
+ public TransformContainer(Matrix localTransform, Matrix containerTransform)
+ {
+ LocalTransform = localTransform;
+ ContainerTransform = containerTransform;
+ }
+ }
+
+ internal ImmediateDrawingContext(IDrawingContextImpl impl, bool ownsImpl)
+ {
+ _ownsImpl = ownsImpl;
+ PlatformImpl = impl;
+ _currentContainerTransform = impl.Transform;
+ }
+
+ public IDrawingContextImpl PlatformImpl { get; }
+
+ private Matrix _currentTransform = Matrix.Identity;
+
+ private Matrix _currentContainerTransform;
+
+ ///
+ /// Gets the current transform of the drawing context.
+ ///
+ public Matrix CurrentTransform
+ {
+ get { return _currentTransform; }
+ private set
+ {
+ _currentTransform = value;
+ var transform = _currentTransform * _currentContainerTransform;
+ PlatformImpl.Transform = transform;
+ }
+ }
+
+ ///
+ /// Draws an bitmap.
+ ///
+ /// The bitmap.
+ /// The rect in the output to draw to.
+ public void DrawBitmap(IBitmap source, Rect rect)
+ {
+ _ = source ?? throw new ArgumentNullException(nameof(source));
+ DrawBitmap(source, new Rect(source.Size), rect);
+ }
+
+ ///
+ /// Draws an image.
+ ///
+ /// The bitmap.
+ /// The rect in the image to draw.
+ /// The rect in the output to draw to.
+ /// The bitmap interpolation mode.
+ public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
+ {
+ _ = source ?? throw new ArgumentNullException(nameof(source));
+ PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect, bitmapInterpolationMode);
+ }
+
+ ///
+ /// Draws a line.
+ ///
+ /// The stroke pen.
+ /// The first point of the line.
+ /// The second point of the line.
+ public void DrawLine(ImmutablePen pen, Point p1, Point p2)
+ {
+ if (PenIsVisible(pen))
+ {
+ PlatformImpl.DrawLine(pen, p1, p2);
+ }
+ }
+
+ ///
+ /// Draws a rectangle with the specified Brush and Pen.
+ ///
+ /// The brush used to fill the rectangle, or null for no fill.
+ /// The pen used to stroke the rectangle, or null for no stroke.
+ /// The rectangle bounds.
+ /// The radius in the X dimension of the rounded corners.
+ /// This value will be clamped to the range of 0 to Width/2
+ ///
+ /// The radius in the Y dimension of the rounded corners.
+ /// This value will be clamped to the range of 0 to Height/2
+ ///
+ /// Box shadow effect parameters
+ ///
+ /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
+ /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
+ ///
+ public void DrawRectangle(IImmutableBrush? brush, ImmutablePen? pen, Rect rect, double radiusX = 0, double radiusY = 0,
+ BoxShadows boxShadows = default)
+ {
+ if (brush == null && !PenIsVisible(pen))
+ {
+ return;
+ }
+
+ if (!MathUtilities.IsZero(radiusX))
+ {
+ radiusX = Math.Min(radiusX, rect.Width / 2);
+ }
+
+ if (!MathUtilities.IsZero(radiusY))
+ {
+ radiusY = Math.Min(radiusY, rect.Height / 2);
+ }
+
+ PlatformImpl.DrawRectangle(brush, pen, new RoundedRect(rect, radiusX, radiusY), boxShadows);
+ }
+
+ ///
+ /// Draws the outline of a rectangle.
+ ///
+ /// The pen.
+ /// The rectangle bounds.
+ /// The corner radius.
+ public void DrawRectangle(ImmutablePen pen, Rect rect, float cornerRadius = 0.0f)
+ {
+ DrawRectangle(null, pen, rect, cornerRadius, cornerRadius);
+ }
+
+ ///
+ /// Draws an ellipse with the specified Brush and Pen.
+ ///
+ /// The brush used to fill the ellipse, or null for no fill.
+ /// The pen used to stroke the ellipse, or null for no stroke.
+ /// The location of the center of the ellipse.
+ /// The horizontal radius of the ellipse.
+ /// The vertical radius of the ellipse.
+ ///
+ /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
+ /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
+ ///
+ public void DrawEllipse(IImmutableBrush? brush, ImmutablePen? pen, Point center, double radiusX, double radiusY)
+ {
+ if (brush == null && !PenIsVisible(pen))
+ {
+ return;
+ }
+
+ var originX = center.X - radiusX;
+ var originY = center.Y - radiusY;
+ var width = radiusX * 2;
+ var height = radiusY * 2;
+
+ PlatformImpl.DrawEllipse(brush, pen, new Rect(originX, originY, width, height));
+ }
+
+ ///
+ /// Draws a glyph run.
+ ///
+ /// The foreground brush.
+ /// The glyph run.
+ public void DrawGlyphRun(IImmutableBrush foreground, GlyphRun glyphRun)
+ {
+ _ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
+
+ PlatformImpl.DrawGlyphRun(foreground, glyphRun);
+ }
+
+ ///
+ /// Draws a filled rectangle.
+ ///
+ /// The brush.
+ /// The rectangle bounds.
+ /// The corner radius.
+ public void FillRectangle(IImmutableBrush brush, Rect rect, float cornerRadius = 0.0f)
+ {
+ DrawRectangle(brush, null, rect, cornerRadius, cornerRadius);
+ }
+
+ public readonly record struct PushedState : IDisposable
+ {
+ private readonly int _level;
+ private readonly ImmediateDrawingContext _context;
+ private readonly Matrix _matrix;
+ private readonly PushedStateType _type;
+
+ public enum PushedStateType
+ {
+ None,
+ Matrix,
+ Opacity,
+ Clip,
+ MatrixContainer,
+ GeometryClip,
+ OpacityMask,
+ }
+
+ internal PushedState(ImmediateDrawingContext context, PushedStateType type, Matrix matrix = default(Matrix))
+ {
+ if (context._states is null)
+ throw new ObjectDisposedException(nameof(ImmediateDrawingContext));
+
+ _context = context;
+ _type = type;
+ _matrix = matrix;
+ _level = context._currentLevel += 1;
+ context._states.Push(this);
+ }
+
+ public void Dispose()
+ {
+ if (_type == PushedStateType.None)
+ return;
+ if (_context._states is null || _context._transformContainers is null)
+ throw new ObjectDisposedException(nameof(DrawingContext));
+ if (_context._currentLevel != _level)
+ throw new InvalidOperationException("Wrong Push/Pop state order");
+ _context._currentLevel--;
+ _context._states.Pop();
+ if (_type == PushedStateType.Matrix)
+ _context.CurrentTransform = _matrix;
+ else if (_type == PushedStateType.Clip)
+ _context.PlatformImpl.PopClip();
+ else if (_type == PushedStateType.Opacity)
+ _context.PlatformImpl.PopOpacity();
+ else if (_type == PushedStateType.GeometryClip)
+ _context.PlatformImpl.PopGeometryClip();
+ else if (_type == PushedStateType.OpacityMask)
+ _context.PlatformImpl.PopOpacityMask();
+ else if (_type == PushedStateType.MatrixContainer)
+ {
+ var cont = _context._transformContainers.Pop();
+ _context._currentContainerTransform = cont.ContainerTransform;
+ _context.CurrentTransform = cont.LocalTransform;
+ }
+ }
+ }
+
+
+ public PushedState PushClip(RoundedRect clip)
+ {
+ PlatformImpl.PushClip(clip);
+ return new PushedState(this, PushedState.PushedStateType.Clip);
+ }
+
+ ///
+ /// Pushes a clip rectangle.
+ ///
+ /// The clip rectangle.
+ /// A disposable used to undo the clip rectangle.
+ public PushedState PushClip(Rect clip)
+ {
+ PlatformImpl.PushClip(clip);
+ return new PushedState(this, PushedState.PushedStateType.Clip);
+ }
+
+ ///
+ /// Pushes an opacity value.
+ ///
+ /// The opacity.
+ /// A disposable used to undo the opacity.
+ public PushedState PushOpacity(double opacity)
+ //TODO: Eliminate platform-specific push opacity call
+ {
+ PlatformImpl.PushOpacity(opacity);
+ return new PushedState(this, PushedState.PushedStateType.Opacity);
+ }
+
+ ///
+ /// Pushes an opacity mask.
+ ///
+ /// The opacity mask.
+ ///
+ /// The size of the brush's target area. TODO: Are we sure this is needed?
+ ///
+ /// A disposable to undo the opacity mask.
+ public PushedState PushOpacityMask(IImmutableBrush mask, Rect bounds)
+ {
+ PlatformImpl.PushOpacityMask(mask, bounds);
+ return new PushedState(this, PushedState.PushedStateType.OpacityMask);
+ }
+
+ ///
+ /// Pushes a matrix post-transformation.
+ ///
+ /// The matrix
+ /// A disposable used to undo the transformation.
+ public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
+
+ ///
+ /// Pushes a matrix pre-transformation.
+ ///
+ /// The matrix
+ /// A disposable used to undo the transformation.
+ public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
+
+ ///
+ /// Sets the current matrix transformation.
+ ///
+ /// The matrix
+ /// A disposable used to undo the transformation.
+ public PushedState PushSetTransform(Matrix matrix)
+ {
+ var oldMatrix = CurrentTransform;
+ CurrentTransform = matrix;
+
+ return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
+ }
+
+ ///
+ /// Pushes a new transform context.
+ ///
+ /// A disposable used to undo the transformation.
+ public PushedState PushTransformContainer()
+ {
+ if (_transformContainers is null)
+ throw new ObjectDisposedException(nameof(DrawingContext));
+ _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
+ _currentContainerTransform = CurrentTransform * _currentContainerTransform;
+ _currentTransform = Matrix.Identity;
+ return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
+ }
+
+ ///
+ /// Disposes of any resources held by the .
+ ///
+ public void Dispose()
+ {
+ if (_states is null || _transformContainers is null)
+ throw new ObjectDisposedException(nameof(DrawingContext));
+ while (_states.Count != 0)
+ _states.Peek().Dispose();
+ StateStackPool.Return(_states);
+ _states = null;
+ if (_transformContainers.Count != 0)
+ throw new InvalidOperationException("Transform container stack is non-empty");
+ TransformStackPool.Return(_transformContainers);
+ _transformContainers = null;
+ if (_ownsImpl)
+ PlatformImpl.Dispose();
+ }
+
+ private static bool PenIsVisible(IPen? pen)
+ {
+ return pen?.Brush != null && pen.Thickness > 0;
+ }
+
+ public object? TryGetFeature(Type type) => PlatformImpl.GetFeature(type);
+ }
+}
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs
index 82485c13b0..1f53f06955 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Media.Immutable
///
/// The dashes collection.
/// The dash sequence offset.
- public ImmutableDashStyle(IEnumerable dashes, double offset)
+ public ImmutableDashStyle(IEnumerable? dashes, double offset)
{
_dashes = dashes?.ToArray() ?? Array.Empty();
Offset = offset;
@@ -69,7 +69,7 @@ namespace Avalonia.Media.Immutable
return hashCode;
}
- private static bool SequenceEqual(IReadOnlyList left, IReadOnlyList right)
+ private static bool SequenceEqual(IReadOnlyList left, IReadOnlyList? right)
{
if (ReferenceEquals(left, right))
{
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs
index 1e95acbf22..c86d86d20a 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
///
/// A brush that draws with a gradient.
///
- public abstract class ImmutableGradientBrush : IGradientBrush
+ public abstract class ImmutableGradientBrush : IGradientBrush, IImmutableBrush
{
///
/// Initializes a new instance of the class.
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutablePen.cs b/src/Avalonia.Base/Media/Immutable/ImmutablePen.cs
index 8a53eaf7b8..ef4468fccf 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutablePen.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutablePen.cs
@@ -38,15 +38,13 @@ namespace Avalonia.Media.Immutable
/// The line join.
/// The miter limit.
public ImmutablePen(
- IBrush? brush,
+ IImmutableBrush? brush,
double thickness = 1.0,
ImmutableDashStyle? dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0)
{
- Debug.Assert(!(brush is IMutableBrush));
-
Brush = brush;
Thickness = thickness;
LineCap = lineCap;
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs
index 6755dfd236..4e623f02c5 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
///
/// Fills an area with a solid color.
///
- public class ImmutableSolidColorBrush : ISolidColorBrush, IEquatable
+ public class ImmutableSolidColorBrush : IImmutableSolidColorBrush, IEquatable
{
///
/// Initializes a new instance of the class.
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
index 6df27872fc..1ee52365e0 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
///
/// A brush which displays a repeating image.
///
- public abstract class ImmutableTileBrush : ITileBrush
+ public abstract class ImmutableTileBrush : ITileBrush, IImmutableBrush
{
///
/// Initializes a new instance of the class.
diff --git a/src/Avalonia.Base/Media/KnownColors.cs b/src/Avalonia.Base/Media/KnownColors.cs
index ae2fca5a60..64cf1ef2f1 100644
--- a/src/Avalonia.Base/Media/KnownColors.cs
+++ b/src/Avalonia.Base/Media/KnownColors.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Media
private static readonly IReadOnlyDictionary _knownColorNames;
private static readonly IReadOnlyDictionary _knownColors;
#if !BUILDTASK
- private static readonly Dictionary _knownBrushes;
+ private static readonly Dictionary _knownBrushes;
#endif
[GenerateEnumValueDictionary()]
@@ -39,7 +39,7 @@ namespace Avalonia.Media
_knownColors = knownColors;
#if !BUILDTASK
- _knownBrushes = new Dictionary();
+ _knownBrushes = new ();
#endif
}
@@ -72,7 +72,7 @@ namespace Avalonia.Media
}
#if !BUILDTASK
- public static ISolidColorBrush ToBrush(this KnownColor color)
+ public static IImmutableSolidColorBrush ToBrush(this KnownColor color)
{
lock (_knownBrushes)
{
diff --git a/src/Avalonia.Base/Media/LinearGradientBrush.cs b/src/Avalonia.Base/Media/LinearGradientBrush.cs
index a51adf0949..9b9ce5e4b7 100644
--- a/src/Avalonia.Base/Media/LinearGradientBrush.cs
+++ b/src/Avalonia.Base/Media/LinearGradientBrush.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Media
}
///
- public override IBrush ToImmutable()
+ public override IImmutableBrush ToImmutable()
{
return new ImmutableLinearGradientBrush(this);
}
diff --git a/src/Avalonia.Base/Media/RadialGradientBrush.cs b/src/Avalonia.Base/Media/RadialGradientBrush.cs
index 16c367c0d0..f803051676 100644
--- a/src/Avalonia.Base/Media/RadialGradientBrush.cs
+++ b/src/Avalonia.Base/Media/RadialGradientBrush.cs
@@ -67,7 +67,7 @@ namespace Avalonia.Media
}
///
- public override IBrush ToImmutable()
+ public override IImmutableBrush ToImmutable()
{
return new ImmutableRadialGradientBrush(this);
}
diff --git a/src/Avalonia.Base/Media/SolidColorBrush.cs b/src/Avalonia.Base/Media/SolidColorBrush.cs
index 962819a1a1..d8e25fe748 100644
--- a/src/Avalonia.Base/Media/SolidColorBrush.cs
+++ b/src/Avalonia.Base/Media/SolidColorBrush.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Media
///
/// Fills an area with a solid color.
///
- public class SolidColorBrush : Brush, ISolidColorBrush
+ public class SolidColorBrush : Brush, ISolidColorBrush, IMutableBrush
{
///
/// Defines the property.
@@ -80,7 +80,7 @@ namespace Avalonia.Media
}
///
- public override IBrush ToImmutable()
+ public IImmutableBrush ToImmutable()
{
return new ImmutableSolidColorBrush(this);
}
diff --git a/src/Avalonia.Base/Media/VisualBrush.cs b/src/Avalonia.Base/Media/VisualBrush.cs
index 8dce79cf11..1261d233ac 100644
--- a/src/Avalonia.Base/Media/VisualBrush.cs
+++ b/src/Avalonia.Base/Media/VisualBrush.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Media
///
/// Paints an area with an .
///
- public class VisualBrush : TileBrush, IVisualBrush
+ public class VisualBrush : TileBrush, IVisualBrush, IMutableBrush
{
///
/// Defines the property.
@@ -45,7 +45,7 @@ namespace Avalonia.Media
}
///
- public override IBrush ToImmutable()
+ IImmutableBrush IMutableBrush.ToImmutable()
{
return new ImmutableVisualBrush(this);
}
diff --git a/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs b/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
index ef14211902..b5ac5c817d 100644
--- a/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
+++ b/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Reactive.Disposables;
using Avalonia.Data;
+using Avalonia.Threading;
namespace Avalonia.PropertyStore
{
@@ -116,26 +117,42 @@ namespace Avalonia.PropertyStore
private void SetValue(BindingValue value)
{
- if (Frame.Owner is null)
- return;
+ static void Execute(BindingEntryBase instance, BindingValue value)
+ {
+ if (instance.Frame.Owner is null)
+ return;
- LoggingUtils.LogIfNecessary(Frame.Owner.Owner, Property, value);
+ LoggingUtils.LogIfNecessary(instance.Frame.Owner.Owner, instance.Property, value);
- if (value.HasValue)
- {
- if (!_hasValue || !EqualityComparer.Default.Equals(_value, value.Value))
+ if (value.HasValue)
+ {
+ if (!instance._hasValue || !EqualityComparer.Default.Equals(instance._value, value.Value))
+ {
+ instance._value = value.Value;
+ instance._hasValue = true;
+ if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
+ instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority);
+ }
+ }
+ else if (value.Type != BindingValueType.DoNothing)
{
- _value = value.Value;
- _hasValue = true;
- if (_subscription is not null && _subscription != s_creatingQuiet)
- Frame.Owner?.OnBindingValueChanged(this, Frame.Priority);
+ instance.ClearValue();
+ if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
+ instance.Frame.Owner?.OnBindingValueCleared(instance.Property, instance.Frame.Priority);
}
}
- else if (value.Type != BindingValueType.DoNothing)
+
+ if (Dispatcher.UIThread.CheckAccess())
{
- ClearValue();
- if (_subscription is not null && _subscription != s_creatingQuiet)
- Frame.Owner?.OnBindingValueCleared(Property, Frame.Priority);
+ Execute(this, value);
+ }
+ else
+ {
+ // To avoid allocating closure in the outer scope we need to capture variables
+ // locally. This allows us to skip most of the allocations when on UI thread.
+ var instance = this;
+ var newValue = value;
+ Dispatcher.UIThread.Post(() => Execute(instance, newValue));
}
}
diff --git a/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs b/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
index 4dca6c0100..8acb885604 100644
--- a/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
+++ b/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Data;
+using Avalonia.Threading;
namespace Avalonia.PropertyStore
{
@@ -40,20 +41,56 @@ namespace Avalonia.PropertyStore
public void OnNext(T value)
{
- if (Property.ValidateValue?.Invoke(value) != false)
- _owner.SetValue(Property, value, BindingPriority.LocalValue);
+ static void Execute(ValueStore owner, StyledPropertyBase property, T value)
+ {
+ if (property.ValidateValue?.Invoke(value) != false)
+ owner.SetValue(property, value, BindingPriority.LocalValue);
+ else
+ owner.ClearLocalValue(property);
+ }
+
+ if (Dispatcher.UIThread.CheckAccess())
+ {
+ Execute(_owner, Property, value);
+ }
else
- _owner.ClearLocalValue(Property);
+ {
+ // To avoid allocating closure in the outer scope we need to capture variables
+ // locally. This allows us to skip most of the allocations when on UI thread.
+ var instance = _owner;
+ var property = Property;
+ var newValue = value;
+ Dispatcher.UIThread.Post(() => Execute(instance, property, newValue));
+ }
}
public void OnNext(BindingValue value)
{
- LoggingUtils.LogIfNecessary(_owner.Owner, Property, value);
+ static void Execute(LocalValueBindingObserver instance, BindingValue value)
+ {
+ var owner = instance._owner;
+ var property = instance.Property;
+
+ LoggingUtils.LogIfNecessary(owner.Owner, property, value);
- if (value.HasValue)
- _owner.SetValue(Property, value.Value, BindingPriority.LocalValue);
- else if (value.Type != BindingValueType.DataValidationError)
- _owner.ClearLocalValue(Property);
+ if (value.HasValue)
+ owner.SetValue(property, value.Value, BindingPriority.LocalValue);
+ else if (value.Type != BindingValueType.DataValidationError)
+ owner.ClearLocalValue(property);
+ }
+
+ if (Dispatcher.UIThread.CheckAccess())
+ {
+ Execute(this, value);
+ }
+ else
+ {
+ // To avoid allocating closure in the outer scope we need to capture variables
+ // locally. This allows us to skip most of the allocations when on UI thread.
+ var instance = this;
+ var newValue = value;
+ Dispatcher.UIThread.Post(() => Execute(instance, newValue));
+ }
}
}
}
diff --git a/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs b/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
index 099e997d38..7c529591b6 100644
--- a/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
+++ b/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
@@ -1,5 +1,7 @@
using System;
+using System.Security.Cryptography;
using Avalonia.Data;
+using Avalonia.Threading;
namespace Avalonia.PropertyStore
{
@@ -34,28 +36,47 @@ namespace Avalonia.PropertyStore
public void OnNext(object? value)
{
- if (value is BindingNotification n)
+ static void Execute(LocalValueUntypedBindingObserver instance, object? value)
{
- value = n.Value;
- LoggingUtils.LogIfNecessary(_owner.Owner, Property, n);
- }
+ var owner = instance._owner;
+ var property = instance.Property;
- if (value == AvaloniaProperty.UnsetValue)
- {
- _owner.ClearLocalValue(Property);
- }
- else if (value == BindingOperations.DoNothing)
- {
- // Do nothing!
+ if (value is BindingNotification n)
+ {
+ value = n.Value;
+ LoggingUtils.LogIfNecessary(owner.Owner, property, n);
+ }
+
+ if (value == AvaloniaProperty.UnsetValue)
+ {
+ owner.ClearLocalValue(property);
+ }
+ else if (value == BindingOperations.DoNothing)
+ {
+ // Do nothing!
+ }
+ else if (UntypedValueUtils.TryConvertAndValidate(property, value, out var typedValue))
+ {
+ owner.SetValue(property, typedValue, BindingPriority.LocalValue);
+ }
+ else
+ {
+ owner.ClearLocalValue(property);
+ LoggingUtils.LogInvalidValue(owner.Owner, property, typeof(T), value);
+ }
}
- else if (UntypedValueUtils.TryConvertAndValidate(Property, value, out var typedValue))
+
+ if (Dispatcher.UIThread.CheckAccess())
{
- _owner.SetValue(Property, typedValue, BindingPriority.LocalValue);
+ Execute(this, value);
}
- else
+ else if (value != BindingOperations.DoNothing)
{
- _owner.ClearLocalValue(Property);
- LoggingUtils.LogInvalidValue(_owner.Owner, Property, typeof(T), value);
+ // To avoid allocating closure in the outer scope we need to capture variables
+ // locally. This allows us to skip most of the allocations when on UI thread.
+ var instance = this;
+ var newValue = value;
+ Dispatcher.UIThread.Post(() => Execute(instance, newValue));
}
}
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs b/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
index 889c1d34c8..80b6bb9d2a 100644
--- a/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
@@ -80,4 +80,6 @@ internal abstract class AnimationInstanceBase : IAnimationInstance
_invalidated = true;
TargetObject.NotifyAnimatedValueChanged(Property);
}
+
+ public void OnTick() => Invalidate();
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
index 8ec4ec19bc..b14d2c0b19 100644
--- a/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
@@ -6,7 +6,7 @@ using Avalonia.Rendering.Composition.Server;
namespace Avalonia.Rendering.Composition.Animations
{
- internal interface IAnimationInstance
+ internal interface IAnimationInstance : IServerClockItem
{
ServerObject TargetObject { get; }
ExpressionVariant Evaluate(TimeSpan now, ExpressionVariant currentValue);
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs
new file mode 100644
index 0000000000..f816231781
--- /dev/null
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using System.Numerics;
+using Avalonia.Rendering.Composition.Server;
+using Avalonia.Rendering.Composition.Transport;
+
+namespace Avalonia.Rendering.Composition;
+
+public class CompositionCustomVisual : CompositionContainerVisual
+{
+ private List
- Func Closing { get; set; }
+ Func Closing { get; set; }
///
/// Gets a value to indicate if the platform was able to extend client area to non-client area.
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 798caf0b15..480a8ee7a7 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -109,7 +109,7 @@ namespace Avalonia.Controls.Presenters
static TextPresenter()
{
- AffectsRender(CaretBrushProperty, SelectionBrushProperty);
+ AffectsRender(CaretBrushProperty, SelectionBrushProperty, TextElement.ForegroundProperty);
}
public TextPresenter()
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index b0911403cc..88f3b520ce 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -430,14 +430,14 @@ namespace Avalonia.Controls
///
/// Fired before a window is closed.
///
- public event EventHandler? Closing;
+ public event EventHandler? Closing;
///
/// Closes the window.
///
public void Close()
{
- Close(false);
+ CloseCore(WindowCloseReason.WindowClosing, true);
}
///
@@ -453,16 +453,16 @@ namespace Avalonia.Controls
public void Close(object dialogResult)
{
_dialogResult = dialogResult;
- Close(false);
+ CloseCore(WindowCloseReason.WindowClosing, true);
}
- internal void Close(bool ignoreCancel)
+ internal void CloseCore(WindowCloseReason reason, bool isProgrammatic)
{
bool close = true;
try
{
- if (!ignoreCancel && ShouldCancelClose())
+ if (ShouldCancelClose(new WindowClosingEventArgs(reason, isProgrammatic)))
{
close = false;
}
@@ -480,9 +480,10 @@ namespace Avalonia.Controls
/// Handles a closing notification from .
/// true if closing is cancelled. Otherwise false.
///
- protected virtual bool HandleClosing()
+ /// The reason the window is closing.
+ private protected virtual bool HandleClosing(WindowCloseReason reason)
{
- if (!ShouldCancelClose())
+ if (!ShouldCancelClose(new WindowClosingEventArgs(reason, false)))
{
CloseInternal();
return false;
@@ -510,20 +511,22 @@ namespace Avalonia.Controls
_showingAsDialog = false;
}
- private bool ShouldCancelClose(CancelEventArgs? args = null)
+ private bool ShouldCancelClose(WindowClosingEventArgs args)
{
- if (args is null)
- {
- args = new CancelEventArgs();
- }
-
bool canClose = true;
- foreach (var (child, _) in _children.ToArray())
+ if (_children.Count > 0)
{
- if (child.ShouldCancelClose(args))
+ var childArgs = args.CloseReason == WindowCloseReason.WindowClosing ?
+ new WindowClosingEventArgs(WindowCloseReason.OwnerWindowClosing, args.IsProgrammatic) :
+ args;
+
+ foreach (var (child, _) in _children.ToArray())
{
- canClose = false;
+ if (child.ShouldCancelClose(childArgs))
+ {
+ canClose = false;
+ }
}
}
@@ -1033,7 +1036,7 @@ namespace Avalonia.Controls
/// overridden method must call on the base class if the
/// event needs to be raised.
///
- protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e);
+ protected virtual void OnClosing(WindowClosingEventArgs e) => Closing?.Invoke(this, e);
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
diff --git a/src/Avalonia.Controls/WindowClosingEventArgs.cs b/src/Avalonia.Controls/WindowClosingEventArgs.cs
new file mode 100644
index 0000000000..7cfae2d005
--- /dev/null
+++ b/src/Avalonia.Controls/WindowClosingEventArgs.cs
@@ -0,0 +1,57 @@
+using System.ComponentModel;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Specifies the reason that a window was closed.
+ ///
+ public enum WindowCloseReason
+ {
+ ///
+ /// The cause of the closure was not provided by the underlying platform.
+ ///
+ Undefined,
+
+ ///
+ /// The window itself was requested to close.
+ ///
+ WindowClosing,
+
+ ///
+ /// The window is closing due to a parent/owner window closing.
+ ///
+ OwnerWindowClosing,
+
+ ///
+ /// The window is closing due to the application shutting down.
+ ///
+ ApplicationShutdown,
+
+ ///
+ /// The window is closing due to the operating system shutting down.
+ ///
+ OSShutdown,
+ }
+
+ ///
+ /// Provides data for the event.
+ ///
+ public class WindowClosingEventArgs : CancelEventArgs
+ {
+ internal WindowClosingEventArgs(WindowCloseReason reason, bool isProgrammatic)
+ {
+ CloseReason = reason;
+ IsProgrammatic = isProgrammatic;
+ }
+
+ ///
+ /// Gets a value that indicates why the window is being closed.
+ ///
+ public WindowCloseReason CloseReason { get; }
+
+ ///
+ /// Gets a value indicating whether the window is being closed programmatically.
+ ///
+ public bool IsProgrammatic { get; }
+ }
+}
diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
index 037c5e0c71..020e09526e 100644
--- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
+++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
@@ -42,7 +42,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; }
public Action WindowStateChanged { get; set; }
diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
index 80a4c7d897..94679e8ade 100644
--- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs
+++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
@@ -32,7 +32,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action Paint { get; set; }
public Action Resized { get; set; }
public Action ScalingChanged { get; set; }
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public Action Closed { get; set; }
public Action LostFocus { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();
diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs
index 725fab1eaa..8eafce208b 100644
--- a/src/Avalonia.Headless/HeadlessWindowImpl.cs
+++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs
@@ -175,7 +175,7 @@ namespace Avalonia.Headless
}
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
class FramebufferProxy : ILockedFramebuffer
{
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index 2201503168..880a385744 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/src/Avalonia.Native/WindowImpl.cs
@@ -48,7 +48,7 @@ namespace Avalonia.Native
{
if (_parent.Closing != null)
{
- return _parent.Closing().AsComBool();
+ return _parent.Closing(WindowCloseReason.WindowClosing).AsComBool();
}
return true.AsComBool();
@@ -207,7 +207,7 @@ namespace Avalonia.Native
// NO OP on OSX
}
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public ITopLevelNativeMenuExporter NativeMenuExporter { get; }
diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
index 5c48c297b8..d5c95ab46c 100644
--- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
@@ -9,17 +9,16 @@
IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
-
-
diff --git a/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
index ba1b35e2ee..945622f22f 100644
--- a/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
@@ -9,17 +9,16 @@
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 890b49344c..810a806c8a 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -358,7 +358,7 @@ namespace Avalonia.X11
public Action ScalingChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public Action WindowStateChanged { get; set; }
public Action TransparencyLevelChanged
@@ -546,7 +546,7 @@ namespace Avalonia.X11
{
if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW)
{
- if (Closing?.Invoke() != true)
+ if (Closing?.Invoke(WindowCloseReason.WindowClosing) != true)
Dispose();
}
else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index 848da1bfb6..a7bec62366 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -1137,11 +1137,14 @@ namespace Avalonia.Skia
if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0)
{
var srcDashes = pen.DashStyle.Dashes;
- var dashesArray = new float[srcDashes.Count];
- for (var i = 0; i < srcDashes.Count; ++i)
+ var count = srcDashes.Count % 2 == 0 ? srcDashes.Count : srcDashes.Count * 2;
+
+ var dashesArray = new float[count];
+
+ for (var i = 0; i < count; ++i)
{
- dashesArray[i] = (float) srcDashes[i] * paint.StrokeWidth;
+ dashesArray[i] = (float) srcDashes[i % srcDashes.Count] * paint.StrokeWidth;
}
var offset = (float)(pen.DashStyle.Offset * pen.Thickness);
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index a7cca5b0f3..f828b156da 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -1409,7 +1409,7 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")]
public static extern bool TranslateMessage(ref MSG lpMsg);
- [DllImport("user32.dll")]
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")]
diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
index 9b4eefd170..9a829aff92 100644
--- a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.ExceptionServices;
using Avalonia.Logging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
@@ -88,8 +87,6 @@ internal class AngleWin32PlatformGraphics : IPlatformGraphics
return null;
}
- return new AngleWin32PlatformGraphics(egl, AngleWin32EglDisplay.CreateSharedD3D11Display(egl));
-
foreach (var api in (options?.AllowedPlatformApis ?? new []
{
AngleOptions.PlatformApi.DirectX11
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
index fe7d881f11..8c362b0c29 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
@@ -10,6 +10,7 @@ using Avalonia.Controls.Remote;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
+using Avalonia.Threading;
using Avalonia.Win32.Automation;
using Avalonia.Win32.Input;
using Avalonia.Win32.Interop.Automation;
@@ -69,7 +70,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_CLOSE:
{
- bool? preventClosing = Closing?.Invoke();
+ bool? preventClosing = Closing?.Invoke(WindowCloseReason.WindowClosing);
if (preventClosing == true)
{
return IntPtr.Zero;
@@ -106,6 +107,9 @@ namespace Avalonia.Win32
_touchDevice?.Dispose();
//Free other resources
Dispose();
+
+ // Schedule cleanup of anything that requires window to be destroyed
+ Dispatcher.UIThread.Post(AfterCloseCleanup);
return IntPtr.Zero;
}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 9e11959101..b2ce68ee83 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -191,7 +191,7 @@ namespace Avalonia.Win32
public Action Activated { get; set; }
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public Action Closed { get; set; }
@@ -643,12 +643,6 @@ namespace Avalonia.Win32
_hwnd = IntPtr.Zero;
}
- if (_className != null)
- {
- UnregisterClass(_className, GetModuleHandle(null));
- _className = null;
- }
-
_framebuffer.Dispose();
}
@@ -1144,6 +1138,15 @@ namespace Avalonia.Win32
}
}
+ private void AfterCloseCleanup()
+ {
+ if (_className != null)
+ {
+ UnregisterClass(_className, GetModuleHandle(null));
+ _className = null;
+ }
+ }
+
private void MaximizeWithoutCoveringTaskbar()
{
IntPtr monitor = MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
index 602e5e4496..0162f53f5e 100644
--- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
+++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
@@ -17,6 +17,9 @@
+
+
+
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
index c42cb0241b..547b2cb176 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
@@ -12,6 +12,7 @@ using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Moq;
+using Nito.AsyncEx;
using Xunit;
#nullable enable
@@ -920,6 +921,120 @@ namespace Avalonia.Base.UnitTests
}
}
+ [Theory]
+ [InlineData(BindingPriority.LocalValue)]
+ [InlineData(BindingPriority.Style)]
+ public void Typed_Bind_Executes_On_UIThread(BindingPriority priority)
+ {
+ AsyncContext.Run(async () =>
+ {
+ var target = new Class1();
+ var source = new Subject();
+ var currentThreadId = Thread.CurrentThread.ManagedThreadId;
+ var raised = 0;
+
+ var threadingInterfaceMock = new Mock();
+ threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
+ .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
+
+ var services = new TestServices(
+ threadingInterface: threadingInterfaceMock.Object);
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
+ ++raised;
+ };
+
+ using (UnitTestApplication.Start(services))
+ {
+ target.Bind(Class1.FooProperty, source, priority);
+
+ await Task.Run(() => source.OnNext("foobar"));
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
+ Assert.Equal(1, raised);
+ }
+ });
+ }
+
+ [Theory]
+ [InlineData(BindingPriority.LocalValue)]
+ [InlineData(BindingPriority.Style)]
+ public void Untyped_Bind_Executes_On_UIThread(BindingPriority priority)
+ {
+ AsyncContext.Run(async () =>
+ {
+ var target = new Class1();
+ var source = new Subject();
+ var currentThreadId = Thread.CurrentThread.ManagedThreadId;
+ var raised = 0;
+
+ var threadingInterfaceMock = new Mock();
+ threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
+ .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
+
+ var services = new TestServices(
+ threadingInterface: threadingInterfaceMock.Object);
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
+ ++raised;
+ };
+
+ using (UnitTestApplication.Start(services))
+ {
+ target.Bind(Class1.FooProperty, source, priority);
+
+ await Task.Run(() => source.OnNext("foobar"));
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
+ Assert.Equal(1, raised);
+ }
+ });
+ }
+
+ [Theory]
+ [InlineData(BindingPriority.LocalValue)]
+ [InlineData(BindingPriority.Style)]
+ public void BindingValue_Bind_Executes_On_UIThread(BindingPriority priority)
+ {
+ AsyncContext.Run(async () =>
+ {
+ var target = new Class1();
+ var source = new Subject>();
+ var currentThreadId = Thread.CurrentThread.ManagedThreadId;
+ var raised = 0;
+
+ var threadingInterfaceMock = new Mock();
+ threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
+ .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
+
+ var services = new TestServices(
+ threadingInterface: threadingInterfaceMock.Object);
+
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
+ ++raised;
+ };
+
+ using (UnitTestApplication.Start(services))
+ {
+ target.Bind(Class1.FooProperty, source, priority);
+
+ await Task.Run(() => source.OnNext("foobar"));
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
+ Assert.Equal(1, raised);
+ }
+ });
+ }
+
[Fact]
public async Task Bind_With_Scheduler_Executes_On_Scheduler()
{
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
index 20172eea88..973090ee92 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
@@ -7,8 +7,10 @@ using System.Threading.Tasks;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Platform;
+using Avalonia.Threading;
using Avalonia.UnitTests;
using Moq;
+using Nito.AsyncEx;
using Xunit;
namespace Avalonia.Base.UnitTests
@@ -519,25 +521,39 @@ namespace Avalonia.Base.UnitTests
}
[Fact]
- public async Task Bind_Executes_On_UIThread()
+ public void Bind_Executes_On_UIThread()
{
- var target = new Class1();
- var source = new Subject();
- var currentThreadId = Thread.CurrentThread.ManagedThreadId;
+ AsyncContext.Run(async () =>
+ {
+ var target = new Class1();
+ var source = new Subject();
+ var currentThreadId = Thread.CurrentThread.ManagedThreadId;
+ var raised = 0;
- var threadingInterfaceMock = new Mock();
- threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
- .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
+ var threadingInterfaceMock = new Mock();
+ threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
+ .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
- var services = new TestServices(
- threadingInterface: threadingInterfaceMock.Object);
+ var services = new TestServices(
+ threadingInterface: threadingInterfaceMock.Object);
- using (UnitTestApplication.Start(services))
- {
- target.Bind(Class1.FooProperty, source);
+ target.PropertyChanged += (s, e) =>
+ {
+ Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
+ ++raised;
+ };
- await Task.Run(() => source.OnNext("foobar"));
- }
+ using (UnitTestApplication.Start(services))
+ {
+ target.Bind(Class1.FooProperty, source);
+
+ await Task.Run(() => source.OnNext("foobar"));
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Equal("foobar", target.Foo);
+ Assert.Equal(1, raised);
+ }
+ });
}
[Fact]
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index 435d0d92ce..9dc0cb95bd 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -155,12 +155,16 @@ namespace Avalonia.Controls.UnitTests
window.Closing += (sender, e) =>
{
+ Assert.Equal(WindowCloseReason.WindowClosing, e.CloseReason);
+ Assert.Equal(programaticClose, e.IsProgrammatic);
count++;
windowClosing = count;
};
child.Closing += (sender, e) =>
{
+ Assert.Equal(WindowCloseReason.OwnerWindowClosing, e.CloseReason);
+ Assert.Equal(programaticClose, e.IsProgrammatic);
count++;
childClosing = count;
};
@@ -186,7 +190,7 @@ namespace Avalonia.Controls.UnitTests
}
else
{
- var cancel = window.PlatformImpl.Closing();
+ var cancel = window.PlatformImpl.Closing(WindowCloseReason.WindowClosing);
Assert.Equal(false, cancel);
}
@@ -248,7 +252,7 @@ namespace Avalonia.Controls.UnitTests
}
else
{
- var cancel = window.PlatformImpl.Closing();
+ var cancel = window.PlatformImpl.Closing(WindowCloseReason.WindowClosing);
Assert.Equal(true, cancel);
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
index d51d6122cd..59a17a8cd1 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
@@ -166,6 +166,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
[Fact]
public void Binding_Method_To_Command_Collected()
{
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
WeakReference MakeRef()
{
var weakVm = new WeakReference(null);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
index 520abee59a..92807b2cb9 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
@@ -1,6 +1,8 @@
using System;
+using System.Runtime.CompilerServices;
using System.Xml;
using Avalonia.Controls;
+using Avalonia.Data;
using Avalonia.Media;
using Avalonia.Styling;
using Xunit;
@@ -9,6 +11,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class MergeResourceIncludeTests
{
+ static MergeResourceIncludeTests()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
+ }
+
[Fact]
public void MergeResourceInclude_Works_With_Single_Resource()
{
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
index d76e51f419..5d6d4a78e4 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using Avalonia.Controls;
+using Avalonia.Data;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Avalonia.Media;
@@ -15,6 +17,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class StyleIncludeTests
{
+ static StyleIncludeTests()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
+ AssetLoader.RegisterResUriParsers();
+ }
+
[Fact]
public void StyleInclude_Is_Built()
{