diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index a2dedd7340..e241f953e7 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -35,9 +35,9 @@ namespace Avalonia.Controls
public Action WindowingSubsystemInitializer { get; private set; }
///
- /// Gets or sets the name of the windowing subsystem to use.
+ /// Gets the name of the currently selected windowing subsystem.
///
- public string WindowingSubsystemName { get; set; }
+ public string WindowingSubsystemName { get; private set; }
///
/// Gets or sets a method to call the initialize the windowing subsystem.
@@ -45,9 +45,9 @@ namespace Avalonia.Controls
public Action RenderingSubsystemInitializer { get; private set; }
///
- /// Gets or sets the name of the rendering subsystem to use.
+ /// Gets the name of the currently selected rendering subsystem.
///
- public string RenderingSubsystemName { get; set; }
+ public string RenderingSubsystemName { get; private set; }
///
/// Gets or sets a method to call after the is setup.
@@ -141,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 = "")
{
WindowingSubsystemInitializer = initializer;
+ WindowingSubsystemName = name;
return Self;
}
@@ -152,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 = "")
{
RenderingSubsystemInitializer = initializer;
+ RenderingSubsystemName = name;
return Self;
}
@@ -188,14 +190,14 @@ namespace Avalonia.Controls
{
var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService().GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes()
- where attribute.RequiredWindowingSubsystem == ""
- || attribute.RequiredWindowingSubsystem == WindowingSubsystemName
- where attribute.RequiredRenderingSubsystem == ""
- || attribute.RequiredRenderingSubsystem == RenderingSubsystemName
+ 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.RequiredWindowingSubsystem.Length descending
- orderby export.RequiredRenderingSubsystem.Length descending
+ 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
diff --git a/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs b/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
index 50e39252e4..5a34c5c0e1 100644
--- a/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
+++ b/src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
@@ -2,6 +2,44 @@
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
{
@@ -14,7 +52,7 @@ namespace Avalonia.Platform
public string Name { get; private set; }
public Type ModuleType { get; private set; }
- public string RequiredWindowingSubsystem { get; set; } = "";
- public string RequiredRenderingSubsystem { get; set; } = "";
+ public string ForWindowingSubsystem { get; set; } = "";
+ public string ForRenderingSubsystem { get; set; } = "";
}
}
diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
index 157fb7acd9..3510539bc7 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
+++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
@@ -43,12 +43,12 @@ namespace Avalonia
select attribute).First();
UseWindowingSubsystem(() => windowingSubsystemAttribute.InitializationType
- .GetRuntimeMethod(windowingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null));
- WindowingSubsystemName = windowingSubsystemAttribute.Name;
+ .GetRuntimeMethod(windowingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),
+ windowingSubsystemAttribute.Name);
UseRenderingSubsystem(() => renderingSubsystemAttribute.InitializationType
- .GetRuntimeMethod(renderingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null));
- RenderingSubsystemName = renderingSubsystemAttribute.Name;
+ .GetRuntimeMethod(renderingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),
+ renderingSubsystemAttribute.Name);
return this;
}
diff --git a/src/Gtk/Avalonia.Cairo/CairoPlatform.cs b/src/Gtk/Avalonia.Cairo/CairoPlatform.cs
index c97a95a1ed..1e6eef103b 100644
--- a/src/Gtk/Avalonia.Cairo/CairoPlatform.cs
+++ b/src/Gtk/Avalonia.Cairo/CairoPlatform.cs
@@ -14,8 +14,7 @@ namespace Avalonia
{
public static T UseCairo(this T builder) where T : AppBuilderBase, new()
{
- builder.UseRenderingSubsystem(Cairo.CairoPlatform.Initialize);
- builder.RenderingSubsystemName = "Cairo";
+ builder.UseRenderingSubsystem(Cairo.CairoPlatform.Initialize, "Cairo");
return builder;
}
}
diff --git a/src/Gtk/Avalonia.Gtk/GtkPlatform.cs b/src/Gtk/Avalonia.Gtk/GtkPlatform.cs
index 15c16b3c2c..e79c51915d 100644
--- a/src/Gtk/Avalonia.Gtk/GtkPlatform.cs
+++ b/src/Gtk/Avalonia.Gtk/GtkPlatform.cs
@@ -16,8 +16,7 @@ namespace Avalonia
{
public static T UseGtk(this T builder) where T : AppBuilderBase, new()
{
- builder.UseWindowingSubsystem(Gtk.GtkPlatform.Initialize);
- builder.WindowingSubsystemName = "Gtk";
+ builder.UseWindowingSubsystem(Gtk.GtkPlatform.Initialize, "Gtk");
return builder;
}
}
diff --git a/src/Skia/Avalonia.Skia/SkiaPlatform.cs b/src/Skia/Avalonia.Skia/SkiaPlatform.cs
index a49bb8bb13..001970fa36 100644
--- a/src/Skia/Avalonia.Skia/SkiaPlatform.cs
+++ b/src/Skia/Avalonia.Skia/SkiaPlatform.cs
@@ -10,8 +10,7 @@ namespace Avalonia
{
public static T UseSkia(this T builder) where T : AppBuilderBase, new()
{
- builder.UseRenderingSubsystem(Skia.SkiaPlatform.Initialize);
- builder.RenderingSubsystemName = "Skia";
+ 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 22d4a498cc..7222976a33 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -14,8 +14,7 @@ namespace Avalonia
{
public static T UseDirect2D1(this T builder) where T : AppBuilderBase, new()
{
- builder.UseRenderingSubsystem(Direct2D1.Direct2D1Platform.Initialize);
- builder.RenderingSubsystemName = "Direct2D1";
+ builder.UseRenderingSubsystem(Direct2D1.Direct2D1Platform.Initialize, "Direct2D1");
return builder;
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index 6b5f55cb47..efd5c7a184 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -23,8 +23,7 @@ namespace Avalonia
{
public static T UseWin32(this T builder) where T : AppBuilderBase, new()
{
- builder.UseWindowingSubsystem(Win32.Win32Platform.Initialize);
- builder.WindowingSubsystemName = "Win32";
+ 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 4a25b7d374..1b69735221 100644
--- a/src/iOS/Avalonia.iOS/iOSPlatform.cs
+++ b/src/iOS/Avalonia.iOS/iOSPlatform.cs
@@ -15,8 +15,7 @@ namespace Avalonia
{
public static T UseiOS(this T builder) where T : AppBuilderBase, new()
{
- builder.UseWindowingSubsystem(iOSPlatform.Initialize);
- builder.WindowingSubsystemName = "iOS";
+ builder.UseWindowingSubsystem(iOSPlatform.Initialize, "iOS");
return builder;
}
diff --git a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
index d426a7edb3..9de97b41cc 100644
--- a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
@@ -8,8 +8,8 @@ using Avalonia.Controls.UnitTests;
using Avalonia.Platform;
[assembly: ExportAvaloniaModule("DefaultModule", typeof(AppBuilderTests.DefaultModule))]
-[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.Direct2DModule), RequiredRenderingSubsystem = "Direct2D1")]
-[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.SkiaModule), RequiredRenderingSubsystem = "Skia")]
+[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.Direct2DModule), ForRenderingSubsystem = "Direct2D1")]
+[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.SkiaModule), ForRenderingSubsystem = "Skia")]
[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.DefaultRenderingModule))]
@@ -82,8 +82,7 @@ namespace Avalonia.Controls.UnitTests
ResetModuleLoadStates();
var builder = AppBuilder.Configure()
.UseWindowingSubsystem(() => { })
- .UseRenderingSubsystem(() => { });
- builder.RenderingSubsystemName = "Direct2D1";
+ .UseRenderingSubsystem(() => { }, "Direct2D1");
builder.UseAvaloniaModules().SetupWithoutStarting();
Assert.False(DefaultRenderingModule.IsLoaded);
Assert.True(Direct2DModule.IsLoaded);
@@ -92,8 +91,7 @@ namespace Avalonia.Controls.UnitTests
ResetModuleLoadStates();
builder = AppBuilder.Configure()
.UseWindowingSubsystem(() => { })
- .UseRenderingSubsystem(() => { });
- builder.RenderingSubsystemName = "Skia";
+ .UseRenderingSubsystem(() => { }, "Skia");
builder.UseAvaloniaModules().SetupWithoutStarting();
Assert.False(DefaultRenderingModule.IsLoaded);
Assert.False(Direct2DModule.IsLoaded);
@@ -109,8 +107,7 @@ namespace Avalonia.Controls.UnitTests
ResetModuleLoadStates();
var builder = AppBuilder.Configure()
.UseWindowingSubsystem(() => { })
- .UseRenderingSubsystem(() => { });
- builder.RenderingSubsystemName = "Cairo";
+ .UseRenderingSubsystem(() => { }, "Cairo");
builder.UseAvaloniaModules().SetupWithoutStarting();
Assert.True(DefaultRenderingModule.IsLoaded);
Assert.False(Direct2DModule.IsLoaded);