diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index 5afbb444ee..e241f953e7 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -3,6 +3,7 @@
using System;
using System.Reflection;
+using System.Linq;
using Avalonia.Platform;
namespace Avalonia.Controls
@@ -21,33 +22,48 @@ namespace Avalonia.Controls
///
/// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader)
///
- public Action RuntimePlatformServices { get; set; }
+ public Action RuntimePlatformServicesInitializer { get; private set; }
///
/// Gets or sets the instance being initialized.
///
- public Application Instance { get; set; }
+ public Application Instance { get; protected set; }
///
/// Gets or sets a method to call the initialize the windowing subsystem.
///
- public Action WindowingSubsystem { get; set; }
+ public Action WindowingSubsystemInitializer { get; private set; }
+
+ ///
+ /// Gets the name of the currently selected windowing subsystem.
+ ///
+ public string WindowingSubsystemName { get; private set; }
///
/// Gets or sets a method to call the initialize the windowing subsystem.
///
- public Action RenderingSubsystem { get; set; }
+ public Action RenderingSubsystemInitializer { get; private set; }
+
+ ///
+ /// Gets the name of the currently selected rendering subsystem.
+ ///
+ public string RenderingSubsystemName { get; private set; }
+
+ ///
+ /// Gets or sets a method to call after the is setup.
+ ///
+ public Action AfterSetupCallback { get; private set; } = builder => { };
///
/// Gets or sets a method to call before is called on the
/// .
///
- public Action BeforeStartCallback { get; set; }
+ public Action BeforeStartCallback { get; private set; } = builder => { };
protected AppBuilderBase(IRuntimePlatform platform, Action platformSevices)
{
RuntimePlatform = platform;
- RuntimePlatformServices = platformSevices;
+ RuntimePlatformServicesInitializer = platformSevices;
}
///
@@ -85,7 +101,13 @@ namespace Avalonia.Controls
/// An instance.
public TAppBuilder BeforeStarting(Action callback)
{
- BeforeStartCallback = callback;
+ BeforeStartCallback = (Action)Delegate.Combine(BeforeStartCallback, callback);
+ return Self;
+ }
+
+ public TAppBuilder AfterSetup(Action callback)
+ {
+ AfterSetupCallback = (Action)Delegate.Combine(AfterSetupCallback, callback);
return Self;
}
@@ -97,7 +119,7 @@ namespace Avalonia.Controls
where TMainWindow : Window, new()
{
Setup();
- BeforeStartCallback?.Invoke(Self);
+ BeforeStartCallback(Self);
var window = new TMainWindow();
window.Show();
@@ -119,9 +141,10 @@ namespace Avalonia.Controls
///
/// The method to call to initialize the windowing subsystem.
/// An instance.
- public TAppBuilder UseWindowingSubsystem(Action initializer)
+ public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "")
{
- WindowingSubsystem = initializer;
+ WindowingSubsystemInitializer = initializer;
+ WindowingSubsystemName = name;
return Self;
}
@@ -130,16 +153,17 @@ namespace Avalonia.Controls
///
/// The dll in which to look for subsystem.
/// An instance.
- public TAppBuilder UseWindowingSubsystem(string dll) => UseWindowingSubsystem(GetInitializer(dll));
+ public TAppBuilder UseWindowingSubsystem(string dll) => UseWindowingSubsystem(GetInitializer(dll), dll.Replace("Avalonia.", string.Empty));
///
/// Specifies a rendering subsystem to use.
///
/// The method to call to initialize the rendering subsystem.
/// An instance.
- public TAppBuilder UseRenderingSubsystem(Action initializer)
+ public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "")
{
- RenderingSubsystem = initializer;
+ RenderingSubsystemInitializer = initializer;
+ RenderingSubsystemName = name;
return Self;
}
@@ -160,6 +184,28 @@ namespace Avalonia.Controls
init.Invoke(null, null);
};
+ public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules());
+
+ private void SetupAvaloniaModules()
+ {
+ var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService().GetLoadedAssemblies()
+ from attribute in assembly.GetCustomAttributes()
+ where attribute.ForWindowingSubsystem == ""
+ || attribute.ForWindowingSubsystem == WindowingSubsystemName
+ where attribute.ForRenderingSubsystem == ""
+ || attribute.ForRenderingSubsystem == RenderingSubsystemName
+ group attribute by attribute.Name into exports
+ select (from export in exports
+ orderby export.ForWindowingSubsystem.Length descending
+ orderby export.ForRenderingSubsystem.Length descending
+ select export).First().ModuleType into moduleType
+ select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors
+ where constructor.GetParameters().Length == 0 && !constructor.IsStatic
+ select constructor).Single() into constructor
+ select (Action)(() => constructor.Invoke(new object[0]));
+ Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke();
+ }
+
///
/// Sets up the platform-speciic services for the .
///
@@ -170,26 +216,27 @@ namespace Avalonia.Controls
throw new InvalidOperationException("No App instance configured.");
}
- if (RuntimePlatformServices == null)
+ if (RuntimePlatformServicesInitializer == null)
{
throw new InvalidOperationException("No runtime platform services configured.");
}
- if (WindowingSubsystem == null)
+ if (WindowingSubsystemInitializer == null)
{
throw new InvalidOperationException("No windowing system configured.");
}
- if (RenderingSubsystem == null)
+ if (RenderingSubsystemInitializer == null)
{
throw new InvalidOperationException("No rendering system configured.");
}
Instance.RegisterServices();
- RuntimePlatformServices();
- WindowingSubsystem();
- RenderingSubsystem();
+ RuntimePlatformServicesInitializer();
+ WindowingSubsystemInitializer();
+ RenderingSubsystemInitializer();
Instance.Initialize();
+ AfterSetupCallback(Self);
}
}
}
diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj
index 336f7da2dc..7192abd620 100644
--- a/src/Avalonia.Controls/Avalonia.Controls.csproj
+++ b/src/Avalonia.Controls/Avalonia.Controls.csproj
@@ -59,6 +59,8 @@
+
+
diff --git a/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs b/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
new file mode 100644
index 0000000000..5a34c5c0e1
--- /dev/null
+++ b/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace Avalonia.Platform
+{
+ ///
+ /// Defines an "Avalonia Module", a 3rd party extension to Avalonia that can be automatically initialized by an AppBuilder instance.
+ ///
+ ///
+ /// Avalonia Modules can either be platform independent (ex default control styles provider) or dependent on a
+ /// specific windowing or rendering subsystem being used (ex native rendering speedup, subsystem-specific interop backends).
+ /// In the case of a subsystem-specific module, you can specify multiple module implementations, and also a fallback
+ /// platform-independent module if you so choose. Additionally, these different implementations can be in different assemblies.
+ /// They just need to all share the same module name.
+ ///
+ /// For example, if I had a module Foo that has a special back-end for Skia and a less performant/less user friendly back-end for
+ /// any other rendering subsystem, I would do the following:
+ ///
+ /// // In assembly FooModuleSkia.dll
+ /// [assembly:ExportAvaloniaModule("Foo", typeof(FooModuleSkia), ForRenderingSubsystem="Skia")]
+ ///
+ /// class FooModuleSkia
+ /// {
+ /// public FooModuleSkia()
+ /// {
+ /// InitializeModule();
+ /// }
+ /// }
+ ///
+ /// // In assembly FooModuleFallback.dll
+ /// [assembly:ExportAvaloniaModule("Foo", typeof(FooModuleFallback))]
+ ///
+ /// class FooModuleFallback
+ /// {
+ /// public FooModuleFallback()
+ /// {
+ /// InitializeModule();
+ /// }
+ /// }
+ ///
+ ///
+ /// The fallback module will only be initialized if the Skia-specific module is not applicable.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class ExportAvaloniaModuleAttribute : Attribute
+ {
+ public ExportAvaloniaModuleAttribute(string name, Type moduleType)
+ {
+ Name = name;
+ ModuleType = moduleType;
+ }
+
+ public string Name { get; private set; }
+ public Type ModuleType { get; private set; }
+
+ public string ForWindowingSubsystem { get; set; } = "";
+ public string ForRenderingSubsystem { get; set; } = "";
+ }
+}
diff --git a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
new file mode 100644
index 0000000000..628f73ca80
--- /dev/null
+++ b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Platform
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class ExportWindowingSubsystemAttribute : Attribute
+ {
+ public ExportWindowingSubsystemAttribute(OperatingSystemType requiredRuntimePlatform, int priority, string name, Type initializationType, string initializationMethod)
+ {
+ Name = name;
+ InitializationType = initializationType;
+ InitializationMethod = initializationMethod;
+ RequiredOS = requiredRuntimePlatform;
+ Priority = priority;
+ }
+
+ public string InitializationMethod { get; private set; }
+ public Type InitializationType { get; private set; }
+ public string Name { get; private set; }
+ public int Priority { get; private set; }
+ public OperatingSystemType RequiredOS { get; private set; }
+ }
+}
diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
index 5dc36865ee..3510539bc7 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
+++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
+using System.IO;
namespace Avalonia
{
@@ -23,18 +24,47 @@ namespace Avalonia
public AppBuilder UsePlatformDetect()
{
- var platformId = (int)Environment.OSVersion.Platform;
- if (platformId == 4 || platformId == 6)
- {
- UseRenderingSubsystem("Avalonia.Cairo");
- UseWindowingSubsystem("Avalonia.Gtk");
- }
- else
+ var os = RuntimePlatform.GetRuntimeInfo().OperatingSystem;
+
+ LoadAssembliesInDirectory();
+
+ var windowingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
+ from attribute in assembly.GetCustomAttributes()
+ where attribute.RequiredOS == os
+ orderby attribute.Priority ascending
+ select attribute).First();
+
+ var renderingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
+ from attribute in assembly.GetCustomAttributes()
+ where attribute.RequiredOS == os
+ where attribute.RequiresWindowingSubsystem == null
+ || attribute.RequiresWindowingSubsystem == windowingSubsystemAttribute.Name
+ orderby attribute.Priority ascending
+ select attribute).First();
+
+ UseWindowingSubsystem(() => windowingSubsystemAttribute.InitializationType
+ .GetRuntimeMethod(windowingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),
+ windowingSubsystemAttribute.Name);
+
+ UseRenderingSubsystem(() => renderingSubsystemAttribute.InitializationType
+ .GetRuntimeMethod(renderingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),
+ renderingSubsystemAttribute.Name);
+
+ return this;
+ }
+
+ private void LoadAssembliesInDirectory()
+ {
+ foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll"))
{
- UseRenderingSubsystem("Avalonia.Direct2D1");
- UseWindowingSubsystem("Avalonia.Win32");
+ try
+ {
+ Assembly.LoadFile(file.FullName);
+ }
+ catch (Exception)
+ {
+ }
}
- return this;
}
}
}
diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
index 55a4578f59..b58ad3af1e 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
+++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
@@ -64,6 +64,10 @@
{D2221C82-4A25-4583-9B43-D791E3F6820C}
Avalonia.Controls
+
+ {eb582467-6abb-43a1-b052-e981ba910e3a}
+ Avalonia.SceneGraph
+
{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}
Avalonia.Styling
@@ -81,4 +85,4 @@
-->
-
+
\ No newline at end of file
diff --git a/src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj b/src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj
index 25a79307e6..1f2734b2e9 100644
--- a/src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj
+++ b/src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj
@@ -100,6 +100,7 @@
+
diff --git a/src/Avalonia.SceneGraph/Platform/ExportRenderingSubsystemAttribute.cs b/src/Avalonia.SceneGraph/Platform/ExportRenderingSubsystemAttribute.cs
new file mode 100644
index 0000000000..a020bf9b1c
--- /dev/null
+++ b/src/Avalonia.SceneGraph/Platform/ExportRenderingSubsystemAttribute.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Platform
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class ExportRenderingSubsystemAttribute : Attribute
+ {
+ public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod)
+ {
+ Name = name;
+ InitializationType = initializationType;
+ InitializationMethod = initializationMethod;
+ RequiredOS = requiredOS;
+ Priority = priority;
+ }
+
+ public string InitializationMethod { get; private set; }
+ public Type InitializationType { get; private set; }
+ public string Name { get; private set; }
+ public int Priority { get; private set; }
+ public OperatingSystemType RequiredOS { get; private set; }
+ public string RequiresWindowingSubsystem { get; set; }
+ }
+}
diff --git a/src/Gtk/Avalonia.Cairo/CairoPlatform.cs b/src/Gtk/Avalonia.Cairo/CairoPlatform.cs
index 6606ed4aef..1e6eef103b 100644
--- a/src/Gtk/Avalonia.Cairo/CairoPlatform.cs
+++ b/src/Gtk/Avalonia.Cairo/CairoPlatform.cs
@@ -14,7 +14,7 @@ namespace Avalonia
{
public static T UseCairo(this T builder) where T : AppBuilderBase, new()
{
- builder.RenderingSubsystem = Avalonia.Cairo.CairoPlatform.Initialize;
+ builder.UseRenderingSubsystem(Cairo.CairoPlatform.Initialize, "Cairo");
return builder;
}
}
diff --git a/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
index cdda1132bb..63457cc1a2 100644
--- a/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
+++ b/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
@@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using Avalonia.Cairo;
+using Avalonia.Platform;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -37,3 +39,7 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+
+[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
diff --git a/src/Gtk/Avalonia.Gtk/GtkPlatform.cs b/src/Gtk/Avalonia.Gtk/GtkPlatform.cs
index 455aa67405..e79c51915d 100644
--- a/src/Gtk/Avalonia.Gtk/GtkPlatform.cs
+++ b/src/Gtk/Avalonia.Gtk/GtkPlatform.cs
@@ -16,7 +16,7 @@ namespace Avalonia
{
public static T UseGtk(this T builder) where T : AppBuilderBase, new()
{
- builder.WindowingSubsystem = Avalonia.Gtk.GtkPlatform.Initialize;
+ builder.UseWindowingSubsystem(Gtk.GtkPlatform.Initialize, "Gtk");
return builder;
}
}
diff --git a/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
index 6c970b77f6..f0e9ab4ccb 100644
--- a/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
+++ b/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
@@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using Avalonia.Gtk;
+using Avalonia.Platform;
using System.Reflection;
using System.Runtime.CompilerServices;
@@ -19,3 +21,8 @@ using System.Runtime.CompilerServices;
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
+
+[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
+
diff --git a/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs b/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
index 0afee62368..69a3f34489 100644
--- a/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
+++ b/src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
@@ -1,3 +1,5 @@
+using Avalonia.Platform;
+using Avalonia.Skia;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -34,3 +36,5 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+
+[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
diff --git a/src/Skia/Avalonia.Skia/SkiaPlatform.cs b/src/Skia/Avalonia.Skia/SkiaPlatform.cs
index 3f9e49d3c3..001970fa36 100644
--- a/src/Skia/Avalonia.Skia/SkiaPlatform.cs
+++ b/src/Skia/Avalonia.Skia/SkiaPlatform.cs
@@ -10,7 +10,7 @@ namespace Avalonia
{
public static T UseSkia(this T builder) where T : AppBuilderBase, new()
{
- builder.RenderingSubsystem = Avalonia.Skia.SkiaPlatform.Initialize;
+ builder.UseRenderingSubsystem(Skia.SkiaPlatform.Initialize, "Skia");
return builder;
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 10f32861e8..7222976a33 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -14,7 +14,7 @@ namespace Avalonia
{
public static T UseDirect2D1(this T builder) where T : AppBuilderBase, new()
{
- builder.RenderingSubsystem = Avalonia.Direct2D1.Direct2D1Platform.Initialize;
+ builder.UseRenderingSubsystem(Direct2D1.Direct2D1Platform.Initialize, "Direct2D1");
return builder;
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
index a4556b001f..3ab086b156 100644
--- a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
+++ b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
@@ -2,5 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Reflection;
+using Avalonia.Platform;
+using Avalonia.Direct2D1;
[assembly: AssemblyTitle("Avalonia.Direct2D1")]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize))]
+
diff --git a/src/Windows/Avalonia.Win32/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Win32/Properties/AssemblyInfo.cs
index cf7be620be..5b4d2cef23 100644
--- a/src/Windows/Avalonia.Win32/Properties/AssemblyInfo.cs
+++ b/src/Windows/Avalonia.Win32/Properties/AssemblyInfo.cs
@@ -1,6 +1,9 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using Avalonia.Platform;
+using Avalonia.Win32;
using System.Reflection;
[assembly: AssemblyTitle("Avalonia.Win32")]
+[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 1, "Win32", typeof(Win32Platform), nameof(Win32Platform.Initialize))]
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index c6971a9f65..4aa1346a98 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -23,7 +23,7 @@ namespace Avalonia
{
public static T UseWin32(this T builder) where T : AppBuilderBase, new()
{
- builder.WindowingSubsystem = Avalonia.Win32.Win32Platform.Initialize;
+ builder.UseWindowingSubsystem(Win32.Win32Platform.Initialize, "Win32");
return builder;
}
}
diff --git a/src/iOS/Avalonia.iOS/iOSPlatform.cs b/src/iOS/Avalonia.iOS/iOSPlatform.cs
index 26c7c4b913..1b69735221 100644
--- a/src/iOS/Avalonia.iOS/iOSPlatform.cs
+++ b/src/iOS/Avalonia.iOS/iOSPlatform.cs
@@ -15,7 +15,7 @@ namespace Avalonia
{
public static T UseiOS(this T builder) where T : AppBuilderBase, new()
{
- builder.WindowingSubsystem = Avalonia.iOS.iOSPlatform.Initialize;
+ builder.UseWindowingSubsystem(iOSPlatform.Initialize, "iOS");
return builder;
}
diff --git a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
new file mode 100644
index 0000000000..9de97b41cc
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+using Avalonia.Controls.UnitTests;
+using Avalonia.Platform;
+
+[assembly: ExportAvaloniaModule("DefaultModule", typeof(AppBuilderTests.DefaultModule))]
+[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.Direct2DModule), ForRenderingSubsystem = "Direct2D1")]
+[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.SkiaModule), ForRenderingSubsystem = "Skia")]
+[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.DefaultRenderingModule))]
+
+
+namespace Avalonia.Controls.UnitTests
+{
+
+ public class AppBuilderTests
+ {
+ class App : Application
+ {
+ }
+
+ public class DefaultModule
+ {
+ public static bool IsLoaded = false;
+ public DefaultModule()
+ {
+ IsLoaded = true;
+ }
+ }
+
+ public class DefaultRenderingModule
+ {
+ public static bool IsLoaded = false;
+ public DefaultRenderingModule()
+ {
+ IsLoaded = true;
+ }
+ }
+
+ public class Direct2DModule
+ {
+ public static bool IsLoaded = false;
+ public Direct2DModule()
+ {
+ IsLoaded = true;
+ }
+ }
+
+ public class SkiaModule
+ {
+ public static bool IsLoaded = false;
+ public SkiaModule()
+ {
+ IsLoaded = true;
+ }
+ }
+
+ [Fact]
+ public void LoadsDefaultModule()
+ {
+ using (AvaloniaLocator.EnterScope())
+ {
+ ResetModuleLoadStates();
+ AppBuilder.Configure()
+ .UseWindowingSubsystem(() => { })
+ .UseRenderingSubsystem(() => { })
+ .UseAvaloniaModules()
+ .SetupWithoutStarting();
+
+ Assert.True(DefaultModule.IsLoaded);
+ }
+ }
+
+ [Fact]
+ public void LoadsRenderingModuleWithMatchingRenderingSubsystem()
+ {
+ using (AvaloniaLocator.EnterScope())
+ {
+ ResetModuleLoadStates();
+ var builder = AppBuilder.Configure()
+ .UseWindowingSubsystem(() => { })
+ .UseRenderingSubsystem(() => { }, "Direct2D1");
+ builder.UseAvaloniaModules().SetupWithoutStarting();
+ Assert.False(DefaultRenderingModule.IsLoaded);
+ Assert.True(Direct2DModule.IsLoaded);
+ Assert.False(SkiaModule.IsLoaded);
+
+ ResetModuleLoadStates();
+ builder = AppBuilder.Configure()
+ .UseWindowingSubsystem(() => { })
+ .UseRenderingSubsystem(() => { }, "Skia");
+ builder.UseAvaloniaModules().SetupWithoutStarting();
+ Assert.False(DefaultRenderingModule.IsLoaded);
+ Assert.False(Direct2DModule.IsLoaded);
+ Assert.True(SkiaModule.IsLoaded);
+ }
+ }
+
+ [Fact]
+ public void LoadsRenderingModuleWithoutDependenciesWhenNoModuleMatches()
+ {
+ using (AvaloniaLocator.EnterScope())
+ {
+ ResetModuleLoadStates();
+ var builder = AppBuilder.Configure()
+ .UseWindowingSubsystem(() => { })
+ .UseRenderingSubsystem(() => { }, "Cairo");
+ builder.UseAvaloniaModules().SetupWithoutStarting();
+ Assert.True(DefaultRenderingModule.IsLoaded);
+ Assert.False(Direct2DModule.IsLoaded);
+ Assert.False(SkiaModule.IsLoaded);
+ }
+ }
+
+ private static void ResetModuleLoadStates()
+ {
+ DefaultModule.IsLoaded = false;
+ DefaultRenderingModule.IsLoaded = false;
+ Direct2DModule.IsLoaded = false;
+ SkiaModule.IsLoaded = false;
+ }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
index e46b43d90e..f9b3b4de96 100644
--- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
+++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
@@ -94,6 +94,7 @@
+
@@ -152,6 +153,10 @@
+
+ {4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}
+ Avalonia.DotNetFrameworkRuntime
+
{3e53a01a-b331-47f3-b828-4a5717e77a24}
Avalonia.Markup.Xaml
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index 13d97920ee..54338a773e 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -267,12 +267,16 @@ namespace Avalonia.Controls.UnitTests.Primitives
var globalStyles = new Mock();
globalStyles.Setup(x => x.Styles).Returns(styles);
+ var renderInterface = new Mock();
+ renderInterface.Setup(x => x.CreateRenderer(It.IsAny())).Returns(() => new Mock().Object);
+
AvaloniaLocator.CurrentMutable
.Bind().ToTransient()
.Bind().ToFunc(() => globalStyles.Object)
.Bind().ToConstant(new WindowingPlatformMock())
- .Bind().ToTransient();
-
+ .Bind().ToTransient()
+ .Bind().ToFunc(() => renderInterface.Object);
+
return result;
}
diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs
index fa2a4f9967..d322a6dbbc 100644
--- a/tests/Avalonia.UnitTests/TestServices.cs
+++ b/tests/Avalonia.UnitTests/TestServices.cs
@@ -152,7 +152,8 @@ namespace Avalonia.UnitTests
It.IsAny(),
It.IsAny()) == Mock.Of() &&
x.CreateStreamGeometry() == Mock.Of(
- y => y.Open() == Mock.Of()));
+ y => y.Open() == Mock.Of()) &&
+ x.CreateRenderer(It.IsAny()) == Mock.Of());
}
}
}