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