// 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 System;
using System.Reflection;
using System.Linq;
using Avalonia.Platform;
namespace Avalonia.Controls
{
///
/// Base class for initializing platform-specific services for an .
///
/// The type of the AppBuilder class itself.
public abstract class AppBuilderBase where TAppBuilder : AppBuilderBase, new()
{
///
/// Gets or sets the instance.
///
public IRuntimePlatform RuntimePlatform { get; set; }
///
/// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader)
///
public Action RuntimePlatformServicesInitializer { get; private set; }
///
/// Gets or sets the instance being initialized.
///
public Application Instance { get; protected set; }
///
/// Gets or sets a method to call the initialize the windowing subsystem.
///
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 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; private set; } = builder => { };
protected AppBuilderBase(IRuntimePlatform platform, Action platformSevices)
{
RuntimePlatform = platform;
RuntimePlatformServicesInitializer = platformSevices;
}
///
/// Begin configuring an .
///
/// The subclass of to configure.
/// An instance.
public static TAppBuilder Configure()
where TApp : Application, new()
{
return Configure(new TApp());
}
///
/// Begin configuring an .
///
/// An instance.
public static TAppBuilder Configure(Application app)
{
AvaloniaLocator.CurrentMutable.BindToSelf(app);
return new TAppBuilder()
{
Instance = app,
};
}
protected TAppBuilder Self => (TAppBuilder) this;
///
/// Registers a callback to call before is called on the
/// .
///
/// The callback.
/// An instance.
public TAppBuilder BeforeStarting(Action callback)
{
BeforeStartCallback = (Action)Delegate.Combine(BeforeStartCallback, callback);
return Self;
}
public TAppBuilder AfterSetup(Action callback)
{
AfterSetupCallback = (Action)Delegate.Combine(AfterSetupCallback, callback);
return Self;
}
///
/// Starts the application with an instance of .
///
/// The window type.
public void Start()
where TMainWindow : Window, new()
{
Setup();
BeforeStartCallback(Self);
var window = new TMainWindow();
window.Show();
Instance.Run(window);
}
///
/// Sets up the platform-specific services for the application, but does not run it.
///
///
public TAppBuilder SetupWithoutStarting()
{
Setup();
return Self;
}
///
/// Specifies a windowing subsystem to use.
///
/// The method to call to initialize the windowing subsystem.
/// The name of the windowing subsystem.
/// An instance.
public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "")
{
WindowingSubsystemInitializer = initializer;
WindowingSubsystemName = name;
return Self;
}
///
/// Specifies a windowing subsystem to use.
///
/// The dll in which to look for subsystem.
/// An instance.
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.
/// The name of the rendering subsystem.
/// An instance.
public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "")
{
RenderingSubsystemInitializer = initializer;
RenderingSubsystemName = name;
return Self;
}
///
/// Specifies a rendering subsystem to use.
///
/// The dll in which to look for subsystem.
/// An instance.
public TAppBuilder UseRenderingSubsystem(string dll) => UseRenderingSubsystem(GetInitializer(dll));
static Action GetInitializer(string assemblyName) => () =>
{
var assembly = Assembly.Load(new AssemblyName(assemblyName));
var platformClassName = assemblyName.Replace("Avalonia.", string.Empty) + "Platform";
var platformClassFullName = assemblyName + "." + platformClassName;
var platformClass = assembly.GetType(platformClassFullName);
var init = platformClass.GetRuntimeMethod("Initialize", new Type[0]);
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 .
///
private void Setup()
{
if (Instance == null)
{
throw new InvalidOperationException("No App instance configured.");
}
if (RuntimePlatformServicesInitializer == null)
{
throw new InvalidOperationException("No runtime platform services configured.");
}
if (WindowingSubsystemInitializer == null)
{
throw new InvalidOperationException("No windowing system configured.");
}
if (RenderingSubsystemInitializer == null)
{
throw new InvalidOperationException("No rendering system configured.");
}
Instance.RegisterServices();
RuntimePlatformServicesInitializer();
WindowingSubsystemInitializer();
RenderingSubsystemInitializer();
Instance.Initialize();
AfterSetupCallback(Self);
}
}
}