Browse Source

Merge branch 'master' into master

pull/9674/head
M. Tomecki 3 years ago
committed by GitHub
parent
commit
cc038f8537
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .editorconfig
  2. 89
      samples/ControlCatalog/Pages/CompositionPage.axaml
  3. 172
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  4. 2
      src/Avalonia.Base/Input/KeyEventArgs.cs
  5. 2
      src/Avalonia.Base/Input/TextInputEventArgs.cs
  6. 5
      src/Avalonia.Base/Media/Brush.cs
  7. 4
      src/Avalonia.Base/Media/BrushExtensions.cs
  8. 282
      src/Avalonia.Base/Media/Brushes.cs
  9. 2
      src/Avalonia.Base/Media/ConicGradientBrush.cs
  10. 8
      src/Avalonia.Base/Media/DashStyle.cs
  11. 4
      src/Avalonia.Base/Media/GradientBrush.cs
  12. 2
      src/Avalonia.Base/Media/IDashStyle.cs
  13. 9
      src/Avalonia.Base/Media/IImmutableBrush.cs
  14. 4
      src/Avalonia.Base/Media/IMutableBrush.cs
  15. 9
      src/Avalonia.Base/Media/ISolidColorBrush.cs
  16. 4
      src/Avalonia.Base/Media/ImageBrush.cs
  17. 373
      src/Avalonia.Base/Media/ImmediateDrawingContext.cs
  18. 4
      src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs
  19. 2
      src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs
  20. 4
      src/Avalonia.Base/Media/Immutable/ImmutablePen.cs
  21. 2
      src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs
  22. 2
      src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
  23. 6
      src/Avalonia.Base/Media/KnownColors.cs
  24. 2
      src/Avalonia.Base/Media/LinearGradientBrush.cs
  25. 2
      src/Avalonia.Base/Media/RadialGradientBrush.cs
  26. 4
      src/Avalonia.Base/Media/SolidColorBrush.cs
  27. 4
      src/Avalonia.Base/Media/VisualBrush.cs
  28. 45
      src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
  29. 53
      src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
  30. 53
      src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
  31. 2
      src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
  32. 2
      src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
  33. 37
      src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs
  34. 65
      src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs
  35. 2
      src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
  36. 6
      src/Avalonia.Base/Rendering/Composition/Server/IServerClockItem.cs
  37. 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  38. 16
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  39. 44
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  40. 82
      src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs
  41. 2
      src/Avalonia.Base/Rendering/Composition/VisualCollection.cs
  42. 3
      src/Avalonia.Base/Visual.cs
  43. 7
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  44. 10
      src/Avalonia.Controls.DataGrid/DataGridValueConverter.cs
  45. 14
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  46. 9
      src/Avalonia.Controls/NativeMenuBar.cs
  47. 7
      src/Avalonia.Controls/NativeMenuItemSeparator.cs
  48. 2
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  49. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  50. 37
      src/Avalonia.Controls/Window.cs
  51. 57
      src/Avalonia.Controls/WindowClosingEventArgs.cs
  52. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  53. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  54. 2
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  55. 4
      src/Avalonia.Native/WindowImpl.cs
  56. 17
      src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
  57. 21
      src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
  58. 4
      src/Avalonia.X11/X11Window.cs
  59. 9
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  60. 2
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  61. 3
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
  62. 6
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  63. 17
      src/Windows/Avalonia.Win32/WindowImpl.cs
  64. 3
      tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
  65. 115
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  66. 44
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  67. 8
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  68. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
  69. 7
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
  70. 8
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

2
.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

89
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">
<StackPanel>
<TextBlock Classes="h1">Implicit animations</TextBlock>
<TabControl>
<TabItem Header="Implicit animations">
<StackPanel>
<Grid ColumnDefinitions="*,10,40" Margin="0 0 40 0">
<ItemsControl x:Name="Items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates>
<DataTemplate DataType="pages:CompositionPageColorItem">
<Border
pages:CompositionPage.EnableAnimations="True"
Padding="10" BorderBrush="Gray" BorderThickness="2"
Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
<TextBlock Text="{Binding ColorHexValue}"/>
</Border>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
<GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray"
Background="#e0e0e0" Grid.Column="1"
ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
/>
<Border Grid.Column="2">
<LayoutTransformControl
HorizontalAlignment="Center"
MinWidth="30">
<LayoutTransformControl.LayoutTransform>
<RotateTransform Angle="90"/>
</LayoutTransformControl.LayoutTransform>
<TextBlock>Resize me</TextBlock>
</LayoutTransformControl>
</Border>
<ItemsControl x:Name="Items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates>
<DataTemplate DataType="pages:CompositionPageColorItem">
<Border
pages:CompositionPage.EnableAnimations="True"
Padding="10" BorderBrush="Gray" BorderThickness="2"
Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
<TextBlock Text="{Binding ColorHexValue}"/>
</Border>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
<GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray"
Background="#e0e0e0" Grid.Column="1"
ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
/>
<Border Grid.Column="2">
<LayoutTransformControl HorizontalAlignment="Center" MinWidth="30">
<LayoutTransformControl.LayoutTransform>
<RotateTransform Angle="90"/>
</LayoutTransformControl.LayoutTransform>
<TextBlock>Resize me</TextBlock>
</LayoutTransformControl>
</Border>
</Grid>
</StackPanel>
</StackPanel>
</TabItem>
<TabItem Header="Animation">
<DockPanel>
<Button DockPanel.Dock="Top" Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
<Control x:Name="SolidVisualHost" />
</DockPanel>
</TabItem>
<TabItem Header="Custom">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
<Button Margin="10" Click="ButtonStartCustomVisual">Start</Button>
<Button Margin="10" Click="ButtonStopCustomVisual">Stop</Button>
</StackPanel>
<Control x:Name="CustomVisualHost" />
</DockPanel>
</TabItem>
</TabControl>
</UserControl>

172
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<Control>("SolidVisualHost")!);
AttachCustomVisual(this.FindControl<Control>("CustomVisualHost")!);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
this.Get<ItemsControl>("Items").Items = CreateColorItems();
}
private static List<CompositionPageColorItem> 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

2
src/Avalonia.Base/Input/KeyEventArgs.cs

@ -5,7 +5,7 @@ namespace Avalonia.Input
{
public class KeyEventArgs : RoutedEventArgs
{
internal KeyEventArgs()
public KeyEventArgs()
{
}

2
src/Avalonia.Base/Input/TextInputEventArgs.cs

@ -4,7 +4,7 @@ namespace Avalonia.Input
{
public class TextInputEventArgs : RoutedEventArgs
{
internal TextInputEventArgs()
public TextInputEventArgs()
{
}

5
src/Avalonia.Base/Media/Brush.cs

@ -10,7 +10,7 @@ namespace Avalonia.Media
/// Describes how an area is painted.
/// </summary>
[TypeConverter(typeof(BrushConverter))]
public abstract class Brush : Animatable, IMutableBrush
public abstract class Brush : Animatable
{
/// <summary>
/// Defines the <see cref="Opacity"/> property.
@ -92,9 +92,6 @@ namespace Avalonia.Media
throw new FormatException($"Invalid brush string: '{s}'.");
}
/// <inheritdoc/>
public abstract IBrush ToImmutable();
/// <summary>
/// Marks a property as affecting the brush's visual representation.
/// </summary>

4
src/Avalonia.Base/Media/BrushExtensions.cs

@ -16,11 +16,11 @@ namespace Avalonia.Media
/// The result of calling <see cref="IMutableBrush.ToImmutable"/> if the brush is mutable,
/// otherwise <paramref name="brush"/>.
/// </returns>
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;
}
/// <summary>

282
src/Avalonia.Base/Media/Brushes.cs

@ -8,706 +8,706 @@ namespace Avalonia.Media
/// <summary>
/// Gets an <see cref="Colors.AliceBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
public static IImmutableSolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.AntiqueWhite"/> colored brush.
/// </summary>
public static ISolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
public static IImmutableSolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Aqua"/> colored brush.
/// </summary>
public static ISolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
public static IImmutableSolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Aquamarine"/> colored brush.
/// </summary>
public static ISolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
public static IImmutableSolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Azure"/> colored brush.
/// </summary>
public static ISolidColorBrush Azure => KnownColor.Azure.ToBrush();
public static IImmutableSolidColorBrush Azure => KnownColor.Azure.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Beige"/> colored brush.
/// </summary>
public static ISolidColorBrush Beige => KnownColor.Beige.ToBrush();
public static IImmutableSolidColorBrush Beige => KnownColor.Beige.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Bisque"/> colored brush.
/// </summary>
public static ISolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
public static IImmutableSolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Black"/> colored brush.
/// </summary>
public static ISolidColorBrush Black => KnownColor.Black.ToBrush();
public static IImmutableSolidColorBrush Black => KnownColor.Black.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.BlanchedAlmond"/> colored brush.
/// </summary>
public static ISolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
public static IImmutableSolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Blue"/> colored brush.
/// </summary>
public static ISolidColorBrush Blue => KnownColor.Blue.ToBrush();
public static IImmutableSolidColorBrush Blue => KnownColor.Blue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.BlueViolet"/> colored brush.
/// </summary>
public static ISolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
public static IImmutableSolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Brown"/> colored brush.
/// </summary>
public static ISolidColorBrush Brown => KnownColor.Brown.ToBrush();
public static IImmutableSolidColorBrush Brown => KnownColor.Brown.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.BurlyWood"/> colored brush.
/// </summary>
public static ISolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
public static IImmutableSolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.CadetBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
public static IImmutableSolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Chartreuse"/> colored brush.
/// </summary>
public static ISolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
public static IImmutableSolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Chocolate"/> colored brush.
/// </summary>
public static ISolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
public static IImmutableSolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Coral"/> colored brush.
/// </summary>
public static ISolidColorBrush Coral => KnownColor.Coral.ToBrush();
public static IImmutableSolidColorBrush Coral => KnownColor.Coral.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.CornflowerBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
public static IImmutableSolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Cornsilk"/> colored brush.
/// </summary>
public static ISolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
public static IImmutableSolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Crimson"/> colored brush.
/// </summary>
public static ISolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
public static IImmutableSolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Cyan"/> colored brush.
/// </summary>
public static ISolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
public static IImmutableSolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
public static IImmutableSolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkCyan"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
public static IImmutableSolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkGoldenrod"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
public static IImmutableSolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkGray"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
public static IImmutableSolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
public static IImmutableSolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkKhaki"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
public static IImmutableSolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkMagenta"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
public static IImmutableSolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkOliveGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
public static IImmutableSolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkOrange"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
public static IImmutableSolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkOrchid"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
public static IImmutableSolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkRed"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
public static IImmutableSolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkSalmon"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
public static IImmutableSolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkSeaGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
public static IImmutableSolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkSlateBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
public static IImmutableSolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkSlateGray"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
public static IImmutableSolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkTurquoise"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
public static IImmutableSolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DarkViolet"/> colored brush.
/// </summary>
public static ISolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
public static IImmutableSolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DeepPink"/> colored brush.
/// </summary>
public static ISolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
public static IImmutableSolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DeepSkyBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
public static IImmutableSolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DimGray"/> colored brush.
/// </summary>
public static ISolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
public static IImmutableSolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.DodgerBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
public static IImmutableSolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Firebrick"/> colored brush.
/// </summary>
public static ISolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
public static IImmutableSolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.FloralWhite"/> colored brush.
/// </summary>
public static ISolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
public static IImmutableSolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.ForestGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
public static IImmutableSolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Fuchsia"/> colored brush.
/// </summary>
public static ISolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
public static IImmutableSolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Gainsboro"/> colored brush.
/// </summary>
public static ISolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
public static IImmutableSolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.GhostWhite"/> colored brush.
/// </summary>
public static ISolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
public static IImmutableSolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Gold"/> colored brush.
/// </summary>
public static ISolidColorBrush Gold => KnownColor.Gold.ToBrush();
public static IImmutableSolidColorBrush Gold => KnownColor.Gold.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Goldenrod"/> colored brush.
/// </summary>
public static ISolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
public static IImmutableSolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Gray"/> colored brush.
/// </summary>
public static ISolidColorBrush Gray => KnownColor.Gray.ToBrush();
public static IImmutableSolidColorBrush Gray => KnownColor.Gray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Green"/> colored brush.
/// </summary>
public static ISolidColorBrush Green => KnownColor.Green.ToBrush();
public static IImmutableSolidColorBrush Green => KnownColor.Green.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.GreenYellow"/> colored brush.
/// </summary>
public static ISolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
public static IImmutableSolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Honeydew"/> colored brush.
/// </summary>
public static ISolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
public static IImmutableSolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.HotPink"/> colored brush.
/// </summary>
public static ISolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
public static IImmutableSolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.IndianRed"/> colored brush.
/// </summary>
public static ISolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
public static IImmutableSolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Indigo"/> colored brush.
/// </summary>
public static ISolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
public static IImmutableSolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Ivory"/> colored brush.
/// </summary>
public static ISolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
public static IImmutableSolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Khaki"/> colored brush.
/// </summary>
public static ISolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
public static IImmutableSolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Lavender"/> colored brush.
/// </summary>
public static ISolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
public static IImmutableSolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LavenderBlush"/> colored brush.
/// </summary>
public static ISolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
public static IImmutableSolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LawnGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
public static IImmutableSolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LemonChiffon"/> colored brush.
/// </summary>
public static ISolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
public static IImmutableSolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
public static IImmutableSolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightCoral"/> colored brush.
/// </summary>
public static ISolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
public static IImmutableSolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightCyan"/> colored brush.
/// </summary>
public static ISolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
public static IImmutableSolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightGoldenrodYellow"/> colored brush.
/// </summary>
public static ISolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
public static IImmutableSolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightGray"/> colored brush.
/// </summary>
public static ISolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
public static IImmutableSolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
public static IImmutableSolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightPink"/> colored brush.
/// </summary>
public static ISolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
public static IImmutableSolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightSalmon"/> colored brush.
/// </summary>
public static ISolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
public static IImmutableSolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightSeaGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
public static IImmutableSolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightSkyBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
public static IImmutableSolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightSlateGray"/> colored brush.
/// </summary>
public static ISolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
public static IImmutableSolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightSteelBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
public static IImmutableSolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LightYellow"/> colored brush.
/// </summary>
public static ISolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
public static IImmutableSolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Lime"/> colored brush.
/// </summary>
public static ISolidColorBrush Lime => KnownColor.Lime.ToBrush();
public static IImmutableSolidColorBrush Lime => KnownColor.Lime.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.LimeGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
public static IImmutableSolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Linen"/> colored brush.
/// </summary>
public static ISolidColorBrush Linen => KnownColor.Linen.ToBrush();
public static IImmutableSolidColorBrush Linen => KnownColor.Linen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Magenta"/> colored brush.
/// </summary>
public static ISolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
public static IImmutableSolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Maroon"/> colored brush.
/// </summary>
public static ISolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
public static IImmutableSolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumAquamarine"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
public static IImmutableSolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
public static IImmutableSolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumOrchid"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
public static IImmutableSolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumPurple"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
public static IImmutableSolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumSeaGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
public static IImmutableSolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumSlateBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
public static IImmutableSolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumSpringGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
public static IImmutableSolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumTurquoise"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
public static IImmutableSolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MediumVioletRed"/> colored brush.
/// </summary>
public static ISolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
public static IImmutableSolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MidnightBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
public static IImmutableSolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MintCream"/> colored brush.
/// </summary>
public static ISolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
public static IImmutableSolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.MistyRose"/> colored brush.
/// </summary>
public static ISolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
public static IImmutableSolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Moccasin"/> colored brush.
/// </summary>
public static ISolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
public static IImmutableSolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.NavajoWhite"/> colored brush.
/// </summary>
public static ISolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
public static IImmutableSolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Navy"/> colored brush.
/// </summary>
public static ISolidColorBrush Navy => KnownColor.Navy.ToBrush();
public static IImmutableSolidColorBrush Navy => KnownColor.Navy.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.OldLace"/> colored brush.
/// </summary>
public static ISolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
public static IImmutableSolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Olive"/> colored brush.
/// </summary>
public static ISolidColorBrush Olive => KnownColor.Olive.ToBrush();
public static IImmutableSolidColorBrush Olive => KnownColor.Olive.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.OliveDrab"/> colored brush.
/// </summary>
public static ISolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
public static IImmutableSolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Orange"/> colored brush.
/// </summary>
public static ISolidColorBrush Orange => KnownColor.Orange.ToBrush();
public static IImmutableSolidColorBrush Orange => KnownColor.Orange.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.OrangeRed"/> colored brush.
/// </summary>
public static ISolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
public static IImmutableSolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Orchid"/> colored brush.
/// </summary>
public static ISolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
public static IImmutableSolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PaleGoldenrod"/> colored brush.
/// </summary>
public static ISolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
public static IImmutableSolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PaleGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
public static IImmutableSolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PaleTurquoise"/> colored brush.
/// </summary>
public static ISolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
public static IImmutableSolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PaleVioletRed"/> colored brush.
/// </summary>
public static ISolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
public static IImmutableSolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PapayaWhip"/> colored brush.
/// </summary>
public static ISolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
public static IImmutableSolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PeachPuff"/> colored brush.
/// </summary>
public static ISolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
public static IImmutableSolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Peru"/> colored brush.
/// </summary>
public static ISolidColorBrush Peru => KnownColor.Peru.ToBrush();
public static IImmutableSolidColorBrush Peru => KnownColor.Peru.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Pink"/> colored brush.
/// </summary>
public static ISolidColorBrush Pink => KnownColor.Pink.ToBrush();
public static IImmutableSolidColorBrush Pink => KnownColor.Pink.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Plum"/> colored brush.
/// </summary>
public static ISolidColorBrush Plum => KnownColor.Plum.ToBrush();
public static IImmutableSolidColorBrush Plum => KnownColor.Plum.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.PowderBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
public static IImmutableSolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Purple"/> colored brush.
/// </summary>
public static ISolidColorBrush Purple => KnownColor.Purple.ToBrush();
public static IImmutableSolidColorBrush Purple => KnownColor.Purple.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Red"/> colored brush.
/// </summary>
public static ISolidColorBrush Red => KnownColor.Red.ToBrush();
public static IImmutableSolidColorBrush Red => KnownColor.Red.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.RosyBrown"/> colored brush.
/// </summary>
public static ISolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
public static IImmutableSolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.RoyalBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
public static IImmutableSolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SaddleBrown"/> colored brush.
/// </summary>
public static ISolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
public static IImmutableSolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Salmon"/> colored brush.
/// </summary>
public static ISolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
public static IImmutableSolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SandyBrown"/> colored brush.
/// </summary>
public static ISolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
public static IImmutableSolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SeaGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
public static IImmutableSolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SeaShell"/> colored brush.
/// </summary>
public static ISolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
public static IImmutableSolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Sienna"/> colored brush.
/// </summary>
public static ISolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
public static IImmutableSolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Silver"/> colored brush.
/// </summary>
public static ISolidColorBrush Silver => KnownColor.Silver.ToBrush();
public static IImmutableSolidColorBrush Silver => KnownColor.Silver.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SkyBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
public static IImmutableSolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SlateBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
public static IImmutableSolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SlateGray"/> colored brush.
/// </summary>
public static ISolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
public static IImmutableSolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Snow"/> colored brush.
/// </summary>
public static ISolidColorBrush Snow => KnownColor.Snow.ToBrush();
public static IImmutableSolidColorBrush Snow => KnownColor.Snow.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SpringGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
public static IImmutableSolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.SteelBlue"/> colored brush.
/// </summary>
public static ISolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
public static IImmutableSolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Tan"/> colored brush.
/// </summary>
public static ISolidColorBrush Tan => KnownColor.Tan.ToBrush();
public static IImmutableSolidColorBrush Tan => KnownColor.Tan.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Teal"/> colored brush.
/// </summary>
public static ISolidColorBrush Teal => KnownColor.Teal.ToBrush();
public static IImmutableSolidColorBrush Teal => KnownColor.Teal.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Thistle"/> colored brush.
/// </summary>
public static ISolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
public static IImmutableSolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Tomato"/> colored brush.
/// </summary>
public static ISolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
public static IImmutableSolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Transparent"/> colored brush.
/// </summary>
public static ISolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
public static IImmutableSolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Turquoise"/> colored brush.
/// </summary>
public static ISolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
public static IImmutableSolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Violet"/> colored brush.
/// </summary>
public static ISolidColorBrush Violet => KnownColor.Violet.ToBrush();
public static IImmutableSolidColorBrush Violet => KnownColor.Violet.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Wheat"/> colored brush.
/// </summary>
public static ISolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
public static IImmutableSolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.White"/> colored brush.
/// </summary>
public static ISolidColorBrush White => KnownColor.White.ToBrush();
public static IImmutableSolidColorBrush White => KnownColor.White.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.WhiteSmoke"/> colored brush.
/// </summary>
public static ISolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
public static IImmutableSolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.Yellow"/> colored brush.
/// </summary>
public static ISolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
public static IImmutableSolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
/// <summary>
/// Gets an <see cref="Colors.YellowGreen"/> colored brush.
/// </summary>
public static ISolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
public static IImmutableSolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
}
}

2
src/Avalonia.Base/Media/ConicGradientBrush.cs

@ -47,7 +47,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
public override IBrush ToImmutable()
public override IImmutableBrush ToImmutable()
{
return new ImmutableConicGradientBrush(this);
}

8
src/Avalonia.Base/Media/DashStyle.cs

@ -17,8 +17,8 @@ namespace Avalonia.Media
/// <summary>
/// Defines the <see cref="Dashes"/> property.
/// </summary>
public static readonly StyledProperty<AvaloniaList<double>> DashesProperty =
AvaloniaProperty.Register<DashStyle, AvaloniaList<double>>(nameof(Dashes));
public static readonly StyledProperty<AvaloniaList<double>?> DashesProperty =
AvaloniaProperty.Register<DashStyle, AvaloniaList<double>?>(nameof(Dashes));
/// <summary>
/// Defines the <see cref="Offset"/> property.
@ -83,7 +83,7 @@ namespace Avalonia.Media
/// <summary>
/// Gets or sets the length of alternating dashes and gaps.
/// </summary>
public AvaloniaList<double> Dashes
public AvaloniaList<double>? Dashes
{
get => GetValue(DashesProperty);
set => SetValue(DashesProperty, value);
@ -98,7 +98,7 @@ namespace Avalonia.Media
set => SetValue(OffsetProperty, value);
}
IReadOnlyList<double> IDashStyle.Dashes => Dashes;
IReadOnlyList<double>? IDashStyle.Dashes => Dashes;
/// <summary>
/// Raised when the dash style changes.

4
src/Avalonia.Base/Media/GradientBrush.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media
/// <summary>
/// Base class for brushes that draw with a gradient.
/// </summary>
public abstract class GradientBrush : Brush, IGradientBrush
public abstract class GradientBrush : Brush, IGradientBrush, IMutableBrush
{
/// <summary>
/// Defines the <see cref="SpreadMethod"/> property.
@ -92,5 +92,7 @@ namespace Avalonia.Media
{
RaiseInvalidated(EventArgs.Empty);
}
public abstract IImmutableBrush ToImmutable();
}
}

2
src/Avalonia.Base/Media/IDashStyle.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media
/// <summary>
/// Gets or sets the length of alternating dashes and gaps.
/// </summary>
IReadOnlyList<double> Dashes { get; }
IReadOnlyList<double>? Dashes { get; }
/// <summary>
/// Gets or sets how far in the dash sequence the stroke will start.

9
src/Avalonia.Base/Media/IImmutableBrush.cs

@ -0,0 +1,9 @@
namespace Avalonia.Media;
/// <summary>
/// Represents an immutable brush which can be safely used with various threading contexts
/// </summary>
public interface IImmutableBrush : IBrush
{
}

4
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.
/// </summary>
[NotClientImplementable]
public interface IMutableBrush : IBrush, IAffectsRender
internal interface IMutableBrush : IBrush, IAffectsRender
{
/// <summary>
/// Creates an immutable clone of the brush.
/// </summary>
/// <returns>The immutable clone.</returns>
IBrush ToImmutable();
internal IImmutableBrush ToImmutable();
}
}

9
src/Avalonia.Base/Media/ISolidColorBrush.cs

@ -13,4 +13,13 @@ namespace Avalonia.Media
/// </summary>
Color Color { get; }
}
/// <summary>
/// Fills an area with a solid color.
/// </summary>
[NotClientImplementable]
public interface IImmutableSolidColorBrush : ISolidColorBrush, IImmutableBrush
{
}
}

4
src/Avalonia.Base/Media/ImageBrush.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media
/// <summary>
/// Paints an area with an <see cref="IBitmap"/>.
/// </summary>
public class ImageBrush : TileBrush, IImageBrush
public class ImageBrush : TileBrush, IImageBrush, IMutableBrush
{
/// <summary>
/// Defines the <see cref="Visual"/> property.
@ -45,7 +45,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
public override IBrush ToImmutable()
public IImmutableBrush ToImmutable()
{
return new ImmutableImageBrush(this);
}

373
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<Stack<PushedState>> StateStackPool { get; } =
ThreadSafeObjectPool<Stack<PushedState>>.Default;
private static ThreadSafeObjectPool<Stack<TransformContainer>> TransformStackPool { get; } =
ThreadSafeObjectPool<Stack<TransformContainer>>.Default;
private Stack<PushedState>? _states = StateStackPool.Get();
private Stack<TransformContainer>? _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;
/// <summary>
/// Gets the current transform of the drawing context.
/// </summary>
public Matrix CurrentTransform
{
get { return _currentTransform; }
private set
{
_currentTransform = value;
var transform = _currentTransform * _currentContainerTransform;
PlatformImpl.Transform = transform;
}
}
/// <summary>
/// Draws an bitmap.
/// </summary>
/// <param name="source">The bitmap.</param>
/// <param name="rect">The rect in the output to draw to.</param>
public void DrawBitmap(IBitmap source, Rect rect)
{
_ = source ?? throw new ArgumentNullException(nameof(source));
DrawBitmap(source, new Rect(source.Size), rect);
}
/// <summary>
/// Draws an image.
/// </summary>
/// <param name="source">The bitmap.</param>
/// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param>
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
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);
}
/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pen">The stroke pen.</param>
/// <param name="p1">The first point of the line.</param>
/// <param name="p2">The second point of the line.</param>
public void DrawLine(ImmutablePen pen, Point p1, Point p2)
{
if (PenIsVisible(pen))
{
PlatformImpl.DrawLine(pen, p1, p2);
}
}
/// <summary>
/// Draws a rectangle with the specified Brush and Pen.
/// </summary>
/// <param name="brush">The brush used to fill the rectangle, or <c>null</c> for no fill.</param>
/// <param name="pen">The pen used to stroke the rectangle, or <c>null</c> for no stroke.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="radiusX">The radius in the X dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Width/2
/// </param>
/// <param name="radiusY">The radius in the Y dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Height/2
/// </param>
/// <param name="boxShadows">Box shadow effect parameters</param>
/// <remarks>
/// 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.
/// </remarks>
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);
}
/// <summary>
/// Draws the outline of a rectangle.
/// </summary>
/// <param name="pen">The pen.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
public void DrawRectangle(ImmutablePen pen, Rect rect, float cornerRadius = 0.0f)
{
DrawRectangle(null, pen, rect, cornerRadius, cornerRadius);
}
/// <summary>
/// Draws an ellipse with the specified Brush and Pen.
/// </summary>
/// <param name="brush">The brush used to fill the ellipse, or <c>null</c> for no fill.</param>
/// <param name="pen">The pen used to stroke the ellipse, or <c>null</c> for no stroke.</param>
/// <param name="center">The location of the center of the ellipse.</param>
/// <param name="radiusX">The horizontal radius of the ellipse.</param>
/// <param name="radiusY">The vertical radius of the ellipse.</param>
/// <remarks>
/// 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.
/// </remarks>
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));
}
/// <summary>
/// Draws a glyph run.
/// </summary>
/// <param name="foreground">The foreground brush.</param>
/// <param name="glyphRun">The glyph run.</param>
public void DrawGlyphRun(IImmutableBrush foreground, GlyphRun glyphRun)
{
_ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
PlatformImpl.DrawGlyphRun(foreground, glyphRun);
}
/// <summary>
/// Draws a filled rectangle.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
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);
}
/// <summary>
/// Pushes a clip rectangle.
/// </summary>
/// <param name="clip">The clip rectangle.</param>
/// <returns>A disposable used to undo the clip rectangle.</returns>
public PushedState PushClip(Rect clip)
{
PlatformImpl.PushClip(clip);
return new PushedState(this, PushedState.PushedStateType.Clip);
}
/// <summary>
/// Pushes an opacity value.
/// </summary>
/// <param name="opacity">The opacity.</param>
/// <returns>A disposable used to undo the opacity.</returns>
public PushedState PushOpacity(double opacity)
//TODO: Eliminate platform-specific push opacity call
{
PlatformImpl.PushOpacity(opacity);
return new PushedState(this, PushedState.PushedStateType.Opacity);
}
/// <summary>
/// Pushes an opacity mask.
/// </summary>
/// <param name="mask">The opacity mask.</param>
/// <param name="bounds">
/// The size of the brush's target area. TODO: Are we sure this is needed?
/// </param>
/// <returns>A disposable to undo the opacity mask.</returns>
public PushedState PushOpacityMask(IImmutableBrush mask, Rect bounds)
{
PlatformImpl.PushOpacityMask(mask, bounds);
return new PushedState(this, PushedState.PushedStateType.OpacityMask);
}
/// <summary>
/// Pushes a matrix post-transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
/// <summary>
/// Pushes a matrix pre-transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
/// <summary>
/// Sets the current matrix transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushSetTransform(Matrix matrix)
{
var oldMatrix = CurrentTransform;
CurrentTransform = matrix;
return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
}
/// <summary>
/// Pushes a new transform context.
/// </summary>
/// <returns>A disposable used to undo the transformation.</returns>
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);
}
/// <summary>
/// Disposes of any resources held by the <see cref="DrawingContext"/>.
/// </summary>
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);
}
}

4
src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs

@ -17,7 +17,7 @@ namespace Avalonia.Media.Immutable
/// </summary>
/// <param name="dashes">The dashes collection.</param>
/// <param name="offset">The dash sequence offset.</param>
public ImmutableDashStyle(IEnumerable<double> dashes, double offset)
public ImmutableDashStyle(IEnumerable<double>? dashes, double offset)
{
_dashes = dashes?.ToArray() ?? Array.Empty<double>();
Offset = offset;
@ -69,7 +69,7 @@ namespace Avalonia.Media.Immutable
return hashCode;
}
private static bool SequenceEqual(IReadOnlyList<double> left, IReadOnlyList<double> right)
private static bool SequenceEqual(IReadOnlyList<double> left, IReadOnlyList<double>? right)
{
if (ReferenceEquals(left, right))
{

2
src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs

@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
/// <summary>
/// A brush that draws with a gradient.
/// </summary>
public abstract class ImmutableGradientBrush : IGradientBrush
public abstract class ImmutableGradientBrush : IGradientBrush, IImmutableBrush
{
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class.

4
src/Avalonia.Base/Media/Immutable/ImmutablePen.cs

@ -38,15 +38,13 @@ namespace Avalonia.Media.Immutable
/// <param name="lineJoin">The line join.</param>
/// <param name="miterLimit">The miter limit.</param>
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;

2
src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs

@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
/// <summary>
/// Fills an area with a solid color.
/// </summary>
public class ImmutableSolidColorBrush : ISolidColorBrush, IEquatable<ImmutableSolidColorBrush>
public class ImmutableSolidColorBrush : IImmutableSolidColorBrush, IEquatable<ImmutableSolidColorBrush>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableSolidColorBrush"/> class.

2
src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs

@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
/// <summary>
/// A brush which displays a repeating image.
/// </summary>
public abstract class ImmutableTileBrush : ITileBrush
public abstract class ImmutableTileBrush : ITileBrush, IImmutableBrush
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush"/> class.

6
src/Avalonia.Base/Media/KnownColors.cs

@ -10,7 +10,7 @@ namespace Avalonia.Media
private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames;
private static readonly IReadOnlyDictionary<uint, string> _knownColors;
#if !BUILDTASK
private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes;
private static readonly Dictionary<KnownColor, IImmutableSolidColorBrush> _knownBrushes;
#endif
[GenerateEnumValueDictionary()]
@ -39,7 +39,7 @@ namespace Avalonia.Media
_knownColors = knownColors;
#if !BUILDTASK
_knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>();
_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)
{

2
src/Avalonia.Base/Media/LinearGradientBrush.cs

@ -47,7 +47,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
public override IBrush ToImmutable()
public override IImmutableBrush ToImmutable()
{
return new ImmutableLinearGradientBrush(this);
}

2
src/Avalonia.Base/Media/RadialGradientBrush.cs

@ -67,7 +67,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
public override IBrush ToImmutable()
public override IImmutableBrush ToImmutable()
{
return new ImmutableRadialGradientBrush(this);
}

4
src/Avalonia.Base/Media/SolidColorBrush.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media
/// <summary>
/// Fills an area with a solid color.
/// </summary>
public class SolidColorBrush : Brush, ISolidColorBrush
public class SolidColorBrush : Brush, ISolidColorBrush, IMutableBrush
{
/// <summary>
/// Defines the <see cref="Color"/> property.
@ -80,7 +80,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
public override IBrush ToImmutable()
public IImmutableBrush ToImmutable()
{
return new ImmutableSolidColorBrush(this);
}

4
src/Avalonia.Base/Media/VisualBrush.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media
/// <summary>
/// Paints an area with an <see cref="Visual"/>.
/// </summary>
public class VisualBrush : TileBrush, IVisualBrush
public class VisualBrush : TileBrush, IVisualBrush, IMutableBrush
{
/// <summary>
/// Defines the <see cref="Visual"/> property.
@ -45,7 +45,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
public override IBrush ToImmutable()
IImmutableBrush IMutableBrush.ToImmutable()
{
return new ImmutableVisualBrush(this);
}

45
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<TValue> value)
{
if (Frame.Owner is null)
return;
static void Execute(BindingEntryBase<TValue, TSource> instance, BindingValue<TValue> 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<TValue>.Default.Equals(_value, value.Value))
if (value.HasValue)
{
if (!instance._hasValue || !EqualityComparer<TValue>.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));
}
}

53
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<T> 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<T> value)
{
LoggingUtils.LogIfNecessary(_owner.Owner, Property, value);
static void Execute(LocalValueBindingObserver<T> instance, BindingValue<T> 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));
}
}
}
}

53
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<T> 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));
}
}
}

2
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();
}

2
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);

37
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<object>? _messages;
internal CompositionCustomVisual(Compositor compositor, CompositionCustomVisualHandler handler)
: base(compositor, new ServerCompositionCustomVisual(compositor.Server, handler))
{
}
public void SendHandlerMessage(object message)
{
(_messages ??= new()).Add(message);
RegisterForSerialization();
}
private protected override void SerializeChangesCore(BatchStreamWriter writer)
{
base.SerializeChangesCore(writer);
if (_messages == null || _messages.Count == 0)
writer.Write(0);
else
{
writer.Write(_messages.Count);
foreach (var m in _messages)
writer.WriteObject(m);
_messages.Clear();
}
}
}

65
src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs

@ -0,0 +1,65 @@
using System;
using System.Numerics;
using Avalonia.Media;
using Avalonia.Rendering.Composition.Server;
namespace Avalonia.Rendering.Composition;
public abstract class CompositionCustomVisualHandler
{
private ServerCompositionCustomVisual? _host;
public virtual void OnMessage(object message)
{
}
public virtual void OnAnimationFrameUpdate()
{
}
public abstract void OnRender(ImmediateDrawingContext drawingContext);
void VerifyAccess()
{
if (_host == null)
throw new InvalidOperationException("Object is not yet attached to the compositor");
_host.Compositor.VerifyAccess();
}
protected Vector2 EffectiveSize
{
get
{
VerifyAccess();
return _host!.Size;
}
}
protected TimeSpan CompositionNow
{
get
{
VerifyAccess();
return _host!.Compositor.ServerNow;
}
}
public virtual Rect GetRenderBounds() =>
new(0, 0, EffectiveSize.X, EffectiveSize.Y);
internal void Attach(ServerCompositionCustomVisual visual) => _host = visual;
protected void Invalidate()
{
VerifyAccess();
_host!.HandlerInvalidate();
}
protected void RegisterForNextAnimationFrameUpdate()
{
VerifyAccess();
_host!.HandlerRegisterForNextAnimationFrameUpdate();
}
}

2
src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs

@ -33,4 +33,6 @@ public partial class Compositor
public CompositionSolidColorVisual CreateSolidColorVisual() =>
new(this, new ServerCompositionSolidColorVisual(Server));
public CompositionCustomVisual CreateCustomVisual(CompositionCustomVisualHandler handler) => new(this, handler);
}

6
src/Avalonia.Base/Rendering/Composition/Server/IServerClockItem.cs

@ -0,0 +1,6 @@
namespace Avalonia.Rendering.Composition.Server;
internal interface IServerClockItem
{
void OnTick();
}

2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@ -87,8 +87,6 @@ namespace Avalonia.Rendering.Composition.Server
}
_renderTarget ??= _compositor.CreateRenderTarget(_surfaces());
Compositor.UpdateServerTime();
if(_dirtyRect.IsDefault && !_redrawRequested)
return;

16
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -216,13 +216,29 @@ namespace Avalonia.Rendering.Composition.Server
partial void OnRootChanging()
{
if (Root != null)
{
Root.RemoveVisual(this);
OnDetachedFromRoot(Root);
}
}
protected virtual void OnDetachedFromRoot(ServerCompositionTarget target)
{
}
partial void OnRootChanged()
{
if (Root != null)
{
Root.AddVisual(this);
OnAttachedToRoot(Root);
}
}
protected virtual void OnAttachedToRoot(ServerCompositionTarget target)
{
}
protected override void ValuesInvalidated()

44
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Avalonia.Logging;
using Avalonia.Platform;
using Avalonia.Rendering.Composition.Animations;
@ -26,11 +27,12 @@ namespace Avalonia.Rendering.Composition.Server
public Stopwatch Clock { get; } = Stopwatch.StartNew();
public TimeSpan ServerNow { get; private set; }
private List<ServerCompositionTarget> _activeTargets = new();
private HashSet<IAnimationInstance> _activeAnimations = new();
private List<IAnimationInstance> _animationsToUpdate = new();
private HashSet<IServerClockItem> _clockItems = new();
private List<IServerClockItem> _clockItemsToUpdate = new();
internal BatchStreamObjectPool<object?> BatchObjectPool;
internal BatchStreamMemoryPool BatchMemoryPool;
private object _lock = new object();
private Thread? _safeThread;
public PlatformRenderInterfaceContextManager RenderInterface { get; }
internal static readonly object RenderThreadJobsStartMarker = new();
internal static readonly object RenderThreadJobsEndMarker = new();
@ -129,22 +131,31 @@ namespace Avalonia.Rendering.Composition.Server
{
lock (_lock)
{
RenderCore();
try
{
_safeThread = Thread.CurrentThread;
RenderCore();
}
finally
{
_safeThread = null;
}
}
}
private void RenderCore()
{
UpdateServerTime();
ApplyPendingBatches();
CompletePendingBatches();
foreach(var animation in _activeAnimations)
_animationsToUpdate.Add(animation);
foreach(var animation in _animationsToUpdate)
animation.Invalidate();
foreach(var animation in _clockItems)
_clockItemsToUpdate.Add(animation);
foreach (var animation in _clockItemsToUpdate)
animation.OnTick();
_animationsToUpdate.Clear();
_clockItemsToUpdate.Clear();
try
{
@ -168,16 +179,23 @@ namespace Avalonia.Rendering.Composition.Server
_activeTargets.Remove(target);
}
public void AddToClock(IAnimationInstance animationInstance) =>
_activeAnimations.Add(animationInstance);
public void AddToClock(IServerClockItem item) =>
_clockItems.Add(item);
public void RemoveFromClock(IAnimationInstance animationInstance) =>
_activeAnimations.Remove(animationInstance);
public void RemoveFromClock(IServerClockItem item) =>
_clockItems.Remove(item);
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
using (RenderInterface.EnsureCurrent())
return RenderInterface.CreateRenderTarget(surfaces);
}
public bool CheckAccess() => _safeThread == Thread.CurrentThread;
public void VerifyAccess()
{
if (!CheckAccess())
throw new InvalidOperationException("This object can be only accessed under compositor lock");
}
}
}

82
src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs

@ -0,0 +1,82 @@
using System;
using System.Numerics;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Rendering.Composition.Transport;
namespace Avalonia.Rendering.Composition.Server;
internal class ServerCompositionCustomVisual : ServerCompositionContainerVisual, IServerClockItem
{
private readonly CompositionCustomVisualHandler _handler;
private bool _wantsNextAnimationFrameAfterTick;
internal ServerCompositionCustomVisual(ServerCompositor compositor, CompositionCustomVisualHandler handler) : base(compositor)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
_handler.Attach(this);
}
protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt)
{
base.DeserializeChangesCore(reader, commitedAt);
var count = reader.Read<int>();
for (var c = 0; c < count; c++)
{
try
{
_handler.OnMessage(reader.ReadObject()!);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
?.Log(_handler, $"Exception in {_handler.GetType().Name}.{nameof(CompositionCustomVisualHandler.OnMessage)} {{0}}", e);
}
}
}
public void OnTick()
{
_wantsNextAnimationFrameAfterTick = false;
_handler.OnAnimationFrameUpdate();
if (!_wantsNextAnimationFrameAfterTick)
Compositor.RemoveFromClock(this);
}
public override Rect OwnContentBounds => _handler.GetRenderBounds();
protected override void OnAttachedToRoot(ServerCompositionTarget target)
{
if (_wantsNextAnimationFrameAfterTick)
Compositor.AddToClock(this);
base.OnAttachedToRoot(target);
}
protected override void OnDetachedFromRoot(ServerCompositionTarget target)
{
Compositor.RemoveFromClock(this);
base.OnDetachedFromRoot(target);
}
internal void HandlerInvalidate() => ValuesInvalidated();
internal void HandlerRegisterForNextAnimationFrameUpdate()
{
_wantsNextAnimationFrameAfterTick = true;
if (Root != null)
Compositor.AddToClock(this);
}
protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
{
using var context = new ImmediateDrawingContext(canvas, false);
try
{
_handler.OnRender(context);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
?.Log(_handler, $"Exception in {_handler.GetType().Name}.{nameof(CompositionCustomVisualHandler.OnRender)} {{0}}", e);
}
}
}

2
src/Avalonia.Base/Rendering/Composition/VisualCollection.cs

@ -69,7 +69,7 @@ namespace Avalonia.Rendering.Composition
{
if (item.Parent != null)
throw new InvalidOperationException("Visual already has a parent");
item.Parent = item;
item.Parent = _owner;
}
}
}

3
src/Avalonia.Base/Visual.cs

@ -540,6 +540,9 @@ namespace Avalonia
OnDetachedFromVisualTree(e);
if (CompositionVisual != null)
{
if (ChildCompositionVisual != null)
CompositionVisual.Children.Remove(ChildCompositionVisual);
CompositionVisual.DrawList = null;
CompositionVisual = null;
}

7
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -50,10 +50,13 @@ namespace Avalonia.Controls
InheritsWidth = true;
}
internal DataGrid OwningGrid
/// <summary>
/// Gets the <see cref="T:Avalonia.Controls.DataGrid"/> control that contains this column.
/// </summary>
protected internal DataGrid OwningGrid
{
get;
set;
internal set;
}
internal int Index

10
src/Avalonia.Controls.DataGrid/DataGridValueConverter.cs

@ -22,14 +22,18 @@ namespace Avalonia.Controls
return DefaultValueConverter.Instance.Convert(value, targetType, parameter, culture);
}
// This suppresses a warning saying that we should use String.IsNullOrEmpty instead of a string
// comparison, but in this case we want to explicitly check for Empty and not Null.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != null && targetType.IsNullableType())
{
var strValue = value as string;
if (string.IsNullOrEmpty(strValue))
// This suppresses a warning saying that we should use String.IsNullOrEmpty instead of a string
// comparison, but in this case we want to explicitly check for Empty and not Null.
#pragma warning disable CA1820
if (strValue == string.Empty)
#pragma warning restore CA1820
{
return null;
}

14
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -83,12 +83,12 @@ namespace Avalonia.Controls.ApplicationLifetimes
public void Shutdown(int exitCode = 0)
{
DoShutdown(new ShutdownRequestedEventArgs(), true, exitCode);
DoShutdown(new ShutdownRequestedEventArgs(), true, true, exitCode);
}
public bool TryShutdown(int exitCode = 0)
{
return DoShutdown(new ShutdownRequestedEventArgs(), false, exitCode);
return DoShutdown(new ShutdownRequestedEventArgs(), true, false, exitCode);
}
public int Start(string[] args)
@ -134,7 +134,11 @@ namespace Avalonia.Controls.ApplicationLifetimes
_activeLifetime = null;
}
private bool DoShutdown(ShutdownRequestedEventArgs e, bool force = false, int exitCode = 0)
private bool DoShutdown(
ShutdownRequestedEventArgs e,
bool isProgrammatic,
bool force = false,
int exitCode = 0)
{
if (!force)
{
@ -159,7 +163,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
{
if (w.Owner is null)
{
w.Close();
w.CloseCore(WindowCloseReason.ApplicationShutdown, isProgrammatic);
}
}
@ -183,7 +187,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
return true;
}
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e);
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e, false);
}
public class ClassicDesktopStyleApplicationLifetimeOptions

9
src/Avalonia.Controls/NativeMenuBar.cs

@ -23,15 +23,6 @@ namespace Avalonia.Controls
});
}
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenu))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenuItem))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenuItemBase))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenuItemSeparator))]
public NativeMenuBar()
{
}
public static void SetEnableMenuItemClickForwarding(MenuItem menuItem, bool enable)
{
menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable);

7
src/Avalonia.Controls/NativeMenuItemSeparator.cs

@ -1,7 +1,10 @@
namespace Avalonia.Controls
{
public class NativeMenuItemSeparator : NativeMenuItemBase
public class NativeMenuItemSeparator : NativeMenuItem
{
public NativeMenuItemSeparator()
{
Header = "-";
}
}
}

2
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -68,7 +68,7 @@ namespace Avalonia.Platform
/// Gets or sets a method called before the underlying implementation is destroyed.
/// Return true to prevent the underlying implementation from closing.
/// </summary>
Func<bool> Closing { get; set; }
Func<WindowCloseReason, bool> Closing { get; set; }
/// <summary>
/// Gets a value to indicate if the platform was able to extend client area to non-client area.

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -109,7 +109,7 @@ namespace Avalonia.Controls.Presenters
static TextPresenter()
{
AffectsRender<TextPresenter>(CaretBrushProperty, SelectionBrushProperty);
AffectsRender<TextPresenter>(CaretBrushProperty, SelectionBrushProperty, TextElement.ForegroundProperty);
}
public TextPresenter()

37
src/Avalonia.Controls/Window.cs

@ -430,14 +430,14 @@ namespace Avalonia.Controls
/// <summary>
/// Fired before a window is closed.
/// </summary>
public event EventHandler<CancelEventArgs>? Closing;
public event EventHandler<WindowClosingEventArgs>? Closing;
/// <summary>
/// Closes the window.
/// </summary>
public void Close()
{
Close(false);
CloseCore(WindowCloseReason.WindowClosing, true);
}
/// <summary>
@ -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 <see cref="IWindowImpl.Closing"/>.
/// <returns>true if closing is cancelled. Otherwise false.</returns>
/// </summary>
protected virtual bool HandleClosing()
/// <param name="reason">The reason the window is closing.</param>
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 <see cref="OnClosing"/> on the base class if the
/// <see cref="Closing"/> event needs to be raised.
/// </remarks>
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)
{

57
src/Avalonia.Controls/WindowClosingEventArgs.cs

@ -0,0 +1,57 @@
using System.ComponentModel;
namespace Avalonia.Controls
{
/// <summary>
/// Specifies the reason that a window was closed.
/// </summary>
public enum WindowCloseReason
{
/// <summary>
/// The cause of the closure was not provided by the underlying platform.
/// </summary>
Undefined,
/// <summary>
/// The window itself was requested to close.
/// </summary>
WindowClosing,
/// <summary>
/// The window is closing due to a parent/owner window closing.
/// </summary>
OwnerWindowClosing,
/// <summary>
/// The window is closing due to the application shutting down.
/// </summary>
ApplicationShutdown,
/// <summary>
/// The window is closing due to the operating system shutting down.
/// </summary>
OSShutdown,
}
/// <summary>
/// Provides data for the <see cref="Window.Closing"/> event.
/// </summary>
public class WindowClosingEventArgs : CancelEventArgs
{
internal WindowClosingEventArgs(WindowCloseReason reason, bool isProgrammatic)
{
CloseReason = reason;
IsProgrammatic = isProgrammatic;
}
/// <summary>
/// Gets a value that indicates why the window is being closed.
/// </summary>
public WindowCloseReason CloseReason { get; }
/// <summary>
/// Gets a value indicating whether the window is being closed programmatically.
/// </summary>
public bool IsProgrammatic { get; }
}
}

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -42,7 +42,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
public Func<bool> Closing { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; }
public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -32,7 +32,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Func<bool> Closing { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; }
public Action Closed { get; set; }
public Action LostFocus { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();

2
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -175,7 +175,7 @@ namespace Avalonia.Headless
}
public Func<bool> Closing { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; }
class FramebufferProxy : ILockedFramebuffer
{

4
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<bool> Closing { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; }
public ITopLevelNativeMenuExporter NativeMenuExporter { get; }

17
src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml

@ -9,17 +9,16 @@
IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
<Menu.Styles>
<!-- Don't use x:DataType and compiled bindings here, as it might crash https://github.com/AvaloniaUI/Avalonia/pull/7954 -->
<Style Selector="MenuItem">
<Setter Property="Header" Value="{ReflectionBinding Header}"/>
<Setter Property="IsEnabled" Value="{ReflectionBinding IsEnabled}"/>
<Setter Property="InputGesture" Value="{ReflectionBinding Gesture}"/>
<Setter Property="Items" Value="{ReflectionBinding Menu.Items}"/>
<Setter Property="Command" Value="{ReflectionBinding Command}"/>
<Setter Property="CommandParameter" Value="{ReflectionBinding CommandParameter}"/>
<Style Selector="MenuItem" x:DataType="NativeMenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="InputGesture" Value="{Binding Gesture}"/>
<Setter Property="Items" Value="{Binding Menu.Items}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
<Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True"/>
<!--NativeMenuItem is IBitmap and MenuItem is Image-->
<Setter Property="Icon" Value="{ReflectionBinding Icon , Converter={StaticResource AvaloniaThemesFluentNativeMenuBarIBitmapToImageConverter}}"/>
<Setter Property="Icon" Value="{Binding Icon , Converter={StaticResource AvaloniaThemesFluentNativeMenuBarIBitmapToImageConverter}}"/>
</Style>
</Menu.Styles>
</Menu>

21
src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml

@ -9,17 +9,16 @@
<Menu IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
<Menu.Styles>
<!-- Don't use x:DataType and compiled bindings here, as it might crash https://github.com/AvaloniaUI/Avalonia/pull/7954 -->
<Style Selector="MenuItem">
<Setter Property="Header" Value="{ReflectionBinding Header}" />
<Setter Property="IsEnabled" Value="{ReflectionBinding IsEnabled}" />
<Setter Property="InputGesture" Value="{ReflectionBinding Gesture}" />
<Setter Property="Items" Value="{ReflectionBinding Menu.Items}" />
<Setter Property="Command" Value="{ReflectionBinding Command}" />
<Setter Property="CommandParameter" Value="{ReflectionBinding CommandParameter}" />
<Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True" />
<!-- NativeMenuItem is IBitmap and MenuItem is Image -->
<Setter Property="Icon" Value="{ReflectionBinding Icon, Converter={StaticResource AvaloniaThemesSimpleNativeMenuBarIBitmapToImageConverter}}" />
<Style Selector="MenuItem" x:DataType="NativeMenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="InputGesture" Value="{Binding Gesture}"/>
<Setter Property="Items" Value="{Binding Menu.Items}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
<Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True"/>
<!--NativeMenuItem is IBitmap and MenuItem is Image-->
<Setter Property="Icon" Value="{Binding Icon , Converter={StaticResource AvaloniaThemesSimpleNativeMenuBarIBitmapToImageConverter}}"/>
</Style>
</Menu.Styles>
</Menu>

4
src/Avalonia.X11/X11Window.cs

@ -358,7 +358,7 @@ namespace Avalonia.X11
public Action<double> ScalingChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
public Func<bool> Closing { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }
public Action<WindowTransparencyLevel> 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)

9
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);

2
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")]

3
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

6
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;
}

17
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -191,7 +191,7 @@ namespace Avalonia.Win32
public Action Activated { get; set; }
public Func<bool> Closing { get; set; }
public Func<WindowCloseReason, bool> 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);

3
tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj

@ -17,6 +17,9 @@
<EmbeddedResource Remove="..\Avalonia.RenderTests\Assets\NotoColorEmoji.ttf" />
<EmbeddedResource Include="Media\TextFormatting\BreakPairTable.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Nito.AsyncEx.Context" Version="5.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj" />

115
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<string>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
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<object>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
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<BindingValue<string>>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
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()
{

44
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<object>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
AsyncContext.Run(async () =>
{
var target = new Class1();
var source = new Subject<object>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
.Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
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]

8
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);
}

2
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<ViewModel> MakeRef()
{
var weakVm = new WeakReference<ViewModel>(null);

7
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()
{

8
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()
{

Loading…
Cancel
Save