Browse Source

Merge 0ec7ae2104 into 658afb8717

pull/20181/merge
Betta_Fish 24 hours ago
committed by GitHub
parent
commit
385358381c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 267
      samples/ControlCatalog/Pages/CompositionPage.axaml
  2. 177
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  3. 2
      src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
  4. 27
      src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs
  5. 69
      src/Avalonia.Base/Rendering/Composition/Brushes/CompositionBrush.cs
  6. 84
      src/Avalonia.Base/Rendering/Composition/Brushes/ServerCompositionBrush.cs
  7. 24
      src/Avalonia.Base/Rendering/Composition/CompositionGradientStop.cs
  8. 19
      src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
  9. 13
      src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs
  10. 4
      src/Avalonia.Base/Rendering/Composition/Drawing/ServerResourceHelperExtensions.cs
  11. 7
      src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs
  12. 10
      src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs
  13. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs
  14. 7
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs
  15. 403
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs
  16. 5
      src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs
  17. 8
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionGradientStop.cs
  18. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerObjectAnimations.cs
  19. 147
      src/Avalonia.Base/Rendering/Composition/Server/ServerRenderResource.cs
  20. 37
      src/Avalonia.Base/composition-schema.xml
  21. 4
      src/tools/DevGenerators/CompositionGenerator/Generator.cs

267
samples/ControlCatalog/Pages/CompositionPage.axaml

@ -1,62 +1,207 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="using:ControlCatalog.Pages"
x:Class="ControlCatalog.Pages.CompositionPage">
<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>
</Grid>
</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>
<CheckBox Margin="10"
x:Name="PreciseDirtyRectsCheckboxCustomVisual"
IsCheckedChanged="PreciseDirtyRectsCheckboxCustomVisualChanged"
>Precise dirty rects</CheckBox>
</StackPanel>
<Control x:Name="CustomVisualHost" />
</DockPanel>
</TabItem>
</TabControl>
<UserControl
x:Class="ControlCatalog.Pages.CompositionPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="using:ControlCatalog.Pages">
<TabControl>
<TabItem Header="Implicit animations">
<StackPanel>
<Grid Margin="0,0,40,0" ColumnDefinitions="*,10,40">
<ItemsControl x:Name="Items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates>
<DataTemplate DataType="pages:CompositionPageColorItem">
<Border
Width="100"
Height="100"
Margin="10"
Padding="10"
pages:CompositionPage.EnableAnimations="True"
Background="{Binding ColorBrush}"
BorderBrush="Gray"
BorderThickness="2">
<TextBlock Text="{Binding ColorHexValue}" />
</Border>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
<GridSplitter
Grid.Column="1"
Margin="2"
Background="#e0e0e0"
BorderBrush="Gray"
BorderThickness="1"
ResizeBehavior="PreviousAndNext"
ResizeDirection="Columns" />
<Border Grid.Column="2">
<LayoutTransformControl MinWidth="30" HorizontalAlignment="Center">
<LayoutTransformControl.LayoutTransform>
<RotateTransform Angle="90" />
</LayoutTransformControl.LayoutTransform>
<TextBlock>Resize me</TextBlock>
</LayoutTransformControl>
</Border>
</Grid>
</StackPanel>
</TabItem>
<TabItem Header="Animation">
<DockPanel>
<Button
Margin="10"
Click="ButtonThreadSleep"
DockPanel.Dock="Top">
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(5000);</Button>
<Button Margin="10" Click="ButtonStartCustomVisual">Start</Button>
<Button Margin="10" Click="ButtonStopCustomVisual">Stop</Button>
<CheckBox
x:Name="PreciseDirtyRectsCheckboxCustomVisual"
Margin="10"
IsCheckedChanged="PreciseDirtyRectsCheckboxCustomVisualChanged">
Precise dirty rects
</CheckBox>
</StackPanel>
<Control x:Name="CustomVisualHost" />
</DockPanel>
</TabItem>
<TabItem Header="Composition brush">
<ScrollViewer>
<StackPanel>
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="BorderThickness" Value="2" />
</Style>
</StackPanel.Styles>
<Button Margin="10" Click="ButtonThreadSleep">
Thread.Sleep(5000);
</Button>
<UniformGrid
Margin="12"
ColumnSpacing="12"
RowSpacing="12">
<!-- Solid Color -->
<StackPanel Spacing="6">
<TextBlock
FontWeight="Bold"
Foreground="{Binding #SolidBrushHost.Background, Mode=OneWay}"
Text="SolidColorBrush" />
<Border
x:Name="SolidBrushHost"
Height="120"
Background="Gray"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="8">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="18"
Text="Solid" />
</Border>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button
BorderBrush="{Binding #SolidBrushHost.Background, Mode=OneWay}"
Click="SolidBrushCreateAnimate_Click"
Content="Create" />
</StackPanel>
</StackPanel>
<!-- Linear Gradient -->
<StackPanel Spacing="6">
<TextBlock
FontWeight="Bold"
Foreground="{Binding #LinearBrushHost.Background, Mode=OneWay}"
Text="LinearGradientBrush" />
<Border
x:Name="LinearBrushHost"
Height="120"
Background="Gray"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="8">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="18"
Text="Linear" />
</Border>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button
BorderBrush="{Binding #LinearBrushHost.Background, Mode=OneWay}"
Click="LinearBrushCreateAnimate_Click"
Content="Create" />
</StackPanel>
</StackPanel>
<!-- Radial Gradient -->
<StackPanel Spacing="6">
<TextBlock
FontWeight="Bold"
Foreground="{Binding #RadialBrushHost.Background, Mode=OneWay}"
Text="RadialGradientBrush" />
<Border
x:Name="RadialBrushHost"
Height="120"
Background="Gray"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="8">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="18"
Text="Radial" />
</Border>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button
BorderBrush="{Binding #RadialBrushHost.Background, Mode=OneWay}"
Click="RadialBrushCreateAnimate_Click"
Content="Create" />
</StackPanel>
</StackPanel>
<!-- Conic Gradient -->
<StackPanel Spacing="6">
<TextBlock
FontWeight="Bold"
Foreground="{Binding #ConicBrushHost.Background, Mode=OneWay}"
Text="ConicGradientBrush" />
<Border
x:Name="ConicBrushHost"
Height="120"
Background="Gray"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="8">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="18"
Text="Conic" />
</Border>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button
BorderBrush="{Binding #ConicBrushHost.Background, Mode=OneWay}"
Click="ConicBrushCreateAnimate_Click"
Content="Create" />
</StackPanel>
</StackPanel>
</UniformGrid>
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
</UserControl>

177
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@ -334,6 +334,183 @@ public partial class CompositionPage : UserControl
? CustomVisualHandler.UsePreciseDirtyRects
: CustomVisualHandler.UseNonPreciseDirtyRects);
}
private void SolidBrushCreateAnimate_Click(object? sender, RoutedEventArgs e)
{
var visual = ElementComposition.GetElementVisual(SolidBrushHost);
if (visual == null)
return;
var compositor = visual.Compositor;
var brush = SolidBrushHost.Background as CompositionSolidColorBrush
?? compositor.CreateSolidColorBrush();
brush.Color = Color.FromRgb(168, 213, 85);
SolidBrushHost.Background = brush;
var animation = compositor.CreateColorKeyFrameAnimation();
animation.InsertKeyFrame(0f, Color.FromRgb(168, 213, 85));
animation.InsertKeyFrame(0.25f, Color.FromRgb(31, 167, 168));
animation.InsertKeyFrame(0.75f, Color.FromRgb(44, 121, 251));
animation.InsertKeyFrame(1f, Color.FromRgb(168, 213, 85));
animation.Duration = TimeSpan.FromSeconds(4);
animation.IterationBehavior = AnimationIterationBehavior.Forever;
brush.StartAnimation("Color", animation);
}
private void LinearBrushCreateAnimate_Click(object? sender, RoutedEventArgs e)
{
var visual = ElementComposition.GetElementVisual(LinearBrushHost);
if (visual == null)
return;
var compositor = visual.Compositor;
var brush = LinearBrushHost.Background as CompositionLinearGradientBrush
?? compositor.CreateLinearGradientBrush();
brush.GradientStops.Clear();
brush.GradientStops.Add(compositor.CreateGradientStop(0f, Color.FromRgb(168, 213, 85)));
brush.GradientStops.Add(compositor.CreateGradientStop(0.33f, Color.FromRgb(31, 167, 168)));
brush.GradientStops.Add(compositor.CreateGradientStop(0.66f, Color.FromRgb(44, 121, 251)));
brush.GradientStops.Add(compositor.CreateGradientStop(1f, Color.FromRgb(168, 213, 85)));
brush.StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative);
brush.EndPoint = new RelativePoint(1, 0, RelativeUnit.Relative);
LinearBrushHost.Background = brush;
var start = compositor.CreateRelativePointKeyFrameAnimation();
start.Duration = TimeSpan.FromSeconds(4);
start.IterationBehavior = AnimationIterationBehavior.Forever;
start.InsertKeyFrame(0f, new RelativePoint(-1, 0, RelativeUnit.Relative));
start.InsertKeyFrame(0.5f, new RelativePoint(1, 0, RelativeUnit.Relative));
start.InsertKeyFrame(1f, new RelativePoint(-1, 0, RelativeUnit.Relative));
var end = compositor.CreateRelativePointKeyFrameAnimation();
end.Duration = start.Duration;
end.IterationBehavior = AnimationIterationBehavior.Forever;
end.InsertKeyFrame(0f, new RelativePoint(0, 0, RelativeUnit.Relative));
end.InsertKeyFrame(0.5f, new RelativePoint(2, 0, RelativeUnit.Relative));
end.InsertKeyFrame(1f, new RelativePoint(0, 0, RelativeUnit.Relative));
brush.StartAnimation("StartPoint", start);
brush.StartAnimation("EndPoint", end);
AnimateGradientStops(brush);
}
private void RadialBrushCreateAnimate_Click(object? sender, RoutedEventArgs e)
{
var visual = ElementComposition.GetElementVisual(RadialBrushHost);
if (visual == null)
return;
var compositor = visual.Compositor;
var brush = RadialBrushHost.Background as CompositionRadialGradientBrush
?? compositor.CreateRadialGradientBrush();
brush.GradientStops.Clear();
brush.GradientStops.Add(compositor.CreateGradientStop(0f, Colors.Yellow));
brush.GradientStops.Add(compositor.CreateGradientStop(0.6f, Colors.Orange));
brush.GradientStops.Add(compositor.CreateGradientStop(1f, Colors.Red));
brush.Center = RelativePoint.Center;
brush.GradientOrigin = RelativePoint.Center;
RadialBrushHost.Background = brush;
var centerAnim = compositor.CreateRelativePointKeyFrameAnimation();
centerAnim.Duration = TimeSpan.FromSeconds(3);
centerAnim.IterationBehavior = AnimationIterationBehavior.Forever;
centerAnim.InsertKeyFrame(0f, new RelativePoint(0.2, 0.5, RelativeUnit.Relative));
centerAnim.InsertKeyFrame(0.5f, new RelativePoint(0.8, 0.5, RelativeUnit.Relative));
centerAnim.InsertKeyFrame(1f, new RelativePoint(0.2, 0.5, RelativeUnit.Relative));
brush.StartAnimation("Center", centerAnim);
var radiusXExpression = compositor.CreateExpressionAnimation(
"RelativeScalar(Clamp(0.2 + Abs(this.Target.Center.X - 0.5) * 0.9, 0.2, 0.85), RelativeUnit.Relative)");
var radiusYExpression = compositor.CreateExpressionAnimation(
"RelativeScalar(Clamp(0.2 + Abs(this.Target.Center.Y - 0.5) * 0.9, 0.2, 0.85), RelativeUnit.Relative)");
brush.StartAnimation("RadiusX", radiusXExpression);
brush.StartAnimation("RadiusY", radiusYExpression);
AnimateGradientStops(brush);
}
private void ConicBrushCreateAnimate_Click(object? sender, RoutedEventArgs e)
{
var visual = ElementComposition.GetElementVisual(ConicBrushHost);
if (visual == null)
return;
var compositor = visual.Compositor;
var brush = ConicBrushHost.Background as CompositionConicGradientBrush
?? compositor.CreateConicGradientBrush();
brush.GradientStops.Clear();
brush.GradientStops.Add(compositor.CreateGradientStop(0f, Colors.Cyan));
brush.GradientStops.Add(compositor.CreateGradientStop(0.25f, Colors.Magenta));
brush.GradientStops.Add(compositor.CreateGradientStop(0.5f, Colors.Yellow));
brush.GradientStops.Add(compositor.CreateGradientStop(0.75f, Colors.Lime));
brush.GradientStops.Add(compositor.CreateGradientStop(1f, Colors.Cyan));
brush.Center = RelativePoint.Center;
ConicBrushHost.Background = brush;
var angleAnim = compositor.CreateScalarKeyFrameAnimation();
angleAnim.Duration = TimeSpan.FromSeconds(5);
angleAnim.IterationBehavior = AnimationIterationBehavior.Forever;
angleAnim.InsertKeyFrame(0f, 0f);
angleAnim.InsertKeyFrame(1f, 360f);
brush.StartAnimation("Angle", angleAnim);
var centerExpression = compositor.CreateExpressionAnimation(
"RelativePoint(Abs(Cos(ToRadians(this.Target.Angle))), Abs(Sin(ToRadians(this.Target.Angle))), RelativeUnit.Relative)");
brush.StartAnimation("Center", centerExpression);
AnimateGradientStops(brush, false);
}
private void AnimateGradientStops(CompositionGradientBrush? brush, bool animateOffsets = true)
{
if (brush == null)
return;
var compositor = brush.Compositor;
foreach (var stop in brush.GradientStops)
{
if (stop is not CompositionGradientStop gs)
continue;
var colorAnim = compositor.CreateColorKeyFrameAnimation();
colorAnim.Duration = TimeSpan.FromSeconds(4);
colorAnim.IterationBehavior = AnimationIterationBehavior.Forever;
colorAnim.InsertKeyFrame(0f, gs.Color);
colorAnim.InsertKeyFrame(0.5f, ShiftColor(gs.Color));
colorAnim.InsertKeyFrame(1f, gs.Color);
gs.StartAnimation("Color", colorAnim);
if (!animateOffsets)
continue;
var offsetAnim = compositor.CreateScalarKeyFrameAnimation();
offsetAnim.Duration = TimeSpan.FromSeconds(4);
offsetAnim.IterationBehavior = AnimationIterationBehavior.Forever;
var o = (float)gs.Offset;
var o2 = Math.Clamp(gs.Offset + 0.1, 0, 1);
offsetAnim.InsertKeyFrame(0f, o);
offsetAnim.InsertKeyFrame(0.5f, (float)o2);
offsetAnim.InsertKeyFrame(1f, o);
gs.StartAnimation("Offset", offsetAnim);
}
}
private static Color ShiftColor(Color c)
{
var r = (byte)Math.Min(255, (int)(c.R * 1.15) + 30);
var g = (byte)Math.Max(0, (int)(c.G * 0.75) - 10);
var b = (byte)Math.Min(255, (int)(c.B * 1.2) + 20);
return Color.FromArgb(c.A, r, g, b);
}
}
public class CompositionPageColorItem

2
src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs

@ -30,7 +30,7 @@ internal abstract class AnimationInstanceBase : IAnimationInstance
_trackedObjects = new ();
foreach (var t in trackedObjects)
{
var obj = Parameters.GetObjectParameter(t.name);
var obj = (t.name == "this.Target") ? TargetObject : Parameters.GetObjectParameter(t.name);
if (obj is ServerObject tracked)
{
var off = tracked.GetCompositionProperty(t.member);

27
src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs

@ -98,4 +98,31 @@ namespace Avalonia.Rendering.Composition.Animations
public static BooleanInterpolator Instance { get; } = new BooleanInterpolator();
}
class RelativePointInterpolator : IInterpolator<Avalonia.RelativePoint>
{
public Avalonia.RelativePoint Interpolate(Avalonia.RelativePoint @from, Avalonia.RelativePoint to, float progress)
{
if (@from.Unit != to.Unit)
return progress >= 0.5f ? to : @from;
var x = DoubleInterpolator.Instance.Interpolate(@from.Point.X, to.Point.X, progress);
var y = DoubleInterpolator.Instance.Interpolate(@from.Point.Y, to.Point.Y, progress);
return new Avalonia.RelativePoint(x, y, @from.Unit);
}
public static RelativePointInterpolator Instance { get; } = new();
}
class RelativeScalarInterpolator : IInterpolator<Avalonia.RelativeScalar>
{
public Avalonia.RelativeScalar Interpolate(Avalonia.RelativeScalar @from, Avalonia.RelativeScalar to, float progress)
{
if (@from.Unit != to.Unit)
return progress >= 0.5f ? to : @from;
return new RelativeScalar(@from.Scalar + (to.Scalar - @from.Scalar) * progress, from.Unit);
}
public static RelativeScalarInterpolator Instance { get; } = new();
}
}

69
src/Avalonia.Base/Rendering/Composition/Brushes/CompositionBrush.cs

@ -0,0 +1,69 @@
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport;
using Avalonia.Styling;
namespace Avalonia.Rendering.Composition;
partial class CompositionBrush : IBrush
{
partial void InitializeDefaultsExtra()
{
Server.Activate();
}
}
partial class CompositionSolidColorBrush : ISolidColorBrush
{
internal CompositionSolidColorBrush(Compositor compositor, ServerCompositionSolidColorBrush server, Color color) : base(compositor, server)
{
Server = server;
Color = color;
InitializeDefaults();
}
}
partial class CompositionLinearGradientBrush : ILinearGradientBrush
{
}
partial class CompositionRadialGradientBrush : IRadialGradientBrush
{
public double Radius => RadiusX.Scalar;
}
partial class CompositionConicGradientBrush : IConicGradientBrush
{
}
public abstract partial class CompositionGradientBrush : CompositionBrush, IGradientBrush
{
internal new ServerCompositionGradientBrush Server { get; }
public List<IGradientStop> GradientStops { get; set; } = [];
IReadOnlyList<IGradientStop> IGradientBrush.GradientStops => GradientStops;
public GradientSpreadMethod SpreadMethod { get; set; }
partial void OnRootChanged();
partial void OnRootChanging();
internal CompositionGradientBrush(Compositor compositor, ServerCompositionGradientBrush server) : base(compositor, server)
{
Server = server;
}
private protected override void SerializeChangesCore(BatchStreamWriter writer)
{
base.SerializeChangesCore(writer);
writer.Write(SpreadMethod);
writer.Write(GradientStops.Count);
foreach (var stop in GradientStops)
{
if (stop is CompositionGradientStop comp)
writer.WriteObject(comp.Server);
else
writer.WriteObject(stop);
}
}
}

84
src/Avalonia.Base/Rendering/Composition/Brushes/ServerCompositionBrush.cs

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition.Transport;
namespace Avalonia.Rendering.Composition.Server;
internal partial class ServerCompositionBrush : IBrush
{
ITransform? IBrush.Transform => Transform;
}
internal partial class ServerCompositionGradientBrush : ServerCompositionBrush, IGradientBrush
{
internal ServerCompositionGradientBrush(ServerCompositor compositor) : base(compositor)
{
}
internal static CompositionProperty<List<IGradientStop>> s_IdOfGradientStopsProperty = CompositionProperty.Register<ServerCompositionGradientBrush, List<IGradientStop>>("GradientStops", obj => ((ServerCompositionGradientBrush)obj)._gradientStops, (obj, v) => ((ServerCompositionGradientBrush)obj)._gradientStops = v, null);
private List<IGradientStop> _gradientStops = new();
IReadOnlyList<IGradientStop> IGradientBrush.GradientStops => GradientStops;
public List<IGradientStop> GradientStops
{
get
{
return _gradientStops;
}
set
{
var changed = false;
if (_gradientStops != value)
{
OnGradientStopsChanging();
changed = true;
}
SetValue(s_IdOfGradientStopsProperty, ref _gradientStops, value);
if (changed)
OnGradientStopsChanged();
}
}
partial void OnGradientStopsChanged();
partial void OnGradientStopsChanging();
public GradientSpreadMethod SpreadMethod { get; private set; }
protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt)
{
base.DeserializeChangesCore(reader, committedAt);
SpreadMethod = reader.Read<GradientSpreadMethod>();
var stops = new List<IGradientStop>();
var count = reader.Read<int>();
for (var c = 0; c < count; c++)
{
var read = reader.ReadObject<IGradientStop>();
stops.Add(read);
}
GradientStops = stops;
}
}
partial class ServerCompositionConicGradientBrush : IConicGradientBrush
{
}
partial class ServerCompositionLinearGradientBrush : ILinearGradientBrush
{
}
partial class ServerCompositionRadialGradientBrush : IRadialGradientBrush
{
public double Radius => RadiusX.Scalar;
}
partial class ServerCompositionSolidColorBrush : ISolidColorBrush
{
}

24
src/Avalonia.Base/Rendering/Composition/CompositionGradientStop.cs

@ -0,0 +1,24 @@
using Avalonia.Media;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Utilities;
namespace Avalonia.Rendering.Composition;
public partial class CompositionGradientStop : IGradientStop
{
internal CompositionGradientStop(Compositor compositor, ServerCompositionGradientStop server, double offset, Color color) : base(compositor, server)
{
Server = server;
if (MathUtilities.IsZero(offset))
{
offset = 0;
}
Offset = (offset < 0) ? 0 : (offset > 1) ? 1 : offset;
Color = color;
InitializeDefaults();
}
partial void InitializeDefaultsExtra()
{
Server.Activate();
}
}

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

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Platform.Surfaces;
using Avalonia.Rendering.Composition.Animations;
@ -18,9 +19,9 @@ public partial class Compositor
{
return new CompositionTarget(this, new ServerCompositionTarget(_server, surfaces));
}
public CompositionContainerVisual CreateContainerVisual() => new(this, new ServerCompositionContainerVisual(_server));
public ExpressionAnimation CreateExpressionAnimation() => new ExpressionAnimation(this);
public ExpressionAnimation CreateExpressionAnimation(string expression) => new ExpressionAnimation(this)
@ -40,4 +41,18 @@ public partial class Compositor
public CompositionSurfaceVisual CreateSurfaceVisual() => new(this, new ServerCompositionSurfaceVisual(_server));
public CompositionDrawingSurface CreateDrawingSurface() => new(this);
public CompositionSolidColorBrush CreateSolidColorBrush() => new(this, new ServerCompositionSolidColorBrush(Server));
public CompositionSolidColorBrush CreateSolidColorBrush(Color color) => new(this, new ServerCompositionSolidColorBrush(Server), color);
public CompositionLinearGradientBrush CreateLinearGradientBrush() => new(this, new ServerCompositionLinearGradientBrush(Server));
public CompositionConicGradientBrush CreateConicGradientBrush() => new(this, new ServerCompositionConicGradientBrush(Server));
public CompositionRadialGradientBrush CreateRadialGradientBrush() => new(this, new ServerCompositionRadialGradientBrush(Server));
public CompositionGradientStop CreateGradientStop(double offset, Color color) => new(this, new ServerCompositionGradientStop(Server), offset, color);
public CompositionGradientStop CreateGradientStop() => new(this, new ServerCompositionGradientStop(Server));
}

13
src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs

@ -27,13 +27,13 @@ internal class RenderDataDrawingContext : DrawingContext
return _renderData ??= new(_compositor);
}
}
struct ParentStackItem
{
public RenderDataPushNode? Node;
public List<IRenderDataItem> Items;
}
private List<IRenderDataItem>? _currentItemList;
private static readonly ThreadSafeObjectPool<List<IRenderDataItem>> s_listPool = new();
@ -59,7 +59,7 @@ internal class RenderDataDrawingContext : DrawingContext
{
(_parentNodeStack ??= s_parentStackPool.Get()).Push(default);
return;
}
}
Add(node);
(_parentNodeStack ??= s_parentStackPool.Get()).Push(new ParentStackItem
{
@ -72,7 +72,7 @@ internal class RenderDataDrawingContext : DrawingContext
void Pop<T>() where T : IRenderDataItem
{
var parent = _parentNodeStack!.Pop();
// No-op node
if (parent.Node == null)
return;
@ -98,11 +98,12 @@ internal class RenderDataDrawingContext : DrawingContext
{
if (_compositor == null)
return;
if (resource == null
|| resource is IImmutableBrush
|| resource is ImmutablePen
|| resource is ImmutableTransform)
|| resource is ImmutableTransform
|| resource is CompositionBrush)
return;
if (resource is ICompositionRenderResource renderResource)

4
src/Avalonia.Base/Rendering/Composition/Drawing/ServerResourceHelperExtensions.cs

@ -18,6 +18,8 @@ static class ServerResourceHelperExtensions
return immutable;
if (brush is ICompositionRenderResource<IBrush> resource)
return resource.GetForCompositor(compositor);
if (brush is CompositionBrush compositionBrush)
return compositionBrush.Server;
ThrowNotCompatible(brush);
return null;
}
@ -52,4 +54,4 @@ static class ServerResourceHelperExtensions
resource.GetForCompositor(compositor);
return new ImmutableTransform(transform.Value);
}
}
}

7
src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs

@ -271,6 +271,13 @@ namespace Avalonia.Rendering.Composition.Expressions
{"Transform", (Vector2 a, Matrix3x2 b) => Vector2.Transform(a, b)},
{"Transform", (Vector3 a, Matrix4x4 b) => Vector3.Transform(a, b)},
{"RelativeUnit.Absolute", () => RelativeUnit.Absolute},
{"RelativeUnit.Relative", () => RelativeUnit.Relative},
{"RelativePoint", (float x, float y, RelativeUnit unit) => new RelativePoint(x, y, unit)},
{"RelativePoint", (double x, double y, RelativeUnit unit) => new RelativePoint(x, y, unit)},
{"RelativeScalar", (float value, RelativeUnit unit) => new RelativeScalar(value, unit)},
{"RelativeScalar", (double value, RelativeUnit unit) => new RelativeScalar(value, unit)},
{"Vector2", (float a, float b) => new Vector(a, b)},
{"Vector2", (double a, double b) => new Vector(a, b)},
{"Vector3", (float a, float b, float c) => new Vector3D(a, b, c)},

10
src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs

@ -122,9 +122,17 @@ namespace Avalonia.Rendering.Composition.Expressions
[typeof(Matrix3x2)] = VariantType.Matrix3x2,
[typeof(Matrix4x4)] = VariantType.Matrix4x4,
[typeof(Quaternion)] = VariantType.Quaternion,
[typeof(Color)] = VariantType.Color
[typeof(Color)] = VariantType.Color,
[typeof(RelativePoint)] = VariantType.RelativePoint,
[typeof(RelativeScalar)] = VariantType.RelativeScalar,
[typeof(RelativeUnit)] = VariantType.RelativeUnit
};
public void Add(string name, Func<ExpressionVariant> cb)
{
Add(name, _ => cb());
}
public void Add<T1>(string name, Func<T1, ExpressionVariant> cb) where T1 : struct
{
Add(name, args => cb(args[0].CastOrDefault<T1>()), typeof(T1));

4
src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs

@ -202,6 +202,10 @@ namespace Avalonia.Rendering.Composition.Expressions
Target.CollectReferences(references);
if (Target is ParameterExpression pe)
references.Add((pe.Name, Member));
else if(Target is KeywordExpression ke && ke.Keyword == ExpressionKeyword.Target)
{
references.Add(("this.Target",Member));
}
}
public override ExpressionVariant Evaluate(ref ExpressionEvaluationContext context)

7
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs

@ -39,7 +39,10 @@ namespace Avalonia.Rendering.Composition.Expressions
expr = new KeywordExpression(ExpressionKeyword.False);
else if (parser.TryParseKeywordLowerCase("this.target"))
expr = new KeywordExpression(ExpressionKeyword.Target);
else if (parser.TryParseKeywordLowerCase("relativeunit.relative"))
expr = new FunctionCallExpression("RelativeUnit.Relative", []);
else if (parser.TryParseKeywordLowerCase("relativeunit.absolute"))
expr = new FunctionCallExpression("RelativeUnit.Absolute", []);
if (expr != null)
return true;
@ -246,7 +249,7 @@ namespace Avalonia.Rendering.Composition.Expressions
else if (parser.TryParseCall(out var functionName))
{
var parameterList = new List<Expression>();
while (true)
while (!parser.TryConsume(')'))
{
parameterList.Add(ParseTillTerminator(ref parser, ",)", false, true, out var closingToken));
if (closingToken == ')')

403
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs

@ -21,7 +21,10 @@ namespace Avalonia.Rendering.Composition.Expressions
Matrix3x2,
Matrix4x4,
Quaternion,
Color
Color,
RelativePoint,
RelativeScalar,
RelativeUnit
}
/// <summary>
@ -45,7 +48,9 @@ namespace Avalonia.Rendering.Composition.Expressions
[FieldOffset(4)] public Matrix4x4 Matrix4x4;
[FieldOffset(4)] public Quaternion Quaternion;
[FieldOffset(4)] public Color Color;
[FieldOffset(4)] public RelativePoint RelativePoint;
[FieldOffset(4)] public RelativeScalar RelativeScalar;
[FieldOffset(4)] public RelativeUnit RelativeUnit;
public ExpressionVariant GetProperty(string property)
{
@ -57,7 +62,7 @@ namespace Avalonia.Rendering.Composition.Expressions
return Vector2.Y;
return default;
}
if (Type == VariantType.Vector)
{
if (ReferenceEquals(property, "X"))
@ -75,21 +80,21 @@ namespace Avalonia.Rendering.Composition.Expressions
return Vector3.Y;
if (ReferenceEquals(property, "Z"))
return Vector3.Z;
if(ReferenceEquals(property, "XY"))
if (ReferenceEquals(property, "XY"))
return new Vector2(Vector3.X, Vector3.Y);
if(ReferenceEquals(property, "YX"))
if (ReferenceEquals(property, "YX"))
return new Vector2(Vector3.Y, Vector3.X);
if(ReferenceEquals(property, "XZ"))
if (ReferenceEquals(property, "XZ"))
return new Vector2(Vector3.X, Vector3.Z);
if(ReferenceEquals(property, "ZX"))
if (ReferenceEquals(property, "ZX"))
return new Vector2(Vector3.Z, Vector3.X);
if(ReferenceEquals(property, "YZ"))
if (ReferenceEquals(property, "YZ"))
return new Vector2(Vector3.Y, Vector3.Z);
if(ReferenceEquals(property, "ZY"))
if (ReferenceEquals(property, "ZY"))
return new Vector2(Vector3.Z, Vector3.Y);
return default;
}
if (Type == VariantType.Vector3D)
{
if (ReferenceEquals(property, "X"))
@ -98,17 +103,17 @@ namespace Avalonia.Rendering.Composition.Expressions
return Vector3D.Y;
if (ReferenceEquals(property, "Z"))
return Vector3D.Z;
if(ReferenceEquals(property, "XY"))
if (ReferenceEquals(property, "XY"))
return new Vector(Vector3D.X, Vector3D.Y);
if(ReferenceEquals(property, "YX"))
if (ReferenceEquals(property, "YX"))
return new Vector(Vector3D.Y, Vector3D.X);
if(ReferenceEquals(property, "XZ"))
if (ReferenceEquals(property, "XZ"))
return new Vector(Vector3D.X, Vector3D.Z);
if(ReferenceEquals(property, "ZX"))
if (ReferenceEquals(property, "ZX"))
return new Vector(Vector3D.Z, Vector3D.X);
if(ReferenceEquals(property, "YZ"))
if (ReferenceEquals(property, "YZ"))
return new Vector(Vector3D.Y, Vector3D.Z);
if(ReferenceEquals(property, "ZY"))
if (ReferenceEquals(property, "ZY"))
return new Vector(Vector3D.Z, Vector3D.Y);
return default;
}
@ -142,7 +147,7 @@ namespace Avalonia.Rendering.Composition.Expressions
return Matrix3x2.M32;
return default;
}
if (Type == VariantType.AvaloniaMatrix)
{
if (ReferenceEquals(property, "M11"))
@ -215,7 +220,7 @@ namespace Avalonia.Rendering.Composition.Expressions
return Quaternion.W;
return default;
}
if (Type == VariantType.Color)
{
if (ReferenceEquals(property, "A"))
@ -229,6 +234,26 @@ namespace Avalonia.Rendering.Composition.Expressions
return default;
}
if (Type == VariantType.RelativePoint)
{
if (ReferenceEquals(property, "X"))
return (float)RelativePoint.Point.X;
if (ReferenceEquals(property, "Y"))
return (float)RelativePoint.Point.Y;
if (ReferenceEquals(property, "Unit"))
return RelativePoint.Unit;
return default;
}
if (Type == VariantType.RelativeScalar)
{
if (ReferenceEquals(property, "Scalar"))
return RelativeScalar.Scalar;
if (ReferenceEquals(property, "Unit"))
return RelativeScalar.Unit;
return default;
}
return default;
}
@ -245,7 +270,7 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Scalar,
Scalar = scalar
};
public static implicit operator ExpressionVariant(double d) =>
new ExpressionVariant
{
@ -260,7 +285,7 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Vector2,
Vector2 = value
};
public static implicit operator ExpressionVariant(Vector value) =>
new ExpressionVariant
{
@ -274,7 +299,7 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Vector3,
Vector3 = value
};
public static implicit operator ExpressionVariant(Vector3D value) =>
new ExpressionVariant
{
@ -296,7 +321,7 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Matrix3x2,
Matrix3x2 = value
};
public static implicit operator ExpressionVariant(Matrix value) =>
new ExpressionVariant
{
@ -317,7 +342,7 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Quaternion,
Quaternion = value
};
public static implicit operator ExpressionVariant(Avalonia.Media.Color value) =>
new ExpressionVariant
{
@ -325,6 +350,27 @@ namespace Avalonia.Rendering.Composition.Expressions
Color = value
};
public static implicit operator ExpressionVariant(RelativePoint value) =>
new ExpressionVariant
{
Type = VariantType.RelativePoint,
RelativePoint = value
};
public static implicit operator ExpressionVariant(RelativeScalar value) =>
new ExpressionVariant
{
Type = VariantType.RelativeScalar,
RelativeScalar = value
};
public static implicit operator ExpressionVariant(RelativeUnit value) =>
new ExpressionVariant
{
Type = VariantType.RelativeUnit,
RelativeUnit = value
};
public static ExpressionVariant operator +(ExpressionVariant left, ExpressionVariant right)
{
if (left.Type != right.Type || left.Type == VariantType.Invalid)
@ -332,13 +378,13 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Scalar)
return left.Scalar + right.Scalar;
if (left.Type == VariantType.Double)
return left.Double + right.Double;
if (left.Type == VariantType.Vector2)
return left.Vector2 + right.Vector2;
if (left.Type == VariantType.Vector)
return left.Vector + right.Vector;
@ -350,16 +396,29 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector4)
return left.Vector4 + right.Vector4;
if (left.Type == VariantType.Matrix3x2)
return left.Matrix3x2 + right.Matrix3x2;
if (left.Type == VariantType.Matrix4x4)
return left.Matrix4x4 + right.Matrix4x4;
if (left.Type == VariantType.Quaternion)
return left.Quaternion + right.Quaternion;
if (left.Type == VariantType.RelativePoint && left.RelativePoint.Unit == right.RelativePoint.Unit)
{
return new RelativePoint(
left.RelativePoint.Point.X + right.RelativePoint.Point.X,
left.RelativePoint.Point.Y + right.RelativePoint.Point.Y,
left.RelativePoint.Unit);
}
if (left.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
{
return new RelativeScalar(left.RelativeScalar.Scalar + right.RelativeScalar.Scalar, left.RelativeScalar.Unit);
}
return default;
}
@ -370,13 +429,13 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Scalar)
return left.Scalar - right.Scalar;
if (left.Type == VariantType.Double)
return left.Double - right.Double;
if (left.Type == VariantType.Vector2)
return left.Vector2 - right.Vector2;
if (left.Type == VariantType.Vector)
return left.Vector - right.Vector;
@ -388,16 +447,29 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector4)
return left.Vector4 - right.Vector4;
if (left.Type == VariantType.Matrix3x2)
return left.Matrix3x2 - right.Matrix3x2;
if (left.Type == VariantType.Matrix4x4)
return left.Matrix4x4 - right.Matrix4x4;
if (left.Type == VariantType.Quaternion)
return left.Quaternion - right.Quaternion;
if (left.Type == VariantType.RelativePoint && left.RelativePoint.Unit == right.RelativePoint.Unit)
{
return new RelativePoint(
left.RelativePoint.Point.X - right.RelativePoint.Point.X,
left.RelativePoint.Point.Y - right.RelativePoint.Point.Y,
left.RelativePoint.Unit);
}
if (left.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
{
return new RelativeScalar(left.RelativeScalar.Scalar - right.RelativeScalar.Scalar, left.RelativeScalar.Unit);
}
return default;
}
@ -406,37 +478,43 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Scalar)
return -left.Scalar;
if (left.Type == VariantType.Double)
return -left.Double;
if (left.Type == VariantType.Vector2)
return -left.Vector2;
if (left.Type == VariantType.Vector)
return -left.Vector;
if (left.Type == VariantType.Vector3)
return -left.Vector3;
if (left.Type == VariantType.Vector3D)
return -left.Vector3D;
if (left.Type == VariantType.Vector4)
return -left.Vector4;
if (left.Type == VariantType.Matrix3x2)
return -left.Matrix3x2;
if (left.Type == VariantType.AvaloniaMatrix)
return -left.AvaloniaMatrix;
if (left.Type == VariantType.Matrix4x4)
return -left.Matrix4x4;
if (left.Type == VariantType.Quaternion)
return -left.Quaternion;
if (left.Type == VariantType.RelativePoint)
return new RelativePoint(-left.RelativePoint.Point.X, -left.RelativePoint.Point.Y, left.RelativePoint.Unit);
if (left.Type == VariantType.RelativeScalar)
return new RelativeScalar(-left.RelativeScalar.Scalar, left.RelativeScalar.Unit);
return default;
}
@ -447,7 +525,7 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Scalar && right.Type == VariantType.Scalar)
return left.Scalar * right.Scalar;
if (left.Type == VariantType.Double && right.Type == VariantType.Double)
return left.Double * right.Double;
@ -459,22 +537,22 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector2 && right.Type == VariantType.Scalar)
return left.Vector2 * right.Scalar;
if (left.Type == VariantType.Vector && right.Type == VariantType.Scalar)
return left.Vector * right.Scalar;
if (left.Type == VariantType.Vector && right.Type == VariantType.Double)
return left.Vector * right.Double;
if (left.Type == VariantType.Vector3 && right.Type == VariantType.Vector3)
return left.Vector3 * right.Vector3;
if (left.Type == VariantType.Vector3D && right.Type == VariantType.Vector3D)
return Vector3D.Multiply(left.Vector3D, right.Vector3D);
if (left.Type == VariantType.Vector3 && right.Type == VariantType.Scalar)
return left.Vector3 * right.Scalar;
if (left.Type == VariantType.Vector3D && right.Type == VariantType.Scalar)
return Vector3D.Multiply(left.Vector3D, right.Scalar);
@ -483,28 +561,63 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector4 && right.Type == VariantType.Scalar)
return left.Vector4 * right.Scalar;
if (left.Type == VariantType.Matrix3x2 && right.Type == VariantType.Matrix3x2)
return left.Matrix3x2 * right.Matrix3x2;
if (left.Type == VariantType.Matrix3x2 && right.Type == VariantType.Scalar)
return left.Matrix3x2 * right.Scalar;
if (left.Type == VariantType.AvaloniaMatrix && right.Type == VariantType.AvaloniaMatrix)
return left.AvaloniaMatrix * right.AvaloniaMatrix;
if (left.Type == VariantType.Matrix4x4 && right.Type == VariantType.Matrix4x4)
return left.Matrix4x4 * right.Matrix4x4;
if (left.Type == VariantType.Matrix4x4 && right.Type == VariantType.Scalar)
return left.Matrix4x4 * right.Scalar;
if (left.Type == VariantType.Quaternion && right.Type == VariantType.Quaternion)
return left.Quaternion * right.Quaternion;
if (left.Type == VariantType.Quaternion && right.Type == VariantType.Scalar)
return left.Quaternion * right.Scalar;
if (left.Type == VariantType.RelativePoint && right.Type == VariantType.Scalar)
return new RelativePoint(left.RelativePoint.Point.X * right.Scalar, left.RelativePoint.Point.Y * right.Scalar, left.RelativePoint.Unit);
if (left.Type == VariantType.RelativePoint && right.Type == VariantType.Double)
return new RelativePoint(left.RelativePoint.Point.X * right.Double, left.RelativePoint.Point.Y * right.Double, left.RelativePoint.Unit);
if (left.Type == VariantType.Scalar && right.Type == VariantType.RelativePoint)
return new RelativePoint(left.Scalar * right.RelativePoint.Point.X, left.Scalar * right.RelativePoint.Point.Y, right.RelativePoint.Unit);
if (left.Type == VariantType.Double && right.Type == VariantType.RelativePoint)
return new RelativePoint(left.Double * right.RelativePoint.Point.X, left.Double * right.RelativePoint.Point.Y, right.RelativePoint.Unit);
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.Scalar)
return new RelativeScalar(left.RelativeScalar.Scalar * right.Scalar, left.RelativeScalar.Unit);
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.Double)
return new RelativeScalar(left.RelativeScalar.Scalar * right.Double, left.RelativeScalar.Unit);
if (left.Type == VariantType.Scalar && right.Type == VariantType.RelativeScalar)
return new RelativeScalar(left.Scalar * right.RelativeScalar.Scalar, right.RelativeScalar.Unit);
if (left.Type == VariantType.Double && right.Type == VariantType.RelativeScalar)
return new RelativeScalar(left.Double * right.RelativeScalar.Scalar, right.RelativeScalar.Unit);
if (left.Type == VariantType.RelativePoint && right.Type == VariantType.RelativePoint && left.RelativePoint.Unit == right.RelativePoint.Unit)
{
return new RelativePoint(
left.RelativePoint.Point.X * right.RelativePoint.Point.X,
left.RelativePoint.Point.Y * right.RelativePoint.Point.Y,
left.RelativePoint.Unit);
}
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
return new RelativeScalar(left.RelativeScalar.Scalar * right.RelativeScalar.Scalar, left.RelativeScalar.Unit);
return default;
}
@ -515,7 +628,7 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Scalar && right.Type == VariantType.Scalar)
return left.Scalar / right.Scalar;
if (left.Type == VariantType.Double && right.Type == VariantType.Double)
return left.Double / right.Double;
@ -527,10 +640,10 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector2 && right.Type == VariantType.Scalar)
return left.Vector2 / right.Scalar;
if (left.Type == VariantType.Vector && right.Type == VariantType.Scalar)
return left.Vector / right.Scalar;
if (left.Type == VariantType.Vector && right.Type == VariantType.Double)
return left.Vector / right.Scalar;
@ -545,7 +658,7 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector3D && right.Type == VariantType.Scalar)
return Avalonia.Vector3D.Divide(left.Vector3D, right.Scalar);
if (left.Type == VariantType.Vector3D && right.Type == VariantType.Double)
return Avalonia.Vector3D.Divide(left.Vector3D, right.Double);
@ -554,10 +667,33 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector4 && right.Type == VariantType.Scalar)
return left.Vector4 / right.Scalar;
if (left.Type == VariantType.Quaternion && right.Type == VariantType.Quaternion)
return left.Quaternion / right.Quaternion;
if (left.Type == VariantType.RelativePoint && right.Type == VariantType.Scalar)
return new RelativePoint(left.RelativePoint.Point.X / right.Scalar, left.RelativePoint.Point.Y / right.Scalar, left.RelativePoint.Unit);
if (left.Type == VariantType.RelativePoint && right.Type == VariantType.Double)
return new RelativePoint(left.RelativePoint.Point.X / right.Double, left.RelativePoint.Point.Y / right.Double, left.RelativePoint.Unit);
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.Scalar)
return new RelativeScalar(left.RelativeScalar.Scalar / right.Scalar, left.RelativeScalar.Unit);
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.Double)
return new RelativeScalar(left.RelativeScalar.Scalar / right.Double, left.RelativeScalar.Unit);
if (left.Type == VariantType.RelativePoint && right.Type == VariantType.RelativePoint && left.RelativePoint.Unit == right.RelativePoint.Unit)
{
return new RelativePoint(
left.RelativePoint.Point.X / right.RelativePoint.Point.X,
left.RelativePoint.Point.Y / right.RelativePoint.Point.Y,
left.RelativePoint.Unit);
}
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
return new RelativeScalar(left.RelativeScalar.Scalar / right.RelativeScalar.Scalar, left.RelativeScalar.Unit);
return default;
}
@ -568,20 +704,20 @@ namespace Avalonia.Rendering.Composition.Expressions
if (Type == VariantType.Scalar)
return Scalar == right.Scalar;
if (Type == VariantType.Double)
return Double == right.Double;
if (Type == VariantType.Vector2)
return Vector2 == right.Vector2;
if (Type == VariantType.Vector)
return Vector == right.Vector;
if (Type == VariantType.Vector3)
return Vector3 == right.Vector3;
if (Type == VariantType.Vector3D)
return Vector3D == right.Vector3D;
@ -593,7 +729,7 @@ namespace Avalonia.Rendering.Composition.Expressions
if (Type == VariantType.Matrix3x2)
return Matrix3x2 == right.Matrix3x2;
if (Type == VariantType.AvaloniaMatrix)
return AvaloniaMatrix == right.AvaloniaMatrix;
@ -602,7 +738,16 @@ namespace Avalonia.Rendering.Composition.Expressions
if (Type == VariantType.Quaternion)
return Quaternion == right.Quaternion;
if (Type == VariantType.RelativePoint)
return RelativePoint == right.RelativePoint;
if (Type == VariantType.RelativeScalar)
return RelativeScalar == right.RelativeScalar;
if (Type == VariantType.RelativeUnit)
return RelativeUnit == right.RelativeUnit;
return default;
}
@ -627,6 +772,8 @@ namespace Avalonia.Rendering.Composition.Expressions
return left.Scalar % right.Scalar;
if (left.Type == VariantType.Double && right.Type == VariantType.Double)
return left.Double % right.Double;
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
return new RelativeScalar(left.RelativeScalar.Scalar % right.RelativeScalar.Scalar, left.RelativeScalar.Unit);
return default;
}
@ -636,6 +783,8 @@ namespace Avalonia.Rendering.Composition.Expressions
return left.Scalar < right.Scalar;
if (left.Type == VariantType.Double && right.Type == VariantType.Double)
return left.Double < right.Double;
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
return left.RelativeScalar.Scalar < right.RelativeScalar.Scalar;
return default;
}
@ -643,9 +792,11 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (left.Type == VariantType.Scalar && right.Type == VariantType.Scalar)
return left.Scalar > right.Scalar;
if (left.Type == VariantType.Double && right.Type == VariantType.Double)
return left.Double > right.Double;
if (left.Type == VariantType.RelativeScalar && right.Type == VariantType.RelativeScalar && left.RelativeScalar.Unit == right.RelativeScalar.Unit)
return left.RelativeScalar.Scalar > right.RelativeScalar.Scalar;
return default;
}
@ -669,7 +820,7 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Boolean)
{
res = (T) (object) Boolean;
res = (T)(object)Boolean;
return true;
}
}
@ -678,27 +829,27 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Scalar)
{
res = (T) (object) Scalar;
res = (T)(object)Scalar;
return true;
}
if (Type == VariantType.Double)
{
res = (T)(object)Scalar;
res = (T)(object)(float)Double;
return true;
}
}
if (typeof(T) == typeof(double))
{
if (Type == VariantType.Double)
{
res = (T) (object) Double;
res = (T)(object)Double;
return true;
}
if (Type == VariantType.Scalar)
{
res = (T)(object)(float)Double;
res = (T)(object)(double)Scalar;
return true;
}
}
@ -707,22 +858,22 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Vector2)
{
res = (T) (object) Vector2;
res = (T)(object)Vector2;
return true;
}
if (Type == VariantType.Vector)
{
res = (T) (object) Vector.ToVector2();
res = (T)(object)Vector.ToVector2();
return true;
}
}
if (typeof(T) == typeof(Vector))
{
if (Type == VariantType.Vector)
{
res = (T) (object) Vector;
res = (T)(object)Vector;
return true;
}
@ -737,24 +888,24 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Vector3)
{
res = (T) (object) Vector3;
res = (T)(object)Vector3;
return true;
}
if (Type == VariantType.Vector3D)
{
res = (T) (object) Vector3D.ToVector3();
res = (T)(object)Vector3D.ToVector3();
return true;
}
}
if (typeof(T) == typeof(Vector3D))
{
if (Type == VariantType.Vector3D)
{
res = (T) (object) Vector3D;
res = (T)(object)Vector3D;
return true;
}
if (Type == VariantType.Vector3)
{
res = (T)(object)new Vector3D(Vector3);
@ -766,7 +917,7 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Vector4)
{
res = (T) (object) Vector4;
res = (T)(object)Vector4;
return true;
}
}
@ -775,16 +926,16 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Matrix3x2)
{
res = (T) (object) Matrix3x2;
res = (T)(object)Matrix3x2;
return true;
}
}
if (typeof(T) == typeof(Matrix))
{
if (Type == VariantType.AvaloniaMatrix)
{
res = (T) (object) Matrix3x2;
res = (T)(object)Matrix3x2;
return true;
}
}
@ -793,7 +944,7 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Matrix4x4)
{
res = (T) (object) Matrix4x4;
res = (T)(object)Matrix4x4;
return true;
}
}
@ -802,16 +953,44 @@ namespace Avalonia.Rendering.Composition.Expressions
{
if (Type == VariantType.Quaternion)
{
res = (T) (object) Quaternion;
res = (T)(object)Quaternion;
return true;
}
}
if (typeof(T) == typeof(Avalonia.Media.Color))
if (typeof(T) == typeof(Color))
{
if (Type == VariantType.Color)
{
res = (T) (object) Color;
res = (T)(object)Color;
return true;
}
}
if (typeof(T) == typeof(RelativePoint))
{
if (Type == VariantType.RelativePoint)
{
res = (T)(object)RelativePoint;
return true;
}
}
if (typeof(T) == typeof(RelativeScalar))
{
if (Type == VariantType.RelativeScalar)
{
res = (T)(object)RelativeScalar;
return true;
}
}
if (typeof(T) == typeof(RelativeUnit))
{
if (Type == VariantType.RelativeUnit)
{
res = (T)(object)RelativeUnit;
return true;
}
}
@ -823,40 +1002,52 @@ namespace Avalonia.Rendering.Composition.Expressions
public static ExpressionVariant Create<T>(T v) where T : struct
{
if (typeof(T) == typeof(bool))
return (bool) (object) v;
return (bool)(object)v;
if (typeof(T) == typeof(float))
return (float) (object) v;
return (float)(object)v;
if (typeof(T) == typeof(double))
return (double)(object)v;
if (typeof(T) == typeof(Vector2))
return (Vector2) (object) v;
return (Vector2)(object)v;
if (typeof(T) == typeof(Vector))
return (Vector) (object) v;
return (Vector)(object)v;
if (typeof(T) == typeof(Vector3))
return (Vector3) (object) v;
return (Vector3)(object)v;
if (typeof(T) == typeof(Vector3D))
return (Vector3D) (object) v;
return (Vector3D)(object)v;
if (typeof(T) == typeof(Vector4))
return (Vector4) (object) v;
return (Vector4)(object)v;
if (typeof(T) == typeof(Matrix3x2))
return (Matrix3x2) (object) v;
return (Matrix3x2)(object)v;
if (typeof(T) == typeof(Matrix))
return (Matrix) (object) v;
return (Matrix)(object)v;
if (typeof(T) == typeof(Matrix4x4))
return (Matrix4x4) (object) v;
return (Matrix4x4)(object)v;
if (typeof(T) == typeof(Quaternion))
return (Quaternion) (object) v;
if (typeof(T) == typeof(Avalonia.Media.Color))
return (Avalonia.Media.Color) (object) v;
return (Quaternion)(object)v;
if (typeof(T) == typeof(Color))
return (Color)(object)v;
if (typeof(T) == typeof(RelativePoint))
return (RelativePoint)(object)v;
if (typeof(T) == typeof(RelativeScalar))
return (RelativeScalar)(object)v;
if (typeof(T) == typeof(RelativeUnit))
return (RelativeUnit)(object)v;
throw new ArgumentException("Invalid variant type: " + typeof(T));
}
@ -895,6 +1086,12 @@ namespace Avalonia.Rendering.Composition.Expressions
return Matrix4x4.ToString();
if (Type == VariantType.Color)
return Color.ToString();
if (Type == VariantType.RelativePoint)
return RelativePoint.ToString();
if (Type == VariantType.RelativeScalar)
return RelativeScalar.ToString();
if (Type == VariantType.RelativeUnit)
return RelativeUnit.ToString();
if (Type == VariantType.Invalid)
return "Invalid";
return "Unknown";

5
src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs

@ -41,6 +41,9 @@ internal class CompositionProperty
{
var id = s_nextId++;
prop = new CompositionProperty<TField>(id, name, typeof(TOwner), getField, setField, getVariant);
if (!s_dynamicRegistry.TryGetValue(typeof(TOwner), out var list))
s_dynamicRegistry[typeof(TOwner)] = list = [];
list.Add(prop);
}
s_ReadOnlyRegistry = null;
@ -112,4 +115,4 @@ internal class CompositionProperty<T> : CompositionProperty
GetField = getField;
SetField = setField;
}
}
}

8
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionGradientStop.cs

@ -0,0 +1,8 @@
using Avalonia.Media;
namespace Avalonia.Rendering.Composition.Server;
partial class ServerCompositionGradientStop : IGradientStop
{
}

4
src/Avalonia.Base/Rendering/Composition/Server/ServerObjectAnimations.cs

@ -84,6 +84,8 @@ class ServerObjectAnimations
NeedsUpdate = false;
_property.SetField(Owner._owner, GetVariant().CastOrDefault<T>());
Owner._owner.NotifyAnimatedValueChanged(_property);
// Notify other expression animations
Owner.OnSetDirectValue(_property);
}
}
}
@ -172,4 +174,4 @@ class ServerObjectAnimations
else
Debug.Assert(false);
}
}
}

147
src/Avalonia.Base/Rendering/Composition/Server/ServerRenderResource.cs

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Avalonia.Rendering.Composition.Transport;
@ -18,24 +19,99 @@ internal interface IServerRenderResource : IServerRenderResourceObserver
void QueuedInvalidate();
}
internal class SimpleServerRenderResource : SimpleServerObject, IServerRenderResource, IDisposable
internal interface IServerRenderResourceHost : IServerRenderResource
{
ServerCompositor Compositor { get; }
void ResourcePropertyChanged();
}
internal class SimpleServerRenderResource : SimpleServerObject, IServerRenderResourceHost, IDisposable
{
private ServerRenderResourceCore _core;
public bool IsDisposed => _core.IsDisposed;
public SimpleServerRenderResource(ServerCompositor compositor) : base(compositor) { }
protected new void SetValue<T>(CompositionProperty prop, ref T field, T value) => SetValue(ref field, value);
protected void SetValue<T>(ref T field, T value) => _core.SetValue(this, ref field, value);
protected void Invalidated() => _core.Invalidated(this);
protected override void ValuesInvalidated()
{
Invalidated();
base.ValuesInvalidated();
}
protected void RemoveObserversFromProperty<T>(ref T field) => _core.RemoveObserversFromProperty(this, ref field);
public virtual void Dispose() => _core.Dispose();
public virtual void DependencyQueuedInvalidate(IServerRenderResource sender) => _core.DependencyQueuedInvalidate(this);
protected virtual void PropertyChanged()
{
}
public void AddObserver(IServerRenderResourceObserver observer) => _core.AddObserver(observer);
public void RemoveObserver(IServerRenderResourceObserver observer) => _core.RemoveObserver(observer);
public virtual void QueuedInvalidate() => _core.QueuedInvalidate(this);
void IServerRenderResourceHost.ResourcePropertyChanged() => PropertyChanged();
}
internal class ServerRenderResource : ServerObject, IServerRenderResourceHost, IDisposable
{
private ServerRenderResourceCore _core;
public bool IsDisposed => _core.IsDisposed;
public ServerRenderResource(ServerCompositor compositor) : base(compositor) { }
protected new void SetValue<T>(CompositionProperty prop, ref T field, T value) => SetValue(ref field, value);
protected void SetValue<T>(ref T field, T value) => _core.SetValue(this, ref field, value);
protected void Invalidated() => _core.Invalidated(this);
protected override void ValuesInvalidated()
{
Invalidated();
base.ValuesInvalidated();
}
protected void RemoveObserversFromProperty<T>(ref T field) => _core.RemoveObserversFromProperty(this, ref field);
public virtual void Dispose() => _core.Dispose();
public virtual void DependencyQueuedInvalidate(IServerRenderResource sender) => _core.DependencyQueuedInvalidate(this);
protected virtual void PropertyChanged()
{
}
public void AddObserver(IServerRenderResourceObserver observer) => _core.AddObserver(observer);
public void RemoveObserver(IServerRenderResourceObserver observer) => _core.RemoveObserver(observer);
public virtual void QueuedInvalidate() => _core.QueuedInvalidate(this);
void IServerRenderResourceHost.ResourcePropertyChanged() => PropertyChanged();
}
internal struct ServerRenderResourceCore
{
private bool _pendingInvalidation;
private bool _disposed;
public bool IsDisposed => _disposed;
private RefCountingSmallDictionary<IServerRenderResourceObserver> _observers;
public SimpleServerRenderResource(ServerCompositor compositor) : base(compositor)
{
}
protected new void SetValue<T>(CompositionProperty prop, ref T field, T value) => SetValue(ref field, value);
protected void SetValue<T>(ref T field, T value)
public void SetValue<T>(IServerRenderResourceHost self, ref T field, T value)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return;
if (_disposed)
{
field = value;
@ -43,64 +119,58 @@ internal class SimpleServerRenderResource : SimpleServerObject, IServerRenderRes
}
if (field is IServerRenderResource oldChild)
oldChild.RemoveObserver(this);
else if (field is IServerRenderResource[] oldChildren)
oldChild.RemoveObserver(self);
else if (field is IEnumerable oldChildren)
{
foreach (var ch in oldChildren)
ch?.RemoveObserver(this);
(ch as IServerRenderResource)?.RemoveObserver(self);
}
field = value;
if (field is IServerRenderResource newChild)
newChild.AddObserver(this);
else if (field is IServerRenderResource[] newChildren)
newChild.AddObserver(self);
else if (field is IEnumerable newChildren)
{
foreach (var ch in newChildren)
ch.AddObserver(this);
(ch as IServerRenderResource)?.AddObserver(self);
}
Invalidated();
Invalidated(self);
}
protected void Invalidated()
public void Invalidated(IServerRenderResourceHost self)
{
// This is needed to avoid triggering on multiple property changes
if (!_pendingInvalidation)
{
_pendingInvalidation = true;
Compositor.EnqueueRenderResourceForInvalidation(this);
PropertyChanged();
self.Compositor.EnqueueRenderResourceForInvalidation(self);
self.ResourcePropertyChanged();
}
}
protected override void ValuesInvalidated()
{
Invalidated();
base.ValuesInvalidated();
}
protected void RemoveObserversFromProperty<T>(ref T field)
public void RemoveObserversFromProperty<T>(IServerRenderResource self, ref T field)
{
(field as IServerRenderResource)?.RemoveObserver(this);
(field as IServerRenderResource)?.RemoveObserver(self);
}
public virtual void Dispose()
public void Dispose()
{
_disposed = true;
// TODO: dispose once we implement pooling
_observers = default;
}
public virtual void DependencyQueuedInvalidate(IServerRenderResource sender) =>
Compositor.EnqueueRenderResourceForInvalidation(this);
protected virtual void PropertyChanged()
public void DependencyQueuedInvalidate(IServerRenderResourceHost self)
{
self.Compositor.EnqueueRenderResourceForInvalidation(self);
}
public void AddObserver(IServerRenderResourceObserver observer)
{
Debug.Assert(!_disposed);
if(_disposed)
if (_disposed)
return;
_observers.Add(observer);
}
@ -112,12 +182,11 @@ internal class SimpleServerRenderResource : SimpleServerObject, IServerRenderRes
_observers.Remove(observer);
}
public virtual void QueuedInvalidate()
public void QueuedInvalidate(IServerRenderResource self)
{
_pendingInvalidation = false;
foreach (var observer in _observers)
observer.Key.DependencyQueuedInvalidate(this);
observer.Key.DependencyQueuedInvalidate(self);
}
}
}

37
src/Avalonia.Base/composition-schema.xml

@ -70,6 +70,8 @@
<KeyFrameAnimation Type="Vector3D"/>
<KeyFrameAnimation Type="Vector4"/>
<KeyFrameAnimation Type="Quaternion"/>
<KeyFrameAnimation Name="RelativePoint" Type="Avalonia.RelativePoint"/>
<KeyFrameAnimation Name="RelativeScalar" Type="Avalonia.RelativeScalar"/>
<Object Name="CompositionSimpleTransform" Internal="true" ServerOnly="true" ServerBase="SimpleServerRenderResource">
<Property Name="Value" Type="Avalonia.Matrix" />
</Object>
@ -119,4 +121,39 @@
<Property Name="SnapsToDevicePixels" Type="bool" Animated="true" DefaultValue="false"/>
<Property Name="EnableClearType" Type="bool" Animated="true" DefaultValue="false"/>
</Object>
<Object Name="CompositionBrush" ServerBase="ServerRenderResource">
<Property Name="Opacity" Type="double" Animated="true" DefaultValue="1"/>
<Property Name="TransformOrigin" Type="RelativePoint" />
<Property Name="Transform" Type="Avalonia.Media.ITransform?" />
</Object>
<Object Name="CompositionSolidColorBrush" Inherits="CompositionBrush" ServerBase="ServerCompositionBrush">
<Property Name="Color" Type="Avalonia.Media.Color" Animated="true"/>
</Object>
<Manual Name="CompositionGradientBrush" />
<Object Name="CompositionConicGradientBrush" Inherits="CompositionGradientBrush" ServerBase="ServerCompositionGradientBrush">
<Property Name="Angle" Type="double" Animated="true"/>
<Property Name="Center" Type="Avalonia.RelativePoint" Animated="true"/>
</Object>
<Object Name="CompositionRadialGradientBrush" Inherits="CompositionGradientBrush" ServerBase="ServerCompositionGradientBrush">
<Property Name="Center" Type="Avalonia.RelativePoint" Animated="true"/>
<Property Name="GradientOrigin" Type="Avalonia.RelativePoint" Animated="true"/>
<Property Name="RadiusX" Type="Avalonia.RelativeScalar" Animated="true" DefaultValue="RelativeScalar.Middle"/>
<Property Name="RadiusY" Type="Avalonia.RelativeScalar" Animated="true" DefaultValue="RelativeScalar.Middle"/>
</Object>
<Object Name="CompositionLinearGradientBrush" Inherits="CompositionGradientBrush" ServerBase="ServerCompositionGradientBrush">
<Property Name="StartPoint" Type="Avalonia.RelativePoint" Animated="true"/>
<Property Name="EndPoint" Type="Avalonia.RelativePoint" Animated="true"/>
</Object>
<Object Name="CompositionTileBrush" Internal="true" ServerOnly="true" ServerBase="ServerCompositionBrush">
<Property Name="AlignmentX" Type="Avalonia.Media.AlignmentX" />
<Property Name="AlignmentY" Type="Avalonia.Media.AlignmentY" />
<Property Name="DestinationRect" Type="Avalonia.RelativeRect" />
<Property Name="SourceRect" Type="Avalonia.RelativeRect" />
<Property Name="Stretch" Type="Avalonia.Media.Stretch" />
<Property Name="TileMode" Type="Avalonia.Media.TileMode" />
</Object>
<Object Name="CompositionGradientStop" ServerBase="ServerRenderResource">
<Property Name="Color" Type="Avalonia.Media.Color" Animated="true"/>
<Property Name="Offset" Type="double" Animated="true"/>
</Object>
</NComposition>

4
src/tools/DevGenerators/CompositionGenerator/Generator.cs

@ -426,6 +426,8 @@ return;
{
"bool",
"float",
"double",
"Vector",
"Vector2",
"Vector3",
"Vector4",
@ -435,6 +437,8 @@ return;
"Quaternion",
"Color",
"Avalonia.Media.Color",
"Avalonia.RelativePoint",
"Avalonia.RelativeScalar",
"Vector3D"
};

Loading…
Cancel
Save