diff --git a/samples/AppWithoutLifetime/MainWindow.axaml.cs b/samples/AppWithoutLifetime/MainWindow.axaml.cs
index d27d5d5653..df3ed3cea0 100644
--- a/samples/AppWithoutLifetime/MainWindow.axaml.cs
+++ b/samples/AppWithoutLifetime/MainWindow.axaml.cs
@@ -17,10 +17,11 @@ public partial class MainWindow : Window
AvaloniaXamlLoader.Load(this);
}
- protected override void OnLoaded()
+ ///
+ protected override void OnLoaded(RoutedEventArgs e)
{
this.AttachDevTools();
- base.OnLoaded();
+ base.OnLoaded(e);
}
public void Open(object sender, RoutedEventArgs e)
diff --git a/samples/AppWithoutLifetime/Sub.axaml.cs b/samples/AppWithoutLifetime/Sub.axaml.cs
index 50c770b3a2..3a7ce787bc 100644
--- a/samples/AppWithoutLifetime/Sub.axaml.cs
+++ b/samples/AppWithoutLifetime/Sub.axaml.cs
@@ -1,5 +1,6 @@
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace AppWithoutLifetime;
@@ -16,9 +17,10 @@ public partial class Sub : Window
AvaloniaXamlLoader.Load(this);
}
- protected override void OnLoaded()
+ ///
+ protected override void OnLoaded(RoutedEventArgs e)
{
this.AttachDevTools();
- base.OnLoaded();
+ base.OnLoaded(e);
}
}
diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
index a43ea4539a..f668067f80 100644
--- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
+++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
@@ -10,6 +10,11 @@
apk
true
+
+
+
+
+
Resources\drawable\Icon.png
@@ -37,6 +42,10 @@
+
+
+
+
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index 486d14661e..9b5b06e5fb 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -5,8 +5,16 @@ using Avalonia.Android;
namespace ControlCatalog.Android
{
- [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
- public class MainActivity : AvaloniaMainActivity
+ [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
+ public class MainActivity : AvaloniaMainActivity
{
+ protected override Avalonia.AppBuilder CustomizeAppBuilder(Avalonia.AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder)
+ .AfterSetup(_ =>
+ {
+ Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
+ });
+ }
}
}
diff --git a/samples/ControlCatalog.Android/Resources/drawable-night-v31/avalonia_anim.xml b/samples/ControlCatalog.Android/Resources/drawable-night-v31/avalonia_anim.xml
new file mode 100644
index 0000000000..dde4b5a7dd
--- /dev/null
+++ b/samples/ControlCatalog.Android/Resources/drawable-night-v31/avalonia_anim.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.Android/Resources/drawable-v31/avalonia_anim.xml b/samples/ControlCatalog.Android/Resources/drawable-v31/avalonia_anim.xml
new file mode 100644
index 0000000000..94f27d9e63
--- /dev/null
+++ b/samples/ControlCatalog.Android/Resources/drawable-v31/avalonia_anim.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.Android/Resources/values-v31/styles.xml b/samples/ControlCatalog.Android/Resources/values-v31/styles.xml
new file mode 100644
index 0000000000..d5ecec4391
--- /dev/null
+++ b/samples/ControlCatalog.Android/Resources/values-v31/styles.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.Android/Resources/values/styles.xml b/samples/ControlCatalog.Android/Resources/values/styles.xml
index 3e1270256d..6e534de2f0 100644
--- a/samples/ControlCatalog.Android/Resources/values/styles.xml
+++ b/samples/ControlCatalog.Android/Resources/values/styles.xml
@@ -6,16 +6,7 @@
-
-
-
-
-
diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs
deleted file mode 100644
index a0b68b129b..0000000000
--- a/samples/ControlCatalog.Android/SplashActivity.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Android.App;
-using Android.Content;
-using Android.Content.PM;
-using Android.OS;
-using Avalonia.Android;
-
-namespace ControlCatalog.Android
-{
- [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : AvaloniaSplashActivity
- {
- protected override Avalonia.AppBuilder CustomizeAppBuilder(Avalonia.AppBuilder builder)
- {
- return base.CustomizeAppBuilder(builder)
- .AfterSetup(_ =>
- {
- Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
- });
- }
-
- protected override void OnCreate(Bundle? savedInstanceState)
- {
- base.OnCreate(savedInstanceState);
- }
-
- protected override void OnResume()
- {
- base.OnResume();
-
- StartActivity(new Intent(Application.Context, typeof(MainActivity)));
-
- Finish();
- }
- }
-}
diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
index 0d3061f361..5bf46510dc 100644
--- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
+++ b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
@@ -144,8 +144,8 @@ public partial class CompositionPage : UserControl
{
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);
+ _solidVisual.Size = new (v.Bounds.Width / 3, v.Bounds.Height / 3);
+ _solidVisual.Offset = new (v.Bounds.Width / 3, v.Bounds.Height / 3, 0);
}
v.AttachedToVisualTree += delegate
{
@@ -164,7 +164,7 @@ public partial class CompositionPage : UserControl
animation.Direction = PlaybackDirection.Alternate;
_solidVisual.StartAnimation("Color", animation);
- _solidVisual.AnchorPoint = new Vector2(0, 0);
+ _solidVisual.AnchorPoint = new (0, 0);
var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
scale.Duration = TimeSpan.FromSeconds(5);
@@ -195,8 +195,8 @@ public partial class CompositionPage : UserControl
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);
+ _customVisual.Size = new (v.Bounds.Width, h);
+ _customVisual.Offset = new (0, (v.Bounds.Height - h) / 2, 0);
}
v.AttachedToVisualTree += delegate
{
diff --git a/samples/ControlCatalog/Pages/GesturePage.cs b/samples/ControlCatalog/Pages/GesturePage.cs
index cc4429f414..c81ba4fb17 100644
--- a/samples/ControlCatalog/Pages/GesturePage.cs
+++ b/samples/ControlCatalog/Pages/GesturePage.cs
@@ -13,7 +13,7 @@ namespace ControlCatalog.Pages
public class GesturePage : UserControl
{
private bool _isInit;
- private float _currentScale;
+ private double _currentScale;
public GesturePage()
{
@@ -53,7 +53,7 @@ namespace ControlCatalog.Pages
if(compositionVisual!= null)
{
_currentScale = 1;
- compositionVisual.Scale = new Vector3(1,1,1);
+ compositionVisual.Scale = new (1,1,1);
compositionVisual.Offset = default;
image.InvalidateMeasure();
}
@@ -69,7 +69,7 @@ namespace ControlCatalog.Pages
}
_currentScale = 1;
- Vector3 currentOffset = default;
+ Vector3D currentOffset = default;
CompositionVisual? compositionVisual = null;
@@ -133,11 +133,11 @@ namespace ControlCatalog.Pages
if (compositionVisual != null && _currentScale != 1)
{
- currentOffset += new Vector3((float)e.Delta.X, (float)e.Delta.Y, 0);
+ currentOffset += new Vector3D(e.Delta.X, e.Delta.Y, 0);
var currentSize = control.Bounds.Size * _currentScale;
- currentOffset = new Vector3((float)MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
+ currentOffset = new Vector3D(MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
(float)MathUtilities.Clamp(currentOffset.Y, 0, currentSize.Height - control.Bounds.Height),
0);
@@ -157,7 +157,7 @@ namespace ControlCatalog.Pages
var ball = control.FindLogicalDescendantOfType();
- Vector3 defaultOffset = default;
+ Vector3D defaultOffset = default;
CompositionVisual? ballCompositionVisual = null;
@@ -181,11 +181,11 @@ namespace ControlCatalog.Pages
control.AddHandler(Gestures.PullGestureEvent, (s, e) =>
{
- Vector3 center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
+ Vector3D center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
InitComposition(ball!);
if (ballCompositionVisual != null)
{
- ballCompositionVisual.Offset = defaultOffset + new System.Numerics.Vector3((float)e.Delta.X * 0.4f, (float)e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
+ ballCompositionVisual.Offset = defaultOffset + new Vector3D(e.Delta.X * 0.4f, e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
e.Handled = true;
}
diff --git a/samples/GpuInterop/DrawingSurfaceDemoBase.cs b/samples/GpuInterop/DrawingSurfaceDemoBase.cs
index aad813ea82..367436e5a5 100644
--- a/samples/GpuInterop/DrawingSurfaceDemoBase.cs
+++ b/samples/GpuInterop/DrawingSurfaceDemoBase.cs
@@ -48,7 +48,7 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
Surface = _compositor.CreateDrawingSurface();
_visual = _compositor.CreateSurfaceVisual();
- _visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height);
+ _visual.Size = new (Bounds.Width, Bounds.Height);
_visual.Surface = Surface;
ElementComposition.SetElementChildVisual(this, _visual);
var (res, info) = await DoInitialize(_compositor, Surface);
@@ -72,7 +72,7 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
if (root == null)
return;
- _visual!.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height);
+ _visual!.Size = new (Bounds.Width, Bounds.Height);
var size = PixelSize.FromSize(Bounds.Size, root.RenderScaling);
RenderFrame(size);
if (SupportsDisco && Disco > 0)
diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs
index d65f0dec92..807afb0bd0 100644
--- a/samples/MobileSandbox.Android/MainActivity.cs
+++ b/samples/MobileSandbox.Android/MainActivity.cs
@@ -1,12 +1,11 @@
using Android.App;
using Android.Content.PM;
-using Avalonia;
using Avalonia.Android;
namespace MobileSandbox.Android
{
- [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
- public class MainActivity : AvaloniaMainActivity
+ [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AvaloniaMainActivity
{
}
}
diff --git a/samples/MobileSandbox.Android/MobileSandbox.Android.csproj b/samples/MobileSandbox.Android/MobileSandbox.Android.csproj
index 94f0d31e4d..3b67d79f7b 100644
--- a/samples/MobileSandbox.Android/MobileSandbox.Android.csproj
+++ b/samples/MobileSandbox.Android/MobileSandbox.Android.csproj
@@ -10,6 +10,12 @@
apk
true
+
+
+
+
+
+
Resources\drawable\Icon.png
@@ -37,6 +43,10 @@
+
+
+
+
diff --git a/samples/MobileSandbox.Android/Resources/drawable-night-v31/avalonia_anim.xml b/samples/MobileSandbox.Android/Resources/drawable-night-v31/avalonia_anim.xml
new file mode 100644
index 0000000000..dde4b5a7dd
--- /dev/null
+++ b/samples/MobileSandbox.Android/Resources/drawable-night-v31/avalonia_anim.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MobileSandbox.Android/Resources/drawable-v31/avalonia_anim.xml b/samples/MobileSandbox.Android/Resources/drawable-v31/avalonia_anim.xml
new file mode 100644
index 0000000000..94f27d9e63
--- /dev/null
+++ b/samples/MobileSandbox.Android/Resources/drawable-v31/avalonia_anim.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MobileSandbox.Android/Resources/values-night/colors.xml b/samples/MobileSandbox.Android/Resources/values-night/colors.xml
new file mode 100644
index 0000000000..3d47b6fc58
--- /dev/null
+++ b/samples/MobileSandbox.Android/Resources/values-night/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #212121
+
diff --git a/samples/MobileSandbox.Android/Resources/values-v31/styles.xml b/samples/MobileSandbox.Android/Resources/values-v31/styles.xml
new file mode 100644
index 0000000000..7518f078e5
--- /dev/null
+++ b/samples/MobileSandbox.Android/Resources/values-v31/styles.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/samples/MobileSandbox.Android/Resources/values/styles.xml b/samples/MobileSandbox.Android/Resources/values/styles.xml
index 2759d2904a..22085806da 100644
--- a/samples/MobileSandbox.Android/Resources/values/styles.xml
+++ b/samples/MobileSandbox.Android/Resources/values/styles.xml
@@ -6,12 +6,7 @@
-
-
-
diff --git a/samples/MobileSandbox.Android/SplashActivity.cs b/samples/MobileSandbox.Android/SplashActivity.cs
deleted file mode 100644
index ced092554d..0000000000
--- a/samples/MobileSandbox.Android/SplashActivity.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Android.App;
-using Android.Content;
-using Avalonia.Android;
-
-namespace MobileSandbox.Android
-{
- [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : AvaloniaSplashActivity
- {
- protected override void OnResume()
- {
- base.OnResume();
-
- StartActivity(new Intent(Application.Context, typeof(MainActivity)));
- }
- }
-}
diff --git a/samples/SafeAreaDemo.Android/MainActivity.cs b/samples/SafeAreaDemo.Android/MainActivity.cs
index b0f0a6e419..1df575eb4d 100644
--- a/samples/SafeAreaDemo.Android/MainActivity.cs
+++ b/samples/SafeAreaDemo.Android/MainActivity.cs
@@ -4,8 +4,8 @@ using Avalonia.Android;
namespace SafeAreaDemo.Android
{
- [Activity(Label = "SafeAreaDemo.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
- public class MainActivity : AvaloniaMainActivity
+ [Activity(Label = "SafeAreaDemo.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
+ public class MainActivity : AvaloniaMainActivity
{
}
}
diff --git a/samples/SafeAreaDemo.Android/Resources/drawable-night-v31/avalonia_anim.xml b/samples/SafeAreaDemo.Android/Resources/drawable-night-v31/avalonia_anim.xml
new file mode 100644
index 0000000000..dde4b5a7dd
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/drawable-night-v31/avalonia_anim.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/Resources/drawable-v31/avalonia_anim.xml b/samples/SafeAreaDemo.Android/Resources/drawable-v31/avalonia_anim.xml
new file mode 100644
index 0000000000..94f27d9e63
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/drawable-v31/avalonia_anim.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/Resources/values-night/colors.xml b/samples/SafeAreaDemo.Android/Resources/values-night/colors.xml
new file mode 100644
index 0000000000..3d47b6fc58
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/values-night/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #212121
+
diff --git a/samples/SafeAreaDemo.Android/Resources/values-v31/styles.xml b/samples/SafeAreaDemo.Android/Resources/values-v31/styles.xml
new file mode 100644
index 0000000000..7518f078e5
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/values-v31/styles.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/Resources/values/styles.xml b/samples/SafeAreaDemo.Android/Resources/values/styles.xml
index 2759d2904a..22085806da 100644
--- a/samples/SafeAreaDemo.Android/Resources/values/styles.xml
+++ b/samples/SafeAreaDemo.Android/Resources/values/styles.xml
@@ -6,12 +6,7 @@
-
-
-
diff --git a/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj b/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj
index f5d2af79d0..cc182b2b56 100644
--- a/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj
+++ b/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj
@@ -1,4 +1,4 @@
-
+
Exe
net7.0-android
@@ -12,13 +12,14 @@
-
- Resources\drawable\Icon.png
-
+
+
-
-
+
+ Resources\drawable\Icon.png
+
+
diff --git a/samples/SafeAreaDemo.Android/SplashActivity.cs b/samples/SafeAreaDemo.Android/SplashActivity.cs
deleted file mode 100644
index 621ad1c675..0000000000
--- a/samples/SafeAreaDemo.Android/SplashActivity.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Android.App;
-using Android.Content;
-using Android.OS;
-using Avalonia;
-using Avalonia.Android;
-using Application = Android.App.Application;
-
-namespace SafeAreaDemo.Android
-{
- [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : AvaloniaSplashActivity
- {
- protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
- {
- return base.CustomizeAppBuilder(builder);
- }
-
- protected override void OnCreate(Bundle? savedInstanceState)
- {
- base.OnCreate(savedInstanceState);
- }
-
- protected override void OnResume()
- {
- base.OnResume();
-
- StartActivity(new Intent(Application.Context, typeof(MainActivity)));
- }
- }
-}
diff --git a/samples/SafeAreaDemo/Views/MainView.xaml.cs b/samples/SafeAreaDemo/Views/MainView.xaml.cs
index 2b651225e7..4b8c5e5f15 100644
--- a/samples/SafeAreaDemo/Views/MainView.xaml.cs
+++ b/samples/SafeAreaDemo/Views/MainView.xaml.cs
@@ -1,4 +1,5 @@
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using SafeAreaDemo.ViewModels;
@@ -11,9 +12,10 @@ namespace SafeAreaDemo.Views
AvaloniaXamlLoader.Load(this);
}
- protected override void OnLoaded()
+ ///
+ protected override void OnLoaded(RoutedEventArgs e)
{
- base.OnLoaded();
+ base.OnLoaded(e);
var insetsManager = TopLevel.GetTopLevel(this)?.InsetsManager;
if (insetsManager != null && DataContext is MainViewModel viewModel)
diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml
index 6929f192c7..f96abcac96 100644
--- a/samples/Sandbox/MainWindow.axaml
+++ b/samples/Sandbox/MainWindow.axaml
@@ -1,4 +1,11 @@
+
+
+
+
+
+
+
diff --git a/src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs b/src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs
new file mode 100644
index 0000000000..3fcfde3ee4
--- /dev/null
+++ b/src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Android
+{
+ partial class AvaloniaMainActivity where TApp : Application, new()
+ {
+ protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
+
+ private static AppBuilder? s_appBuilder;
+ internal static object ViewContent;
+
+ public object Content
+ {
+ get
+ {
+ return ViewContent;
+ }
+ set
+ {
+ ViewContent = value;
+ if (View != null)
+ View.Content = value;
+ }
+ }
+
+ protected AppBuilder CreateAppBuilder()
+ {
+ var builder = AppBuilder.Configure();
+
+ return CustomizeAppBuilder(builder);
+ }
+
+ private void InitializeApp()
+ {
+ if (s_appBuilder == null)
+ {
+ var builder = CreateAppBuilder();
+
+ builder.SetupWithLifetime(new SingleViewLifetime());
+
+ s_appBuilder = builder;
+ }
+
+ View = new AvaloniaView(this);
+ if (ViewContent != null)
+ {
+ View.Content = ViewContent;
+ }
+
+ if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime)
+ {
+ lifetime.View = View;
+ }
+ }
+ }
+}
diff --git a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
index b2cd150933..499924d8fb 100644
--- a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
+++ b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
@@ -11,27 +11,49 @@ using AndroidX.AppCompat.App;
namespace Avalonia.Android
{
- public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler, IActivityNavigationService
+ public class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler, IActivityNavigationService
{
- internal static object ViewContent;
-
public Action ActivityResult { get; set; }
public Action RequestPermissionsResult { get; set; }
+
+ public event EventHandler BackRequested;
+
+ public override void OnBackPressed()
+ {
+ var eventArgs = new AndroidBackRequestedEventArgs();
+
+ BackRequested?.Invoke(this, eventArgs);
+
+ if (!eventArgs.Handled)
+ {
+ base.OnBackPressed();
+ }
+ }
+
+ protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+
+ ActivityResult?.Invoke(requestCode, resultCode, data);
+ }
+
+ public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
+ {
+ base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
+
+ RequestPermissionsResult?.Invoke(requestCode, permissions, grantResults);
+ }
+ }
+
+ public abstract partial class AvaloniaMainActivity : AvaloniaMainActivity where TApp : Application, new()
+ {
internal AvaloniaView View;
private GlobalLayoutListener _listener;
protected override void OnCreate(Bundle savedInstanceState)
{
- View = new AvaloniaView(this);
- if (ViewContent != null)
- {
- View.Content = ViewContent;
- }
+ InitializeApp();
- if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime)
- {
- lifetime.View = View;
- }
base.OnCreate(savedInstanceState);
SetContentView(View);
@@ -41,20 +63,6 @@ namespace Avalonia.Android
View.ViewTreeObserver?.AddOnGlobalLayoutListener(_listener);
}
- public object Content
- {
- get
- {
- return ViewContent;
- }
- set
- {
- ViewContent = value;
- if (View != null)
- View.Content = value;
- }
- }
-
protected override void OnResume()
{
base.OnResume();
@@ -66,20 +74,6 @@ namespace Avalonia.Android
}
}
- public event EventHandler BackRequested;
-
- public override void OnBackPressed()
- {
- var eventArgs = new AndroidBackRequestedEventArgs();
-
- BackRequested?.Invoke(this, eventArgs);
-
- if (!eventArgs.Handled)
- {
- base.OnBackPressed();
- }
- }
-
protected override void OnDestroy()
{
View.Content = null;
@@ -89,20 +83,6 @@ namespace Avalonia.Android
base.OnDestroy();
}
- protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
- {
- base.OnActivityResult(requestCode, resultCode, data);
-
- ActivityResult?.Invoke(requestCode, resultCode, data);
- }
-
- public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
- {
- base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
-
- RequestPermissionsResult?.Invoke(requestCode, permissions, grantResults);
- }
-
class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private AvaloniaView _view;
diff --git a/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
deleted file mode 100644
index 38038ef26c..0000000000
--- a/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Android.OS;
-using AndroidX.AppCompat.App;
-
-namespace Avalonia.Android
-{
- public abstract class AvaloniaSplashActivity : AppCompatActivity
- {
- protected abstract AppBuilder CreateAppBuilder();
-
- private static AppBuilder s_appBuilder;
-
- protected override void OnCreate(Bundle? savedInstanceState)
- {
- base.OnCreate(savedInstanceState);
-
- if (s_appBuilder == null)
- {
- var builder = CreateAppBuilder();
-
- var lifetime = new SingleViewLifetime();
-
- builder.SetupWithLifetime(lifetime);
-
- s_appBuilder = builder;
- }
- }
- }
-
- public abstract class AvaloniaSplashActivity : AvaloniaSplashActivity where TApp : Application, new()
- {
- protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
-
- protected override AppBuilder CreateAppBuilder()
- {
- var builder = AppBuilder.Configure();
-
- return CustomizeAppBuilder(builder);
- }
- }
-}
diff --git a/src/Avalonia.Base/Animation/AnimationInstance`1.cs b/src/Avalonia.Base/Animation/AnimationInstance`1.cs
index 92d2c2c8b5..9d4575b9ad 100644
--- a/src/Avalonia.Base/Animation/AnimationInstance`1.cs
+++ b/src/Avalonia.Base/Animation/AnimationInstance`1.cs
@@ -58,6 +58,9 @@ namespace Avalonia.Animation
if (_animation.Duration < TimeSpan.Zero)
throw new InvalidOperationException("Duration value cannot be negative.");
+ if (_animation.Delay < TimeSpan.Zero)
+ throw new InvalidOperationException("Delay value cannot be negative.");
+
_easeFunc = _animation.Easing;
_speedRatioConv = 1d / _animation.SpeedRatio;
@@ -151,72 +154,74 @@ namespace Avalonia.Animation
var iterDelay = _iterationDelay.Ticks * _speedRatioConv;
var initDelay = _initialDelay.Ticks * _speedRatioConv;
- if (indexTime > 0 & indexTime <= initDelay)
+ // This conditional checks if the time given is the very start/zero
+ // and when we have an active delay time.
+ if (initDelay > 0 && indexTime <= initDelay)
{
DoDelay();
+ return;
}
- else
+
+ // Calculate timebases.
+ var iterationTime = iterDuration + iterDelay;
+ var opsTime = indexTime - initDelay;
+ var playbackTime = opsTime % iterationTime;
+
+ _currentIteration = (ulong)(opsTime / iterationTime);
+
+ // Stop animation when the current iteration is beyond the iteration count or
+ // when the duration is set to zero while animating and snap to the last iterated value.
+ if (_currentIteration + 1 > _iterationCount || _duration == TimeSpan.Zero)
{
- // Calculate timebases.
- var iterationTime = iterDuration + iterDelay;
- var opsTime = indexTime - initDelay;
- var playbackTime = opsTime % iterationTime;
+ var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0);
+ _lastInterpValue = _interpolator(easedTime, _neutralValue);
+ DoComplete();
+ }
- _currentIteration = (ulong)(opsTime / iterationTime);
+ if (playbackTime <= iterDuration)
+ {
+ // Normalize time for interpolation.
+ var normalizedTime = playbackTime / iterDuration;
- // Stop animation when the current iteration is beyond the iteration count or
- // when the duration is set to zero while animating and snap to the last iterated value.
- if (_currentIteration + 1 > _iterationCount || _duration == TimeSpan.Zero)
- {
- var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0);
- _lastInterpValue = _interpolator(easedTime, _neutralValue);
- DoComplete();
- }
+ // Check if normalized time needs to be reversed according to PlaybackDirection
- if (playbackTime <= iterDuration)
- {
- // Normalize time for interpolation.
- var normalizedTime = playbackTime / iterDuration;
-
- // Check if normalized time needs to be reversed according to PlaybackDirection
-
- switch (_playbackDirection)
- {
- case PlaybackDirection.Normal:
- _playbackReversed = false;
- break;
- case PlaybackDirection.Reverse:
- _playbackReversed = true;
- break;
- case PlaybackDirection.Alternate:
- _playbackReversed = _currentIteration % 2 != 0;
- break;
- case PlaybackDirection.AlternateReverse:
- _playbackReversed = _currentIteration % 2 == 0;
- break;
- default:
- throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}");
- }
-
- if (_playbackReversed)
- normalizedTime = 1 - normalizedTime;
-
- // Ease and interpolate
- var easedTime = _easeFunc!.Ease(normalizedTime);
- _lastInterpValue = _interpolator(easedTime, _neutralValue);
-
- PublishNext(_lastInterpValue);
- }
- else if (playbackTime > iterDuration &
- playbackTime <= iterationTime &
- iterDelay > 0)
+ switch (_playbackDirection)
{
- // The last iteration's trailing delay should be skipped.
- if (_currentIteration + 1 < _iterationCount)
- DoDelay();
- else
- DoComplete();
+ case PlaybackDirection.Normal:
+ _playbackReversed = false;
+ break;
+ case PlaybackDirection.Reverse:
+ _playbackReversed = true;
+ break;
+ case PlaybackDirection.Alternate:
+ _playbackReversed = _currentIteration % 2 != 0;
+ break;
+ case PlaybackDirection.AlternateReverse:
+ _playbackReversed = _currentIteration % 2 == 0;
+ break;
+ default:
+ throw new InvalidOperationException(
+ $"Animation direction value is unknown: {_playbackDirection}");
}
+
+ if (_playbackReversed)
+ normalizedTime = 1 - normalizedTime;
+
+ // Ease and interpolate
+ var easedTime = _easeFunc!.Ease(normalizedTime);
+ _lastInterpValue = _interpolator(easedTime, _neutralValue);
+
+ PublishNext(_lastInterpValue);
+ }
+ else if (playbackTime > iterDuration &&
+ playbackTime <= iterationTime &&
+ iterDelay > 0)
+ {
+ // The last iteration's trailing delay should be skipped.
+ if (_currentIteration + 1 < _iterationCount)
+ DoDelay();
+ else
+ DoComplete();
}
}
diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs
index 91358712a0..5744ddf963 100644
--- a/src/Avalonia.Base/Input/Pointer.cs
+++ b/src/Avalonia.Base/Input/Pointer.cs
@@ -88,7 +88,6 @@ namespace Avalonia.Input
/// Captures pointer input to the specified gesture recognizer.
///
/// The gesture recognizer.
- ///
internal void CaptureGestureRecognizer(GestureRecognizer? gestureRecognizer)
{
if (CapturedGestureRecognizer != gestureRecognizer)
diff --git a/src/Avalonia.Base/Media/BoxShadow.cs b/src/Avalonia.Base/Media/BoxShadow.cs
index 91529353ed..a0e1873d30 100644
--- a/src/Avalonia.Base/Media/BoxShadow.cs
+++ b/src/Avalonia.Base/Media/BoxShadow.cs
@@ -73,40 +73,44 @@ namespace Avalonia.Media
public override string ToString()
{
var sb = StringBuilderCache.Acquire();
+ ToString(sb);
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+ internal void ToString(StringBuilder sb)
+ {
if (this == default)
{
- return "none";
+ sb.Append("none");
+ return;
}
if (IsInset)
{
- sb.Append("inset");
+ sb.Append("inset ");
}
if (OffsetX != 0.0)
{
- sb.AppendFormat(" {0}", OffsetX.ToString(CultureInfo.InvariantCulture));
+ sb.AppendFormat("{0} ", OffsetX.ToString(CultureInfo.InvariantCulture));
}
if (OffsetY != 0.0)
{
- sb.AppendFormat(" {0}", OffsetY.ToString(CultureInfo.InvariantCulture));
+ sb.AppendFormat("{0} ", OffsetY.ToString(CultureInfo.InvariantCulture));
}
-
+
if (Blur != 0.0)
{
- sb.AppendFormat(" {0}", Blur.ToString(CultureInfo.InvariantCulture));
+ sb.AppendFormat("{0} ", Blur.ToString(CultureInfo.InvariantCulture));
}
if (Spread != 0.0)
{
- sb.AppendFormat(" {0}", Spread.ToString(CultureInfo.InvariantCulture));
+ sb.AppendFormat("{0} ", Spread.ToString(CultureInfo.InvariantCulture));
}
- sb.AppendFormat(" {0}", Color.ToString());
-
- return StringBuilderCache.GetStringAndRelease(sb);
+ sb.AppendFormat("{0}", Color.ToString());
}
public static unsafe BoxShadow Parse(string s)
diff --git a/src/Avalonia.Base/Media/BoxShadows.cs b/src/Avalonia.Base/Media/BoxShadows.cs
index 385f73f703..3ddd8e0a95 100644
--- a/src/Avalonia.Base/Media/BoxShadows.cs
+++ b/src/Avalonia.Base/Media/BoxShadows.cs
@@ -39,20 +39,20 @@ namespace Avalonia.Media
public override string ToString()
{
- var sb = StringBuilderCache.Acquire();
-
if (Count == 0)
{
return "none";
}
+ var sb = StringBuilderCache.Acquire();
foreach (var boxShadow in this)
{
- sb.AppendFormat("{0} ", boxShadow.ToString());
+ boxShadow.ToString(sb);
+ sb.Append(',');
+ sb.Append(' ');
}
-
+ sb.Remove(sb.Length - 2, 2);
return StringBuilderCache.GetStringAndRelease(sb);
-
}
[EditorBrowsable(EditorBrowsableState.Never)]
diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs
index 84a4a8c873..0a290052c7 100644
--- a/src/Avalonia.Base/Media/MediaContext.cs
+++ b/src/Avalonia.Base/Media/MediaContext.cs
@@ -213,10 +213,10 @@ internal partial class MediaContext : ICompositorScheduler
}
///
- /// Executes the callback in the next iteration of the current UI-thread
+ /// Executes the callback in the next iteration of the current UI-thread
/// render loop / layout pass that.
///
- /// Code to execute.
+ ///
public void BeginInvokeOnRender(Action callback)
{
if (_invokeOnRenderCallbacks == null)
diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
index a0cd2a8dcd..df3c9ebfc5 100644
--- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
@@ -161,7 +161,7 @@ namespace Avalonia.Platform
void PopGeometryClip();
///
-
+ /// Get Fetaure from type
///
/// Attempts to get an optional feature from the drawing context implementation
///
diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
index 4518289335..a69d7b7094 100644
--- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
+++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
@@ -216,7 +216,7 @@ namespace Avalonia.PropertyStore
valueChanged = !EqualityComparer.Default.Equals(Value, v);
Value = v;
Priority = priority;
- if (_uncommon is not null)
+ if (!isCoercedDefaultValue && _uncommon is not null)
_uncommon._uncoercedValue = value;
}
@@ -225,7 +225,7 @@ namespace Avalonia.PropertyStore
baseValueChanged = !EqualityComparer.Default.Equals(_baseValue, v);
_baseValue = v;
BasePriority = priority;
- if (_uncommon is not null)
+ if (!isCoercedDefaultValue && _uncommon is not null)
_uncommon._uncoercedBaseValue = value;
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs
index e19c0ff098..0df21c2c1d 100644
--- a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs
@@ -19,6 +19,12 @@ namespace Avalonia.Rendering.Composition.Animations
public static ScalarInterpolator Instance { get; } = new ScalarInterpolator();
}
+ class DoubleInterpolator : IInterpolator
+ {
+ public double Interpolate(double @from, double to, float progress) => @from + (to - @from) * progress;
+
+ public static DoubleInterpolator Instance { get; } = new ();
+ }
class Vector2Interpolator : IInterpolator
{
@@ -28,6 +34,15 @@ namespace Avalonia.Rendering.Composition.Animations
public static Vector2Interpolator Instance { get; } = new Vector2Interpolator();
}
+ class VectorInterpolator : IInterpolator
+ {
+ public Vector Interpolate(Vector @from, Vector to, float progress)
+ => new(DoubleInterpolator.Instance.Interpolate(from.X, to.X, progress),
+ DoubleInterpolator.Instance.Interpolate(from.Y, to.Y, progress));
+
+ public static VectorInterpolator Instance { get; } = new ();
+ }
+
class Vector3Interpolator : IInterpolator
{
public Vector3 Interpolate(Vector3 @from, Vector3 to, float progress)
@@ -36,6 +51,16 @@ namespace Avalonia.Rendering.Composition.Animations
public static Vector3Interpolator Instance { get; } = new Vector3Interpolator();
}
+ class Vector3DInterpolator : IInterpolator
+ {
+ public Vector3D Interpolate(Vector3D @from, Vector3D to, float progress)
+ => new(DoubleInterpolator.Instance.Interpolate(from.X, to.X, progress),
+ DoubleInterpolator.Instance.Interpolate(from.Y, to.Y, progress),
+ DoubleInterpolator.Instance.Interpolate(from.Z, to.Z, progress));
+
+ public static Vector3DInterpolator Instance { get; } = new ();
+ }
+
class Vector4Interpolator : IInterpolator
{
public Vector4 Interpolate(Vector4 @from, Vector4 to, float progress)
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
index 61a3f7e831..368743f1b5 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
@@ -241,8 +241,8 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
continue;
// TODO: Optimize all of that by moving to the Visual itself, so we won't have to recalculate every time
- comp.Offset = new Vector3((float)visual.Bounds.Left, (float)visual.Bounds.Top, 0);
- comp.Size = new Vector2((float)visual.Bounds.Width, (float)visual.Bounds.Height);
+ comp.Offset = new (visual.Bounds.Left, visual.Bounds.Top, 0);
+ comp.Size = new (visual.Bounds.Width, visual.Bounds.Height);
comp.Visible = visual.IsVisible;
comp.Opacity = (float)visual.Opacity;
comp.ClipToBounds = visual.ClipToBounds;
@@ -269,7 +269,7 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
renderTransform *= (-offset) * visual.RenderTransform.Value * (offset);
}
- comp.TransformMatrix = MatrixUtils.ToMatrix4x4(renderTransform);
+ comp.TransformMatrix = renderTransform;
try
{
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs
index b7ed6fe612..598d4163d1 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs
@@ -28,7 +28,7 @@ public abstract class CompositionCustomVisualHandler
_host.Compositor.VerifyAccess();
}
- protected Vector2 EffectiveSize
+ protected Vector EffectiveSize
{
get
{
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
index 91d2fb59de..121d11bf2a 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
@@ -70,7 +70,7 @@ namespace Avalonia.Rendering.Composition
return false;
}
- var m33 = MatrixUtils.ToMatrix(m.Value);
+ var m33 = m.Value;
return m33.TryInvert(out matrix);
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs
index 896ae4fc3a..2fdf163153 100644
--- a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs
@@ -16,6 +16,7 @@ namespace Avalonia.Rendering.Composition.Expressions
private readonly DelegateExpressionFfi _registry;
static float Lerp(float a, float b, float p) => p * (b - a) + a;
+ static double Lerp(double a, double b, double p) => p * (b - a) + a;
static Matrix3x2 Inverse(Matrix3x2 m)
{
@@ -34,6 +35,13 @@ namespace Avalonia.Rendering.Composition.Expressions
var t = MathUtilities.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t);
}
+
+ static double SmoothStep(double edge0, double edge1, double x)
+ {
+ var t = MathUtilities.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+ return t * t * (3.0f - 2.0f * t);
+ }
+
static Vector2 SmoothStep(Vector2 edge0, Vector2 edge1, Vector2 x)
{
@@ -43,6 +51,15 @@ namespace Avalonia.Rendering.Composition.Expressions
);
}
+
+ static Vector SmoothStep(Vector edge0, Vector edge1, Vector x)
+ {
+ return new (
+ SmoothStep(edge0.X, edge1.X, x.X),
+ SmoothStep(edge0.Y, edge1.Y, x.Y)
+ );
+ }
+
static Vector3 SmoothStep(Vector3 edge0, Vector3 edge1, Vector3 x)
{
return new Vector3(
@@ -52,6 +69,15 @@ namespace Avalonia.Rendering.Composition.Expressions
);
}
+
+ static Vector3D SmoothStep(Vector3D edge0, Vector3D edge1, Vector3D x)
+ {
+ return new (
+ SmoothStep(edge0.X, edge1.X, x.X),
+ SmoothStep(edge0.Y, edge1.Y, x.Y),
+ SmoothStep(edge0.Z, edge1.Z, x.Z)
+ );
+ }
static Vector4 SmoothStep(Vector4 edge0, Vector4 edge1, Vector4 x)
{
@@ -69,7 +95,9 @@ namespace Avalonia.Rendering.Composition.Expressions
{
{"Abs", (float f) => Math.Abs(f)},
{"Abs", (Vector2 v) => Vector2.Abs(v)},
+ {"Abs", (Vector v) => v.Abs()},
{"Abs", (Vector3 v) => Vector3.Abs(v)},
+ {"Abs", (Vector3D v) => v.Abs()},
{"Abs", (Vector4 v) => Vector4.Abs(v)},
{"ACos", (float f) => (float) Math.Acos(f)},
@@ -79,7 +107,9 @@ namespace Avalonia.Rendering.Composition.Expressions
{"Clamp", (float a1, float a2, float a3) => MathUtilities.Clamp(a1, a2, a3)},
{"Clamp", (Vector2 a1, Vector2 a2, Vector2 a3) => Vector2.Clamp(a1, a2, a3)},
+ {"Clamp", (Vector a1, Vector a2, Vector a3) => Vector.Clamp(a1, a2, a3)},
{"Clamp", (Vector3 a1, Vector3 a2, Vector3 a3) => Vector3.Clamp(a1, a2, a3)},
+ {"Clamp", (Vector3D a1, Vector3D a2, Vector3D a3) => Vector3D.Clamp(a1, a2, a3)},
{"Clamp", (Vector4 a1, Vector4 a2, Vector4 a3) => Vector4.Clamp(a1, a2, a3)},
{"Concatenate", (Quaternion a1, Quaternion a2) => Quaternion.Concatenate(a1, a2)},
@@ -109,11 +139,15 @@ namespace Avalonia.Rendering.Composition.Expressions
},
{"Distance", (Vector2 a1, Vector2 a2) => Vector2.Distance(a1, a2)},
+ {"Distance", (Vector a1, Vector a2) => Vector.Distance(a1, a2)},
{"Distance", (Vector3 a1, Vector3 a2) => Vector3.Distance(a1, a2)},
+ {"Distance", (Vector3D a1, Vector3D a2) => Vector3D.Distance(a1, a2)},
{"Distance", (Vector4 a1, Vector4 a2) => Vector4.Distance(a1, a2)},
{"DistanceSquared", (Vector2 a1, Vector2 a2) => Vector2.DistanceSquared(a1, a2)},
+ {"DistanceSquared", (Vector a1, Vector a2) => Vector.DistanceSquared(a1, a2)},
{"DistanceSquared", (Vector3 a1, Vector3 a2) => Vector3.DistanceSquared(a1, a2)},
+ {"DistanceSquared", (Vector3D a1, Vector3D a2) => Vector3D.DistanceSquared(a1, a2)},
{"DistanceSquared", (Vector4 a1, Vector4 a2) => Vector4.DistanceSquared(a1, a2)},
{"Floor", (float v) => (float) Math.Floor(v)},
@@ -123,18 +157,24 @@ namespace Avalonia.Rendering.Composition.Expressions
{"Length", (Vector2 a1) => a1.Length()},
+ {"Length", (Vector a1) => a1.Length},
{"Length", (Vector3 a1) => a1.Length()},
+ {"Length", (Vector3D a1) => a1.Length},
{"Length", (Vector4 a1) => a1.Length()},
{"Length", (Quaternion a1) => a1.Length()},
{"LengthSquared", (Vector2 a1) => a1.LengthSquared()},
+ {"LengthSquared", (Vector a1) => a1*a1},
{"LengthSquared", (Vector3 a1) => a1.LengthSquared()},
+ {"LengthSquared", (Vector3D a1) => Vector3D.Dot(a1, a1)},
{"LengthSquared", (Vector4 a1) => a1.LengthSquared()},
{"LengthSquared", (Quaternion a1) => a1.LengthSquared()},
{"Lerp", (float a1, float a2, float a3) => Lerp(a1, a2, a3)},
{"Lerp", (Vector2 a1, Vector2 a2, float a3) => Vector2.Lerp(a1, a2, a3)},
+ {"Lerp", (Vector a1, Vector a2, float a3) => new Vector(Lerp(a1.X, a2.X, a3), Lerp(a1.Y, a2.Y, a3))},
{"Lerp", (Vector3 a1, Vector3 a2, float a3) => Vector3.Lerp(a1, a2, a3)},
+ {"Lerp", (Vector3D a1, Vector3D a2, float a3) => new Vector3D(Lerp(a1.X, a2.X, a3), Lerp(a1.Y, a2.Y, a3), Lerp(a1.Z, a2.Z, a3))},
{"Lerp", (Vector4 a1, Vector4 a2, float a3) => Vector4.Lerp(a1, a2, a3)},
@@ -173,24 +213,31 @@ namespace Avalonia.Rendering.Composition.Expressions
{"Max", (float a1, float a2) => Math.Max(a1, a2)},
{"Max", (Vector2 a1, Vector2 a2) => Vector2.Max(a1, a2)},
+ {"Max", (Vector a1, Vector a2) => Vector.Max(a1, a2)},
{"Max", (Vector3 a1, Vector3 a2) => Vector3.Max(a1, a2)},
+ {"Max", (Vector3D a1, Vector3D a2) => Vector3D.Max(a1, a2)},
{"Max", (Vector4 a1, Vector4 a2) => Vector4.Max(a1, a2)},
{"Min", (float a1, float a2) => Math.Min(a1, a2)},
{"Min", (Vector2 a1, Vector2 a2) => Vector2.Min(a1, a2)},
+ {"Min", (Vector a1, Vector a2) => Vector.Min(a1, a2)},
{"Min", (Vector3 a1, Vector3 a2) => Vector3.Min(a1, a2)},
+ {"Min", (Vector3D a1, Vector3D a2) => Vector3D.Min(a1, a2)},
{"Min", (Vector4 a1, Vector4 a2) => Vector4.Min(a1, a2)},
{"Mod", (float a, float b) => a % b},
{"Normalize", (Quaternion a) => Quaternion.Normalize(a)},
{"Normalize", (Vector2 a) => Vector2.Normalize(a)},
+ {"Normalize", (Vector a) => Vector.Normalize(a)},
{"Normalize", (Vector3 a) => Vector3.Normalize(a)},
+ {"Normalize", (Vector3D a) => Vector3D.Normalize(a)},
{"Normalize", (Vector4 a) => Vector4.Normalize(a)},
{"Pow", (float a, float b) => (float) Math.Pow(a, b)},
{"Quaternion.CreateFromAxisAngle", (Vector3 a, float b) => Quaternion.CreateFromAxisAngle(a, b)},
+ {"Quaternion.CreateFromAxisAngle", (Vector3D a, float b) => Quaternion.CreateFromAxisAngle(a.ToVector3(), b)},
{"Quaternion", (float a, float b, float c, float d) => new Quaternion(a, b, c, d)},
{"Round", (float a) => (float) Math.Round(a)},
@@ -198,14 +245,18 @@ namespace Avalonia.Rendering.Composition.Expressions
{"Scale", (Matrix3x2 a, float b) => a * b},
{"Scale", (Matrix4x4 a, float b) => a * b},
{"Scale", (Vector2 a, float b) => a * b},
+ {"Scale", (Vector a, float b) => a * b},
{"Scale", (Vector3 a, float b) => a * b},
+ {"Scale", (Vector3D a, float b) => Vector3D.Multiply(a, b)},
{"Scale", (Vector4 a, float b) => a * b},
{"Sin", (float a) => (float) Math.Sin(a)},
{"SmoothStep", (float a1, float a2, float a3) => SmoothStep(a1, a2, a3)},
{"SmoothStep", (Vector2 a1, Vector2 a2, Vector2 a3) => SmoothStep(a1, a2, a3)},
+ {"SmoothStep", (Vector a1, Vector a2, Vector a3) => SmoothStep(a1, a2, a3)},
{"SmoothStep", (Vector3 a1, Vector3 a2, Vector3 a3) => SmoothStep(a1, a2, a3)},
+ {"SmoothStep", (Vector3D a1, Vector3D a2, Vector3D a3) => SmoothStep(a1, a2, a3)},
{"SmoothStep", (Vector4 a1, Vector4 a2, Vector4 a3) => SmoothStep(a1, a2, a3)},
// I have no idea how to do a spherical interpolation for a scalar value, so we are doing a linear one
@@ -222,9 +273,13 @@ namespace Avalonia.Rendering.Composition.Expressions
{"Transform", (Vector2 a, Matrix3x2 b) => Vector2.Transform(a, b)},
{"Transform", (Vector3 a, Matrix4x4 b) => Vector3.Transform(a, b)},
- {"Vector2", (float a, float b) => new Vector2(a, b)},
- {"Vector3", (float a, float b, float c) => new Vector3(a, b, c)},
+ {"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)},
+ {"Vector3", (double a, double b, double c) => new Vector3D(a, b, c)},
{"Vector3", (Vector2 v2, float z) => new Vector3(v2, z)},
+ {"Vector3", (Vector v2, float z) => new Vector3D(v2.X, v2.Y, z)},
+ {"Vector3", (Vector v2, double z) => new Vector3D(v2.X, v2.Y, z)},
{"Vector4", (float a, float b, float c, float d) => new Vector4(a, b, c, d)},
{"Vector4", (Vector2 v2, float z, float w) => new Vector4(v2, z, w)},
{"Vector4", (Vector3 v3, float w) => new Vector4(v3, w)},
diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs
index c15487065c..ad8d370478 100644
--- a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs
@@ -49,6 +49,46 @@ namespace Avalonia.Rendering.Composition.Expressions
}
}
+ return CallWithCast(countGroup, arguments, out result, false);
+ }
+
+ bool CallWithCast(List countGroup, IReadOnlyList arguments, out ExpressionVariant result, bool anyCast)
+ {
+ result = default;
+ foreach (var record in countGroup)
+ {
+ var match = true;
+ for (var c = 0; c < arguments.Count; c++)
+ {
+ var parameter = record.Types[c];
+ var arg = arguments[c].Type;
+ if (parameter != arg)
+ {
+ var canCast = (parameter == VariantType.Double && arg == VariantType.Scalar)
+ || (parameter == VariantType.Vector3D && arg == VariantType.Vector3)
+ || (parameter == VariantType.Vector && arg == VariantType.Vector2)
+ || (anyCast && (
+ (arg == VariantType.Double && parameter == VariantType.Scalar)
+ || (arg == VariantType.Vector3D && parameter == VariantType.Vector3)
+ || (arg == VariantType.Vector && parameter == VariantType.Vector2)
+ ));
+ if (!canCast)
+ {
+ match = false;
+ break;
+ }
+ }
+ }
+
+ if (match)
+ {
+ result = record.Delegate(arguments);
+ return true;
+ }
+ }
+
+ if (anyCast == false)
+ return CallWithCast(countGroup, arguments, out result, true);
return false;
}
@@ -75,8 +115,11 @@ namespace Avalonia.Rendering.Composition.Expressions
{
[typeof(bool)] = VariantType.Boolean,
[typeof(float)] = VariantType.Scalar,
+ [typeof(double)] = VariantType.Double,
[typeof(Vector2)] = VariantType.Vector2,
+ [typeof(Vector)] = VariantType.Vector,
[typeof(Vector3)] = VariantType.Vector3,
+ [typeof(Vector3D)] = VariantType.Vector3D,
[typeof(Vector4)] = VariantType.Vector4,
[typeof(Matrix3x2)] = VariantType.Matrix3x2,
[typeof(Matrix4x4)] = VariantType.Matrix4x4,
diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs
index 21f14283b5..a97b959fe1 100644
--- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs
@@ -17,6 +17,8 @@ namespace Avalonia.Rendering.Composition.Expressions
Vector2,
Vector3,
Vector4,
+ Vector,
+ Vector3D,
AvaloniaMatrix,
Matrix3x2,
Matrix4x4,
@@ -38,6 +40,8 @@ namespace Avalonia.Rendering.Composition.Expressions
[FieldOffset(4)] public Vector2 Vector2;
[FieldOffset(4)] public Vector3 Vector3;
[FieldOffset(4)] public Vector4 Vector4;
+ [FieldOffset(4)] public Vector Vector;
+ [FieldOffset(4)] public Vector3D Vector3D;
[FieldOffset(4)] public Matrix AvaloniaMatrix;
[FieldOffset(4)] public Matrix3x2 Matrix3x2;
[FieldOffset(4)] public Matrix4x4 Matrix4x4;
@@ -55,6 +59,15 @@ namespace Avalonia.Rendering.Composition.Expressions
return Vector2.Y;
return default;
}
+
+ if (Type == VariantType.Vector)
+ {
+ if (ReferenceEquals(property, "X"))
+ return Vector.X;
+ if (ReferenceEquals(property, "Y"))
+ return Vector.Y;
+ return default;
+ }
if (Type == VariantType.Vector3)
{
@@ -78,6 +91,29 @@ namespace Avalonia.Rendering.Composition.Expressions
return new Vector2(Vector3.Z, Vector3.Y);
return default;
}
+
+ if (Type == VariantType.Vector3D)
+ {
+ if (ReferenceEquals(property, "X"))
+ return Vector3D.X;
+ if (ReferenceEquals(property, "Y"))
+ return Vector3D.Y;
+ if (ReferenceEquals(property, "Z"))
+ return Vector3D.Z;
+ if(ReferenceEquals(property, "XY"))
+ return new Vector(Vector3D.X, Vector3D.Y);
+ if(ReferenceEquals(property, "YX"))
+ return new Vector(Vector3D.Y, Vector3D.X);
+ if(ReferenceEquals(property, "XZ"))
+ return new Vector(Vector3D.X, Vector3D.Z);
+ if(ReferenceEquals(property, "ZX"))
+ return new Vector(Vector3D.Z, Vector3D.X);
+ if(ReferenceEquals(property, "YZ"))
+ return new Vector(Vector3D.Y, Vector3D.Z);
+ if(ReferenceEquals(property, "ZY"))
+ return new Vector(Vector3D.Z, Vector3D.Y);
+ return default;
+ }
if (Type == VariantType.Vector4)
{
@@ -115,14 +151,20 @@ namespace Avalonia.Rendering.Composition.Expressions
return AvaloniaMatrix.M11;
if (ReferenceEquals(property, "M12"))
return AvaloniaMatrix.M12;
+ if (ReferenceEquals(property, "M13"))
+ return AvaloniaMatrix.M13;
if (ReferenceEquals(property, "M21"))
return AvaloniaMatrix.M21;
if (ReferenceEquals(property, "M22"))
return AvaloniaMatrix.M22;
+ if (ReferenceEquals(property, "M23"))
+ return AvaloniaMatrix.M23;
if (ReferenceEquals(property, "M31"))
return AvaloniaMatrix.M31;
if (ReferenceEquals(property, "M32"))
return AvaloniaMatrix.M32;
+ if (ReferenceEquals(property, "M33"))
+ return AvaloniaMatrix.M33;
return default;
}
@@ -220,7 +262,13 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Vector2,
Vector2 = value
};
-
+
+ public static implicit operator ExpressionVariant(Vector value) =>
+ new ExpressionVariant
+ {
+ Type = VariantType.Vector,
+ Vector = value
+ };
public static implicit operator ExpressionVariant(Vector3 value) =>
new ExpressionVariant
@@ -228,6 +276,13 @@ namespace Avalonia.Rendering.Composition.Expressions
Type = VariantType.Vector3,
Vector3 = value
};
+
+ public static implicit operator ExpressionVariant(Vector3D value) =>
+ new ExpressionVariant
+ {
+ Type = VariantType.Vector3D,
+ Vector3D = value
+ };
public static implicit operator ExpressionVariant(Vector4 value) =>
@@ -285,10 +340,16 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector2)
return left.Vector2 + right.Vector2;
+
+ if (left.Type == VariantType.Vector)
+ return left.Vector + right.Vector;
if (left.Type == VariantType.Vector3)
return left.Vector3 + right.Vector3;
+ if (left.Type == VariantType.Vector3D)
+ return Avalonia.Vector3D.Add(left.Vector3D, right.Vector3D);
+
if (left.Type == VariantType.Vector4)
return left.Vector4 + right.Vector4;
@@ -317,10 +378,16 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector2)
return left.Vector2 - right.Vector2;
+
+ if (left.Type == VariantType.Vector)
+ return left.Vector - right.Vector;
if (left.Type == VariantType.Vector3)
return left.Vector3 - right.Vector3;
+ if (left.Type == VariantType.Vector3D)
+ return Vector3D.Add(left.Vector3D, -right.Vector3D);
+
if (left.Type == VariantType.Vector4)
return left.Vector4 - right.Vector4;
@@ -347,9 +414,15 @@ namespace Avalonia.Rendering.Composition.Expressions
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;
@@ -383,14 +456,29 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector2 && right.Type == VariantType.Vector2)
return left.Vector2 * right.Vector2;
+ if (left.Type == VariantType.Vector && right.Type == VariantType.Vector)
+ return Vector.Multiply(left.Vector, right.Vector);
+
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);
if (left.Type == VariantType.Vector4 && right.Type == VariantType.Vector4)
return left.Vector4 * right.Vector4;
@@ -436,15 +524,33 @@ namespace Avalonia.Rendering.Composition.Expressions
if (left.Type == VariantType.Vector2 && right.Type == VariantType.Vector2)
return left.Vector2 / right.Vector2;
+ if (left.Type == VariantType.Vector && right.Type == VariantType.Vector)
+ return Vector.Divide(left.Vector, right.Vector);
+
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;
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.Divide(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 Avalonia.Vector3D.Divide(left.Vector3D, right.Scalar);
+
+ if (left.Type == VariantType.Vector3D && right.Type == VariantType.Double)
+ return Avalonia.Vector3D.Divide(left.Vector3D, right.Double);
+
if (left.Type == VariantType.Vector4 && right.Type == VariantType.Vector4)
return left.Vector4 / right.Vector4;
@@ -471,9 +577,15 @@ namespace Avalonia.Rendering.Composition.Expressions
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;
if (Type == VariantType.Vector4)
return Vector4 == right.Vector4;
@@ -571,6 +683,11 @@ namespace Avalonia.Rendering.Composition.Expressions
res = (T) (object) Scalar;
return true;
}
+ if (Type == VariantType.Double)
+ {
+ res = (T)(object)Scalar;
+ return true;
+ }
}
if (typeof(T) == typeof(double))
@@ -580,6 +697,12 @@ namespace Avalonia.Rendering.Composition.Expressions
res = (T) (object) Double;
return true;
}
+
+ if (Type == VariantType.Scalar)
+ {
+ res = (T)(object)(float)Double;
+ return true;
+ }
}
if (typeof(T) == typeof(Vector2))
@@ -589,6 +712,27 @@ namespace Avalonia.Rendering.Composition.Expressions
res = (T) (object) Vector2;
return true;
}
+
+ if (Type == VariantType.Vector)
+ {
+ res = (T) (object) Vector.ToVector2();
+ return true;
+ }
+ }
+
+ if (typeof(T) == typeof(Vector))
+ {
+ if (Type == VariantType.Vector)
+ {
+ res = (T) (object) Vector;
+ return true;
+ }
+
+ if (Type == VariantType.Vector2)
+ {
+ res = (T)(object)new Vector(Vector2);
+ return true;
+ }
}
if (typeof(T) == typeof(Vector3))
@@ -598,6 +742,26 @@ namespace Avalonia.Rendering.Composition.Expressions
res = (T) (object) Vector3;
return true;
}
+ if (Type == VariantType.Vector3D)
+ {
+ res = (T) (object) Vector3D.ToVector3();
+ return true;
+ }
+ }
+
+ if (typeof(T) == typeof(Vector3D))
+ {
+ if (Type == VariantType.Vector3D)
+ {
+ res = (T) (object) Vector3D;
+ return true;
+ }
+
+ if (Type == VariantType.Vector3)
+ {
+ res = (T)(object)new Vector3D(Vector3);
+ return true;
+ }
}
if (typeof(T) == typeof(Vector4))
@@ -668,9 +832,15 @@ namespace Avalonia.Rendering.Composition.Expressions
if (typeof(T) == typeof(Vector2))
return (Vector2) (object) v;
+
+ if (typeof(T) == typeof(Vector))
+ return (Vector) (object) v;
if (typeof(T) == typeof(Vector3))
return (Vector3) (object) v;
+
+ if (typeof(T) == typeof(Vector3D))
+ return (Vector3D) (object) v;
if (typeof(T) == typeof(Vector4))
return (Vector4) (object) v;
@@ -709,8 +879,12 @@ namespace Avalonia.Rendering.Composition.Expressions
return Double.ToString(CultureInfo.InvariantCulture);
if (Type == VariantType.Vector2)
return Vector2.ToString();
+ if (Type == VariantType.Vector)
+ return Vector.ToString();
if (Type == VariantType.Vector3)
return Vector3.ToString();
+ if (Type == VariantType.Vector3D)
+ return Vector3D.ToString();
if (Type == VariantType.Vector4)
return Vector4.ToString();
if (Type == VariantType.Quaternion)
diff --git a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs
index 3d8d5bae78..714895cd71 100644
--- a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs
+++ b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs
@@ -6,41 +6,46 @@ namespace Avalonia.Rendering.Composition
{
static class MatrixUtils
{
- public static Matrix4x4 ComputeTransform(Vector2 size, Vector2 anchorPoint, Vector3 centerPoint,
- Matrix4x4 transformMatrix, Vector3 scale, float rotationAngle, Quaternion orientation, Vector3 offset)
+ public static Matrix ComputeTransform(Vector size, Vector anchorPoint, Vector3D centerPoint,
+ Matrix transformMatrix, Vector3D scale, float rotationAngle, Quaternion orientation, Vector3D offset)
{
// The math here follows the *observed* UWP behavior since there are no docs on how it's supposed to work
-
- var anchor = size * anchorPoint;
- var mat = Matrix4x4.CreateTranslation(-anchor.X, -anchor.Y, 0);
- var center = new Vector3(centerPoint.X, centerPoint.Y, centerPoint.Z);
+ var anchor = Vector.Multiply(size, anchorPoint);
+ var mat = Matrix.CreateTranslation(-anchor.X, -anchor.Y);
+
+ var center = new Vector3D(centerPoint.X, centerPoint.Y, centerPoint.Z);
if (!transformMatrix.IsIdentity)
mat = transformMatrix * mat;
- if (scale != new Vector3(1, 1, 1))
- mat *= Matrix4x4.CreateScale(scale, center);
+ if (scale != new Vector3D(1, 1, 1))
+ mat *= ToMatrix(Matrix4x4.CreateScale(scale.ToVector3(), center.ToVector3()));
//TODO: RotationAxis support
if (rotationAngle != 0)
- mat *= Matrix4x4.CreateRotationZ(rotationAngle, center);
+ mat *= ToMatrix(Matrix4x4.CreateRotationZ(rotationAngle, center.ToVector3()));
if (orientation != Quaternion.Identity)
{
if (centerPoint != default)
{
- mat *= Matrix4x4.CreateTranslation(-center)
- * Matrix4x4.CreateFromQuaternion(orientation)
- * Matrix4x4.CreateTranslation(center);
+ mat *= ToMatrix(Matrix4x4.CreateTranslation(-center.ToVector3())
+ * Matrix4x4.CreateFromQuaternion(orientation)
+ * Matrix4x4.CreateTranslation(center.ToVector3()));
}
else
- mat *= Matrix4x4.CreateFromQuaternion(orientation);
+ mat *= ToMatrix(Matrix4x4.CreateFromQuaternion(orientation));
}
if (offset != default)
- mat *= Matrix4x4.CreateTranslation(offset);
+ {
+ if (offset.Z == 0)
+ mat *= Matrix.CreateTranslation(offset.X, offset.Y);
+ else
+ mat *= ToMatrix(Matrix4x4.CreateTranslation(offset.ToVector3()));
+ }
return mat;
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
index b9e6833d21..c63e7917b3 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
@@ -81,10 +81,9 @@ namespace Avalonia.Rendering.Composition.Server
// If we only have translation and scale, just scale the padding
if (CombinedTransformMatrix is
{
- M12: 0, M13: 0, M14: 0,
- M21: 0, M23: 0, M24: 0,
- M31: 0, M32: 0, M34: 0,
- M43: 0, M44: 1
+ M12: 0, M13: 0,
+ M21: 0, M23: 0,
+ M31: 0, M32: 0
})
padding = new Thickness(padding.Left * CombinedTransformMatrix.M11,
padding.Top * CombinedTransformMatrix.M22,
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
index 0eeffda123..3ff1bf2b98 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
@@ -51,7 +51,7 @@ namespace Avalonia.Rendering.Composition.Server
canvas.PushClip(AdornedVisual._combinedTransformedClipBounds);
}
var transform = GlobalTransformMatrix;
- canvas.PostTransform = MatrixUtils.ToMatrix(transform);
+ canvas.PostTransform = transform;
canvas.Transform = Matrix.Identity;
if (Effect != null)
@@ -71,7 +71,7 @@ namespace Avalonia.Rendering.Composition.Server
RenderCore(canvas, currentTransformedClip);
// Hack to force invalidation of SKMatrix
- canvas.PostTransform = MatrixUtils.ToMatrix(transform);
+ canvas.PostTransform = transform;
canvas.Transform = Matrix.Identity;
if (OpacityMaskBrush != null)
@@ -106,8 +106,8 @@ namespace Avalonia.Rendering.Composition.Server
return ref _readback2;
}
- public Matrix4x4 CombinedTransformMatrix { get; private set; } = Matrix4x4.Identity;
- public Matrix4x4 GlobalTransformMatrix { get; private set; }
+ public Matrix CombinedTransformMatrix { get; private set; } = Matrix.Identity;
+ public Matrix GlobalTransformMatrix { get; private set; }
public record struct UpdateResult(Rect? Bounds, bool InvalidatedOld, bool InvalidatedNew)
{
@@ -134,12 +134,12 @@ namespace Avalonia.Rendering.Composition.Server
{
CombinedTransformMatrix = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint,
// HACK: Ignore RenderTransform set by the adorner layer
- AdornedVisual != null ? Matrix4x4.Identity : TransformMatrix,
+ AdornedVisual != null ? Matrix.Identity : TransformMatrix,
Scale, RotationAngle, Orientation, Offset);
_combinedTransformDirty = false;
}
- var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix4x4.Identity;
+ var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix.Identity;
var newTransform = CombinedTransformMatrix * parentTransform;
@@ -148,7 +148,7 @@ namespace Avalonia.Rendering.Composition.Server
if (GlobalTransformMatrix != newTransform)
{
_isBackface = Vector3.Transform(
- new Vector3(0, 0, float.PositiveInfinity), GlobalTransformMatrix).Z <= 0;
+ new Vector3(0, 0, float.PositiveInfinity), MatrixUtils.ToMatrix4x4(GlobalTransformMatrix)).Z <= 0;
positionChanged = true;
}
@@ -179,14 +179,14 @@ namespace Avalonia.Rendering.Composition.Server
TransformedOwnContentBounds = default;
else
TransformedOwnContentBounds =
- ownBounds.TransformToAABB(MatrixUtils.ToMatrix(GlobalTransformMatrix));
+ ownBounds.TransformToAABB(GlobalTransformMatrix);
}
if (_clipSizeDirty || positionChanged)
{
_transformedClipBounds = ClipToBounds
? new Rect(new Size(Size.X, Size.Y))
- .TransformToAABB(MatrixUtils.ToMatrix(GlobalTransformMatrix))
+ .TransformToAABB(GlobalTransformMatrix)
: null;
_clipSizeDirty = false;
@@ -249,7 +249,7 @@ namespace Avalonia.Rendering.Composition.Server
///
public struct ReadbackData
{
- public Matrix4x4 Matrix;
+ public Matrix Matrix;
public ulong Revision;
public long TargetId;
public bool Visible;
diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs
index 6d6818256a..f4f1f3e8d9 100644
--- a/src/Avalonia.Base/Rendering/Composition/Visual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Visual.cs
@@ -33,7 +33,7 @@ namespace Avalonia.Rendering.Composition
}
}
- internal Matrix4x4? TryGetServerGlobalTransform()
+ internal Matrix? TryGetServerGlobalTransform()
{
if (Root == null)
return null;
diff --git a/src/Avalonia.Base/Rendering/RenderLoop.cs b/src/Avalonia.Base/Rendering/RenderLoop.cs
index aaecf5deaf..80410ba181 100644
--- a/src/Avalonia.Base/Rendering/RenderLoop.cs
+++ b/src/Avalonia.Base/Rendering/RenderLoop.cs
@@ -42,7 +42,6 @@ namespace Avalonia.Rendering
/// Initializes a new instance of the class.
///
/// The render timer.
- /// The UI thread dispatcher.
public RenderLoop(IRenderTimer timer)
{
_timer = timer;
diff --git a/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs b/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs
index 8bdcec2e53..8fe0bb2537 100644
--- a/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs
+++ b/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs
@@ -1,6 +1,4 @@
-#nullable enable
-using System;
-using Avalonia.LogicalTree;
+using Avalonia.LogicalTree;
namespace Avalonia.Styling.Activators
{
@@ -14,7 +12,7 @@ namespace Avalonia.Styling.Activators
private readonly int _step;
private readonly int _offset;
private readonly bool _reversed;
- private int _index = -1;
+ private int? _index;
public NthChildActivator(
ILogical control,
@@ -30,7 +28,7 @@ namespace Avalonia.Styling.Activators
protected override bool EvaluateIsActive()
{
- var index = _index >= 0 ? _index : _provider.GetChildIndex(_control);
+ var index = _index ?? _provider.GetChildIndex(_control);
return NthChildSelector.Evaluate(index, _provider, _step, _offset, _reversed).IsMatch;
}
@@ -50,8 +48,7 @@ namespace Avalonia.Styling.Activators
// 1. Subscribed child index was changed
// 2. Child indexes were reset
// 3. We're a reversed (nth-last-child) selector and total count has changed
- if ((e.Child == _control || e.Action == ChildIndexChangedAction.ChildIndexesReset) ||
- (_reversed && e.Action == ChildIndexChangedAction.TotalCountChanged))
+ switch (e.Action)
{
// We're using the _index field to pass the index of the child to EvaluateIsActive
// *only* when the active state is re-evaluated via this event handler. The docs
@@ -65,16 +62,17 @@ namespace Avalonia.Styling.Activators
// IChildIndexProvider.GetChildIndex. This is because this event can be fired during
// the process of realizing an element of a virtualized list; in this case calling
// GetChildIndex may not return the correct index as the element isn't yet realized.
- _index = e.Index;
- ReevaluateIsActive();
- _index = -1;
+ case ChildIndexChangedAction.ChildIndexChanged when e.Child == _control:
+ _index = e.Index;
+ ReevaluateIsActive();
+ _index = null;
+ break;
+ case ChildIndexChangedAction.ChildIndexesReset:
+ case ChildIndexChangedAction.TotalCountChanged when _reversed:
+ _index = null;
+ ReevaluateIsActive();
+ break;
}
}
-
- private void TotalCountChanged(object? sender, EventArgs e)
- {
- if (_reversed)
- ReevaluateIsActive();
- }
}
}
diff --git a/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs b/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs
index 06848a7524..3dbf927b86 100644
--- a/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs
+++ b/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs
@@ -6,9 +6,14 @@ namespace Avalonia.Styling.Activators
internal abstract class StyleActivatorBase : IStyleActivator
{
private IStyleActivatorSink? _sink;
- private bool _value;
+ private bool? _value;
- public bool GetIsActive() => _value = EvaluateIsActive();
+ public bool GetIsActive()
+ {
+ var value = EvaluateIsActive();
+ _value ??= value;
+ return value;
+ }
public bool IsSubscribed => _sink is not null;
@@ -63,7 +68,7 @@ namespace Avalonia.Styling.Activators
///
protected bool ReevaluateIsActive()
{
- var value = EvaluateIsActive();
+ var value = GetIsActive();
if (value != _value)
{
diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
index bb1663eac0..7d586e1cbd 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
@@ -581,11 +581,6 @@ public partial class Dispatcher
///
/// A Func<Task<TResult>> delegate to invoke through the dispatcher.
///
- ///
- /// The priority that determines in what order the specified
- /// callback is invoked relative to the other pending operations
- /// in the Dispatcher.
- ///
///
/// An task that completes after the task returned from callback finishes
///
diff --git a/src/Avalonia.Base/Vector.cs b/src/Avalonia.Base/Vector.cs
index 166ae6b93b..15722901a6 100644
--- a/src/Avalonia.Base/Vector.cs
+++ b/src/Avalonia.Base/Vector.cs
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
+using System.Numerics;
#if !BUILDTASK
using Avalonia.Animation.Animators;
#endif
@@ -353,5 +354,52 @@ namespace Avalonia
x = this._x;
y = this._y;
}
+
+ internal Vector2 ToVector2() => new Vector2((float)X, (float)Y);
+
+ internal Vector(Vector2 v) : this(v.X, v.Y)
+ {
+
+ }
+
+ ///
+ /// Returns a vector whose elements are the absolute values of each of the specified vector's elements.
+ ///
+ ///
+ public Vector Abs() => new(Math.Abs(X), Math.Abs(Y));
+
+ ///
+ /// Restricts a vector between a minimum and a maximum value.
+ ///
+ public static Vector Clamp(Vector value, Vector min, Vector max) =>
+ Min(Max(value, min), max);
+
+ ///
+ /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors
+ ///
+ public static Vector Max(Vector left, Vector right) =>
+ new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y));
+
+ ///
+ /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors
+ ///
+ public static Vector Min(Vector left, Vector right) =>
+ new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y));
+
+ ///
+ /// Computes the Euclidean distance between the two given points.
+ ///
+ public static double Distance(Vector value1, Vector value2) => Math.Sqrt(DistanceSquared(value1, value2));
+
+ ///
+ /// Returns the Euclidean distance squared between two specified points
+ ///
+ public static double DistanceSquared(Vector value1, Vector value2)
+ {
+ var difference = value1 - value2;
+ return Dot(difference, difference);
+ }
+
+ public static implicit operator Vector(Vector2 v) => new(v);
}
}
diff --git a/src/Avalonia.Base/Vector3D.cs b/src/Avalonia.Base/Vector3D.cs
new file mode 100644
index 0000000000..ec6463c987
--- /dev/null
+++ b/src/Avalonia.Base/Vector3D.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Globalization;
+using System.Numerics;
+using Avalonia.Rendering.Composition.Expressions;
+using Avalonia.Utilities;
+
+namespace Avalonia;
+
+public readonly record struct Vector3D(double X, double Y, double Z)
+{
+ ///
+ /// Parses a string.
+ ///
+ /// The string.
+ /// The .
+ public static Vector3D Parse(string s)
+ {
+ using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Vector."))
+ {
+ return new Vector3D(
+ tokenizer.ReadDouble(),
+ tokenizer.ReadDouble(),
+ tokenizer.ReadDouble()
+ );
+ }
+ }
+
+ internal Vector3 ToVector3() => new Vector3((float)X, (float)Y, (float)Z);
+
+ internal Vector3D(Vector3 v) : this(v.X, v.Y, v.Z)
+ {
+
+ }
+
+ public static implicit operator Vector3D(Vector3 vector) => new(vector);
+
+ ///
+ /// Calculates the dot product of two vectors.
+ ///
+ public static double Dot(Vector3D vector1, Vector3D vector2) =>
+ (vector1.X * vector2.X)
+ + (vector1.Y * vector2.Y)
+ + (vector1.Z * vector2.Z);
+
+ ///
+ /// Adds the second to the first vector
+ ///
+ public static Vector3D Add(Vector3D left, Vector3D right) =>
+ new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
+
+ ///
+ /// Adds the second to the first vector
+ ///
+ public static Vector3D operator +(Vector3D left, Vector3D right) => Add(left, right);
+
+ ///
+ /// Subtracts the second from the first vector
+ ///
+ public static Vector3D Substract(Vector3D left, Vector3D right) =>
+ new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
+
+ ///
+ /// Subtracts the second from the first vector
+ ///
+ public static Vector3D operator -(Vector3D left, Vector3D right) => Substract(left, right);
+
+ ///
+ /// Negates the vector
+ ///
+ public static Vector3D operator -(Vector3D v) => new(-v.X, -v.Y, -v.Z);
+
+ ///
+ /// Multiplies the first vector by the second.
+ ///
+ public static Vector3D Multiply(Vector3D left, Vector3D right) =>
+ new(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
+
+ ///
+ /// Multiplies the vector by the given scalar.
+ ///
+ public static Vector3D Multiply(Vector3D left, double right) =>
+ new(left.X * right, left.Y * right, left.Z * right);
+
+ ///
+ /// Multiplies the vector by the given scalar.
+ ///
+ public static Vector3D operator *(Vector3D left, double right) => Multiply(left, right);
+
+ ///
+ /// Divides the first vector by the second.
+ ///
+ public static Vector3D Divide(Vector3D left, Vector3D right) =>
+ new(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
+
+ ///
+ /// Divides the vector by the given scalar.
+ ///
+ public static Vector3D Divide(Vector3D left, double right) =>
+ new(left.X / right, left.Y / right, left.Z / right);
+
+ /// Returns a vector whose elements are the absolute values of each of the specified vector's elements.
+ public Vector3D Abs() => new(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
+
+ ///
+ /// Restricts a vector between a minimum and a maximum value.
+ ///
+ public static Vector3D Clamp(Vector3D value, Vector3D min, Vector3D max) =>
+ Min(Max(value, min), max);
+
+ ///
+ /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors
+ ///
+ public static Vector3D Max(Vector3D left, Vector3D right) =>
+ new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y), Math.Max(left.Z, right.Z));
+
+ ///
+ /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors
+ ///
+ public static Vector3D Min(Vector3D left, Vector3D right) =>
+ new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y), Math.Min(left.Z, right.Z));
+
+ ///
+ /// Length of the vector.
+ ///
+ public double Length => Math.Sqrt(Dot(this, this));
+
+ ///
+ /// Returns a normalized version of this vector.
+ ///
+ public static Vector3D Normalize(Vector3D value) => Divide(value, value.Length);
+
+ ///
+ /// Computes the squared Euclidean distance between the two given points.
+ ///
+ public static double DistanceSquared(Vector3D value1, Vector3D value2)
+ {
+ var difference = Vector3D.Substract(value1, value2);
+ return Dot(difference, difference);
+ }
+
+ ///
+ /// Computes the Euclidean distance between the two given points.
+ ///
+ public static double Distance(Vector3D value1, Vector3D value2) => Math.Sqrt(DistanceSquared(value1, value2));
+}
\ No newline at end of file
diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml
index 97b2686584..05aea3808e 100644
--- a/src/Avalonia.Base/composition-schema.xml
+++ b/src/Avalonia.Base/composition-schema.xml
@@ -21,14 +21,14 @@
-
-
-
-
+
+
+
+
-
-
+
+
@@ -57,10 +57,13 @@
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
index 2dde18a523..3757f685bf 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
@@ -40,8 +40,6 @@
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
diff --git a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml
index d335e6f4b9..830198f6d1 100644
--- a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml
+++ b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml
@@ -1,22 +1,25 @@
-
-
-
- 14
- 14
- 24
- 2,2,6,1
- 32
- 24
- 0,1,0,2
- 0,1,0,2
- 9,0,0,1
- 10,0,30,0
- 24
- 12,1,0,3
- 32
-
-
+
+ 14
+ 14
+ 24
+ 32
+ 4, 2
+ 4,2
+ 24
+ 11,5,11,7
+ 4 4 8 4
+ 5, 2
+ 24
+ 0,1,0,2
+ 0,1,0,2
+ 9,0,0,1
+ 10,0,30,0
+ 24
+ 12,1,0,3
+ 32
+ 28
+ 6, 0
+ 6,4
+
diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml
index c84772aa61..f4467e40ce 100644
--- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml
+++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml
@@ -18,7 +18,7 @@
-
+
diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
index 5af22dbd1d..378041356a 100644
--- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
+++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
@@ -16,9 +16,10 @@ namespace Avalonia.Themes.Fluent
///
/// Includes the fluent theme in an application.
///
- public class FluentTheme : Styles
+ public class FluentTheme : Styles, IResourceNode
{
- private readonly Styles _compactStyles;
+ private readonly ResourceDictionary _compactStyles;
+ private DensityStyle _densityStyle;
///
/// Initializes a new instance of the class.
@@ -28,9 +29,7 @@ namespace Avalonia.Themes.Fluent
{
AvaloniaXamlLoader.Load(sp, this);
- _compactStyles = (Styles)GetAndRemove("CompactStyles");
-
- EnsureCompactStyles();
+ _compactStyles = (ResourceDictionary)GetAndRemove("CompactStyles");
Palettes = Resources.MergedDictionaries.OfType().FirstOrDefault()
?? throw new InvalidOperationException("FluentTheme was initialized with missing ColorPaletteResourcesCollection.");
@@ -43,17 +42,17 @@ namespace Avalonia.Themes.Fluent
return val;
}
}
-
- public static readonly StyledProperty DensityStyleProperty =
- AvaloniaProperty.Register(nameof(DensityStyle));
+
+ public static readonly DirectProperty DensityStyleProperty = AvaloniaProperty.RegisterDirect(
+ nameof(DensityStyle), o => o.DensityStyle, (o, v) => o.DensityStyle = v);
///
/// Gets or sets the density style of the fluent theme (normal, compact).
///
public DensityStyle DensityStyle
{
- get => GetValue(DensityStyleProperty);
- set => SetValue(DensityStyleProperty, value);
+ get => _densityStyle;
+ set => SetAndRaise(DensityStyleProperty, ref _densityStyle, value);
}
public IDictionary Palettes { get; }
@@ -64,20 +63,20 @@ namespace Avalonia.Themes.Fluent
if (change.Property == DensityStyleProperty)
{
- EnsureCompactStyles();
+ Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}
- private void EnsureCompactStyles()
+ bool IResourceNode.TryGetResource(object key, ThemeVariant? theme, out object? value)
{
- if (DensityStyle == DensityStyle.Compact)
+ // DensityStyle dictionary should be checked first
+ if (_densityStyle == DensityStyle.Compact
+ && _compactStyles.TryGetResource(key, theme, out value))
{
- Add(_compactStyles);
- }
- else
- {
- Remove(_compactStyles);
+ return true;
}
+
+ return base.TryGetResource(key, theme, out value);
}
}
}
diff --git a/src/Avalonia.Themes.Simple/Controls/ItemsControl.xaml b/src/Avalonia.Themes.Simple/Controls/ItemsControl.xaml
index ba6b37fd90..e4f4bbc4a2 100644
--- a/src/Avalonia.Themes.Simple/Controls/ItemsControl.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/ItemsControl.xaml
@@ -11,8 +11,6 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
diff --git a/src/Avalonia.Themes.Simple/Controls/ListBox.xaml b/src/Avalonia.Themes.Simple/Controls/ListBox.xaml
index eeefa498a7..73c27db756 100644
--- a/src/Avalonia.Themes.Simple/Controls/ListBox.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/ListBox.xaml
@@ -27,8 +27,6 @@
HorizontalSnapPointsType="{TemplateBinding (ScrollViewer.HorizontalSnapPointsType)}">
diff --git a/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml b/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml
index ceccdf56c7..5f532df848 100644
--- a/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml
@@ -13,11 +13,10 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
- Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
- SendDataRequest(IntPtr format)
{
- if (_requestedDataTcs == null || _requestedFormatsTcs.Task.IsCompleted)
+ if (_requestedDataTcs == null || _requestedDataTcs.Task.IsCompleted)
_requestedDataTcs = new TaskCompletionSource