1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Core/UI - Move from Ninject to DryIoc (#754)

This commit is contained in:
RobertBeekman 2023-01-30 21:00:10 +01:00 committed by GitHub
parent 166cb888c1
commit b7e723b7e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
121 changed files with 1175 additions and 980 deletions

View File

@ -35,15 +35,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DryIoc.dll" Version="5.3.1" />
<PackageReference Include="EmbedIO" Version="3.5.0" /> <PackageReference Include="EmbedIO" Version="3.5.0" />
<PackageReference Include="HidSharp" Version="2.1.0" /> <PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="LiteDB" Version="5.0.12" /> <PackageReference Include="LiteDB" Version="5.0.12" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" /> <PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Ninject" Version="3.3.6" />
<PackageReference Include="Ninject.Extensions.ChildKernel" Version="3.3.0" />
<PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0" />
<PackageReference Include="RGB.NET.Core" Version="1.0.0" /> <PackageReference Include="RGB.NET.Core" Version="1.0.0" />
<PackageReference Include="RGB.NET.Layout" Version="1.0.0" /> <PackageReference Include="RGB.NET.Layout" Version="1.0.0" />
<PackageReference Include="RGB.NET.Presets" Version="1.0.0" /> <PackageReference Include="RGB.NET.Presets" Version="1.0.0" />

View File

@ -0,0 +1,48 @@
using System;
using System.Reflection;
using Artemis.Core.DryIoc.Factories;
using Artemis.Core.Services;
using Artemis.Storage;
using Artemis.Storage.Migrations.Interfaces;
using Artemis.Storage.Repositories.Interfaces;
using DryIoc;
namespace Artemis.Core.DryIoc;
/// <summary>
/// The main <see cref="IModule" /> of the Artemis Core that binds all services
/// </summary>
public class CoreModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
Assembly coreAssembly = typeof(IArtemisService).Assembly;
Assembly storageAssembly = typeof(IRepository).Assembly;
// Bind all services as singletons
builder.RegisterMany(new[] {coreAssembly}, type => type.IsAssignableTo<IArtemisService>(), Reuse.Singleton);
builder.RegisterMany(new[] {coreAssembly}, type => type.IsAssignableTo<IProtectedArtemisService>(), Reuse.Singleton, setup: Setup.With(condition: HasAccessToProtectedService));
// Bind storage
builder.RegisterDelegate(() => StorageManager.CreateRepository(Constants.DataFolder), Reuse.Singleton);
builder.Register<StorageMigrationService>(Reuse.Singleton);
builder.RegisterMany(new[] {storageAssembly}, type => type.IsAssignableTo<IRepository>(), Reuse.Singleton);
// Bind migrations
builder.RegisterMany(new[] { storageAssembly }, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
builder.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
builder.Register(made: Made.Of(_ => ServiceInfo.Of<IPluginSettingsFactory>(), factory => factory.CreatePluginSettings(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
builder.Register<ILoggerFactory, LoggerFactory>(Reuse.Singleton);
builder.Register(made: Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(), f => f.CreateLogger(Arg.Index<Type>(0)), r => r.Parent.ImplementationType));
}
private bool HasAccessToProtectedService(Request request)
{
// Plugin assembly locations may not be set for some reason, that case it's also not allowed >:(
return request.Parent.ImplementationType != null &&
!string.IsNullOrWhiteSpace(request.Parent.ImplementationType.Assembly.Location) &&
!request.Parent.ImplementationType.Assembly.Location.StartsWith(Constants.PluginsFolder);
}
}

View File

@ -1,17 +1,16 @@
using System; using System;
using System.IO; using System.IO;
using Ninject.Activation;
using Serilog; using Serilog;
using Serilog.Core; using Serilog.Core;
using Serilog.Events; using Serilog.Events;
namespace Artemis.Core.Ninject; namespace Artemis.Core.DryIoc.Factories;
internal class LoggerProvider : Provider<ILogger> internal class LoggerFactory : ILoggerFactory
{ {
internal static readonly LoggingLevelSwitch LoggingLevelSwitch = new(LogEventLevel.Verbose); internal static readonly LoggingLevelSwitch LoggingLevelSwitch = new(LogEventLevel.Verbose);
private static readonly ILogger Logger = new LoggerConfiguration() internal static readonly ILogger Logger = new LoggerConfiguration()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.File(Path.Combine(Constants.LogsFolder, "Artemis log-.log"), .WriteTo.File(Path.Combine(Constants.LogsFolder, "Artemis log-.log"),
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
@ -24,12 +23,10 @@ internal class LoggerProvider : Provider<ILogger>
.MinimumLevel.ControlledBy(LoggingLevelSwitch) .MinimumLevel.ControlledBy(LoggingLevelSwitch)
.CreateLogger(); .CreateLogger();
protected override ILogger CreateInstance(IContext context) /// <inheritdoc />
public ILogger CreateLogger(Type type)
{ {
Type? requestingType = context.Request.ParentContext?.Plan?.Type; return Logger.ForContext(type);
if (requestingType != null)
return Logger.ForContext(requestingType);
return Logger;
} }
} }
@ -40,4 +37,9 @@ internal class ArtemisSink : ILogEventSink
{ {
LogStore.Emit(logEvent); LogStore.Emit(logEvent);
} }
}
internal interface ILoggerFactory
{
ILogger CreateLogger(Type type);
} }

View File

@ -1,35 +1,26 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using Ninject.Activation;
namespace Artemis.Core.Ninject; namespace Artemis.Core.DryIoc.Factories;
// TODO: Investigate if this can't just be set as a constant on the plugin child kernel internal class PluginSettingsFactory : IPluginSettingsFactory
internal class PluginSettingsProvider : Provider<PluginSettings>
{ {
private static readonly List<PluginSettings> PluginSettings = new(); private static readonly List<PluginSettings> PluginSettings = new();
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly IPluginRepository _pluginRepository; private readonly IPluginRepository _pluginRepository;
public PluginSettingsProvider(IPluginRepository pluginRepository, IPluginManagementService pluginManagementService) public PluginSettingsFactory(IPluginRepository pluginRepository, IPluginManagementService pluginManagementService)
{ {
_pluginRepository = pluginRepository; _pluginRepository = pluginRepository;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
} }
protected override PluginSettings CreateInstance(IContext context) public PluginSettings CreatePluginSettings(Type type)
{ {
IRequest parentRequest = context.Request.ParentRequest; Plugin? plugin = _pluginManagementService.GetPluginByAssembly(type.Assembly);
if (parentRequest == null)
throw new ArtemisCoreException("PluginSettings couldn't be injected, failed to get the injection parent request");
// First try by PluginInfo parameter
Plugin? plugin = parentRequest.Parameters.FirstOrDefault(p => p.Name == "Plugin")?.GetValue(context, null) as Plugin;
// Fall back to assembly based detection
if (plugin == null)
plugin = _pluginManagementService.GetPluginByAssembly(parentRequest.Service.Assembly);
if (plugin == null) if (plugin == null)
throw new ArtemisCoreException("PluginSettings can only be injected with the PluginInfo parameter provided " + throw new ArtemisCoreException("PluginSettings can only be injected with the PluginInfo parameter provided " +
@ -46,4 +37,9 @@ internal class PluginSettingsProvider : Provider<PluginSettings>
return settings; return settings;
} }
} }
}
internal interface IPluginSettingsFactory
{
PluginSettings CreatePluginSettings(Type type);
} }

View File

@ -0,0 +1,15 @@
using DryIoc;
namespace Artemis.Core.DryIoc;
/**
* Represents a service module.
*/
public interface IModule
{
/// <summary>
/// Registers the services provided by the module.
/// </summary>
/// <param name="builder">The builder to register the services with.</param>
void Load(IRegistrator builder);
}

View File

@ -0,0 +1,26 @@
using System;
using System.Linq;
using Artemis.Core.Services;
using DryIoc;
namespace Artemis.Core.DryIoc;
internal class PluginModule : IModule
{
public PluginModule(Plugin plugin)
{
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
}
public Plugin Plugin { get; }
/// <inheritdoc />
public void Load(IRegistrator builder)
{
builder.RegisterInstance(Plugin, setup: Setup.With(preventDisposal: true));
// Bind plugin service interfaces, DryIoc expects at least one match when calling RegisterMany so ensure there is something to register first
if (Plugin.Assembly != null && Plugin.Assembly.GetTypes().Any(t => t.IsAssignableTo<IPluginService>()))
builder.RegisterMany(new[] {Plugin.Assembly}, type => type.IsAssignableTo<IPluginService>(), Reuse.Singleton, ifAlreadyRegistered: IfAlreadyRegistered.Keep);
}
}

View File

@ -10,6 +10,11 @@ namespace Artemis.Core;
/// </summary> /// </summary>
public class ProfileCategory : CorePropertyChanged, IStorageModel public class ProfileCategory : CorePropertyChanged, IStorageModel
{ {
/// <summary>
/// Represents an empty profile category.
/// </summary>
public static readonly ProfileCategory Empty = new("Empty", -1);
private readonly List<ProfileConfiguration> _profileConfigurations = new(); private readonly List<ProfileConfiguration> _profileConfigurations = new();
private bool _isCollapsed; private bool _isCollapsed;
private bool _isSuspended; private bool _isSuspended;

View File

@ -11,6 +11,11 @@ namespace Artemis.Core;
/// </summary> /// </summary>
public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
{ {
/// <summary>
/// Represents an empty profile.
/// </summary>
public static readonly ProfileConfiguration Empty = new(ProfileCategory.Empty, "Empty", "Empty");
private ActivationBehaviour _activationBehaviour; private ActivationBehaviour _activationBehaviour;
private bool _activationConditionMet; private bool _activationConditionMet;
private ProfileCategory _category; private ProfileCategory _category;

View File

@ -1,83 +0,0 @@
using Artemis.Core.Services;
using Artemis.Storage;
using Artemis.Storage.Migrations.Interfaces;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
using Ninject.Activation;
using Ninject.Extensions.Conventions;
using Ninject.Modules;
using Ninject.Planning.Bindings.Resolvers;
using Serilog;
namespace Artemis.Core.Ninject;
/// <summary>
/// The main <see cref="NinjectModule" /> of the Artemis Core that binds all services
/// </summary>
public class CoreModule : NinjectModule
{
/// <inheritdoc />
public override void Load()
{
if (Kernel == null)
throw new ArtemisCoreException("Failed to bind Ninject Core module, kernel is null.");
Kernel.Components.Remove<IMissingBindingResolver, SelfBindingResolver>();
// Bind all services as singletons
Kernel.Bind(x =>
{
x.FromThisAssembly()
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IArtemisService>()
.BindAllInterfaces()
.Configure(c => c.InSingletonScope());
});
// Bind all protected services as singletons
Kernel.Bind(x =>
{
x.FromThisAssembly()
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IProtectedArtemisService>()
.BindAllInterfaces()
.Configure(c => c.When(HasAccessToProtectedService).InSingletonScope());
});
Kernel.Bind<LiteRepository>().ToMethod(_ => StorageManager.CreateRepository(Constants.DataFolder)).InSingletonScope();
Kernel.Bind<StorageMigrationService>().ToSelf().InSingletonScope();
// Bind all migrations as singletons
Kernel.Bind(x =>
{
x.FromAssemblyContaining<IStorageMigration>()
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IStorageMigration>()
.BindAllInterfaces()
.Configure(c => c.InSingletonScope());
});
// Bind all repositories as singletons
Kernel.Bind(x =>
{
x.FromAssemblyContaining<IRepository>()
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IRepository>()
.BindAllInterfaces()
.Configure(c => c.InSingletonScope());
});
Kernel.Bind<PluginSettings>().ToProvider<PluginSettingsProvider>();
Kernel.Bind<ILogger>().ToProvider<LoggerProvider>();
Kernel.Bind<LoggerProvider>().ToSelf();
}
private bool HasAccessToProtectedService(IRequest r)
{
return r.ParentRequest != null && !r.ParentRequest.Service.Assembly.Location.StartsWith(Constants.PluginsFolder);
}
}

View File

@ -1,51 +0,0 @@
using System;
using Artemis.Core.Services;
using Ninject.Extensions.Conventions;
using Ninject.Modules;
using Ninject.Planning.Bindings.Resolvers;
namespace Artemis.Core.Ninject;
internal class PluginModule : NinjectModule
{
public PluginModule(Plugin plugin)
{
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
}
public Plugin Plugin { get; }
public override void Load()
{
if (Kernel == null)
throw new ArtemisCoreException("Failed to bind plugin child module, kernel is null.");
Kernel.Components.Remove<IMissingBindingResolver, SelfBindingResolver>();
Kernel.Bind<Plugin>().ToConstant(Plugin).InTransientScope();
// Bind plugin service interfaces
Kernel.Bind(x =>
{
x.From(Plugin.Assembly)
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IPluginService>()
.BindAllInterfaces()
.Configure(c => c.InSingletonScope());
});
// Plugin developers may not use an interface so bind the plugin services to themselves
// Sadly if they do both, the kernel will treat the interface and the base type as two different singletons
// perhaps we can avoid that, but I'm not sure how
Kernel.Bind(x =>
{
x.From(Plugin.Assembly)
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IPluginService>()
.BindToSelf()
.Configure(c => c.InSingletonScope());
});
}
}

View File

@ -1,25 +0,0 @@
using Artemis.Core.Services;
using Ninject;
using Ninject.Activation;
namespace Artemis.Core.Ninject;
internal class SettingsServiceProvider : Provider<ISettingsService>
{
private readonly SettingsService _instance;
public SettingsServiceProvider(IKernel kernel)
{
// This is not lazy, but the core is always going to be using this anyway
_instance = kernel.Get<SettingsService>();
}
protected override ISettingsService CreateInstance(IContext context)
{
IRequest parentRequest = context.Request.ParentRequest;
if (parentRequest == null || typeof(PluginFeature).IsAssignableFrom(parentRequest.Service))
throw new ArtemisPluginException($"SettingsService can not be injected into a plugin. Inject {nameof(PluginSettings)} instead.");
return _instance;
}
}

View File

@ -1,9 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Ninject;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog;
namespace Artemis.Core.DeviceProviders; namespace Artemis.Core.DeviceProviders;
@ -26,14 +24,7 @@ public abstract class DeviceProvider : PluginFeature
/// The RGB.NET device provider backing this Artemis device provider /// The RGB.NET device provider backing this Artemis device provider
/// </summary> /// </summary>
public IRGBDeviceProvider RgbDeviceProvider { get; } public IRGBDeviceProvider RgbDeviceProvider { get; }
/// <summary>
/// TODO: Make internal while still injecting.
/// A logger used by the device provider internally, ignore this
/// </summary>
[Inject]
public ILogger? Logger { get; set; }
/// <summary> /// <summary>
/// A boolean indicating whether this device provider detects the physical layout of connected keyboards. /// A boolean indicating whether this device provider detects the physical layout of connected keyboards.
/// <para> /// <para>

View File

@ -1,6 +1,5 @@
using System; using System;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Ninject;
namespace Artemis.Core.LayerBrushes; namespace Artemis.Core.LayerBrushes;
@ -63,7 +62,7 @@ public class LayerBrushDescriptor
if (layer == null) if (layer == null)
throw new ArgumentNullException(nameof(layer)); throw new ArgumentNullException(nameof(layer));
BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Kernel!.Get(LayerBrushType); BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Resolve(LayerBrushType);
brush.Layer = layer; brush.Layer = layer;
brush.Descriptor = this; brush.Descriptor = this;
brush.LayerBrushEntity = entity ?? new LayerBrushEntity {ProviderId = Provider.Id, BrushType = LayerBrushType.FullName}; brush.LayerBrushEntity = entity ?? new LayerBrushEntity {ProviderId = Provider.Id, BrushType = LayerBrushType.FullName};

View File

@ -1,7 +1,6 @@
using System; using System;
using Artemis.Core.LayerEffects.Placeholder; using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Ninject;
namespace Artemis.Core.LayerEffects; namespace Artemis.Core.LayerEffects;
@ -80,7 +79,7 @@ public class LayerEffectDescriptor
if (LayerEffectType == null) if (LayerEffectType == null)
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType"); throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType");
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType); BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Resolve(LayerEffectType);
effect.ProfileElement = renderElement; effect.ProfileElement = renderElement;
effect.Descriptor = this; effect.Descriptor = this;
if (entity != null) if (entity != null)

View File

@ -7,8 +7,8 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Artemis.Core.DeviceProviders; using Artemis.Core.DeviceProviders;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using DryIoc;
using McMaster.NETCore.Plugins; using McMaster.NETCore.Plugins;
using Ninject;
namespace Artemis.Core; namespace Artemis.Core;
@ -88,9 +88,9 @@ public class Plugin : CorePropertyChanged, IDisposable
public PluginBootstrapper? Bootstrapper { get; internal set; } public PluginBootstrapper? Bootstrapper { get; internal set; }
/// <summary> /// <summary>
/// The Ninject kernel of the plugin /// Gets the IOC container of the plugin, only use this for advanced IOC operations, otherwise see <see cref="Resolve"/> and <see cref="Resolve{T}"/>
/// </summary> /// </summary>
public IKernel? Kernel { get; internal set; } public IContainer? Container { get; internal set; }
/// <summary> /// <summary>
/// The PluginLoader backing this plugin /// The PluginLoader backing this plugin
@ -165,16 +165,66 @@ public class Plugin : CorePropertyChanged, IDisposable
} }
/// <summary> /// <summary>
/// Gets an instance of the specified service using the plugins dependency injection container. /// Gets an instance of the specified service using the plugins dependency injection container.
/// <para>Note: To use parameters reference Ninject and use <see cref="Kernel" /> directly.</para>
/// </summary> /// </summary>
/// <param name="arguments">Arguments to supply to the service.</param>
/// <typeparam name="T">The service to resolve.</typeparam> /// <typeparam name="T">The service to resolve.</typeparam>
/// <returns>An instance of the service.</returns> /// <returns>An instance of the service.</returns>
public T Get<T>() /// <seealso cref="Resolve"/>
public T Resolve<T>(params object?[] arguments)
{ {
if (Kernel == null) if (Container == null)
throw new ArtemisPluginException("Cannot use Get<T> before the plugin finished loading"); throw new ArtemisPluginException("Cannot use Resolve<T> before the plugin finished loading");
return Kernel.Get<T>(); return Container.Resolve<T>(args: arguments);
}
/// <summary>
/// Gets an instance of the specified service using the plugins dependency injection container.
/// </summary>
/// <param name="type">The type of service to resolve.</param>
/// <param name="arguments">Arguments to supply to the service.</param>
/// <returns>An instance of the service.</returns>
/// <seealso cref="Resolve{T}"/>
public object Resolve(Type type, params object?[] arguments)
{
if (Container == null)
throw new ArtemisPluginException("Cannot use Resolve before the plugin finished loading");
return Container.Resolve(type, args: arguments);
}
/// <summary>
/// Registers service of <typeparamref name="TService" /> type implemented by <typeparamref name="TImplementation" /> type.
/// </summary>
/// <param name="scope">The scope in which the service should live, if you are not sure leave it on singleton.</param>
/// <typeparam name="TService">The service to register.</typeparam>
/// <typeparam name="TImplementation">The implementation of the service to register.</typeparam>
public void Register<TService, TImplementation>(PluginServiceScope scope = PluginServiceScope.Singleton) where TImplementation : TService
{
IReuse reuse = scope switch
{
PluginServiceScope.Transient => Reuse.Transient,
PluginServiceScope.Singleton => Reuse.Singleton,
PluginServiceScope.Scoped => Reuse.Scoped,
_ => throw new ArgumentOutOfRangeException(nameof(scope), scope, null)
};
Container.Register<TService, TImplementation>(reuse);
}
/// <summary>
/// Registers implementation type <typeparamref name="TImplementation" /> with itself as service type.
/// </summary>
/// <param name="scope">The scope in which the service should live, if you are not sure leave it on singleton.</param>
/// <typeparam name="TImplementation">The implementation of the service to register.</typeparam>
public void Register<TImplementation>(PluginServiceScope scope = PluginServiceScope.Singleton)
{
IReuse reuse = scope switch
{
PluginServiceScope.Transient => Reuse.Transient,
PluginServiceScope.Singleton => Reuse.Singleton,
PluginServiceScope.Scoped => Reuse.Scoped,
_ => throw new ArgumentOutOfRangeException(nameof(scope), scope, null)
};
Container.Register<TImplementation>(reuse);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -218,7 +268,7 @@ public class Plugin : CorePropertyChanged, IDisposable
feature.Instance?.Dispose(); feature.Instance?.Dispose();
SetEnabled(false); SetEnabled(false);
Kernel?.Dispose(); Container?.Dispose();
PluginLoader?.Dispose(); PluginLoader?.Dispose();
GC.Collect(); GC.Collect();
@ -340,4 +390,26 @@ public class Plugin : CorePropertyChanged, IDisposable
Dispose(true); Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
}
/// <summary>
/// Represents a scope in which a plugin service is injected by the IOC container.
/// </summary>
public enum PluginServiceScope
{
/// <summary>
/// Services in this scope are never reused, a new instance is injected each time.
/// </summary>
Transient,
/// <summary>
/// Services in this scope are reused for as long as the plugin lives, the same instance is injected each time.
/// </summary>
Singleton,
/// <summary>
/// Services in this scope are reused within a container scope, this is an advanced setting you shouldn't need.
/// <para>To learn more see <a href="https://github.com/dadhi/DryIoc/blob/master/docs/DryIoc.Docs/ReuseAndScopes.md#reusescoped">the DryIoc docs</a>.</para>
/// </summary>
Scoped
} }

View File

@ -1,10 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.DeviceProviders;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.Modules;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using Humanizer; using Humanizer;
using Newtonsoft.Json; using Newtonsoft.Json;

View File

@ -1,9 +1,8 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
using Artemis.Core.DryIoc.Factories;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Core.Services;
using Ninject;
using Serilog; using Serilog;
namespace Artemis.Core; namespace Artemis.Core;
@ -11,7 +10,7 @@ namespace Artemis.Core;
/// <summary> /// <summary>
/// Represents a registration for a timed plugin update /// Represents a registration for a timed plugin update
/// </summary> /// </summary>
public class TimedUpdateRegistration : IDisposable public sealed class TimedUpdateRegistration : IDisposable
{ {
private readonly object _lock = new(); private readonly object _lock = new();
private readonly ILogger _logger; private readonly ILogger _logger;
@ -21,9 +20,7 @@ public class TimedUpdateRegistration : IDisposable
internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action<double> action, string? name) internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action<double> action, string? name)
{ {
if (CoreService.Kernel == null) _logger = LoggerFactory.Logger.ForContext<TimedUpdateRegistration>();
throw new ArtemisCoreException("Cannot create a TimedUpdateRegistration before initializing the Core");
_logger = CoreService.Kernel.Get<ILogger>();
Feature = feature; Feature = feature;
Interval = interval; Interval = interval;
@ -38,9 +35,7 @@ public class TimedUpdateRegistration : IDisposable
internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Func<double, Task> asyncAction, string? name) internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Func<double, Task> asyncAction, string? name)
{ {
if (CoreService.Kernel == null) _logger = LoggerFactory.Logger.ForContext<TimedUpdateRegistration>();
throw new ArtemisCoreException("Cannot create a TimedUpdateRegistration before initializing the Core");
_logger = CoreService.Kernel.Get<ILogger>();
Feature = feature; Feature = feature;
Interval = interval; Interval = interval;
@ -125,33 +120,13 @@ public class TimedUpdateRegistration : IDisposable
} }
/// <inheritdoc /> /// <inheritdoc />
public sealed override string ToString() public override string ToString()
{ {
if (Interval.TotalSeconds >= 1) if (Interval.TotalSeconds >= 1)
return $"{Name} ({Interval.TotalSeconds} sec)"; return $"{Name} ({Interval.TotalSeconds} sec)";
return $"{Name} ({Interval.TotalMilliseconds} ms)"; return $"{Name} ({Interval.TotalMilliseconds} ms)";
} }
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Stop();
Feature.Enabled -= FeatureOnEnabled;
Feature.Disabled -= FeatureOnDisabled;
_disposed = true;
}
}
private void TimerOnElapsed(object? sender, ElapsedEventArgs e) private void TimerOnElapsed(object? sender, ElapsedEventArgs e)
{ {
if (!Feature.IsEnabled) if (!Feature.IsEnabled)
@ -204,9 +179,12 @@ public class TimedUpdateRegistration : IDisposable
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Dispose(true); Stop();
Feature.Profiler.ClearMeasurements(ToString());
GC.SuppressFinalize(this); Feature.Enabled -= FeatureOnEnabled;
Feature.Disabled -= FeatureOnDisabled;
_disposed = true;
Feature.Profiler.ClearMeasurements(ToString());
} }
} }

View File

@ -3,11 +3,11 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Artemis.Core.Ninject; using Artemis.Core.DryIoc.Factories;
using Artemis.Core.ScriptingProviders; using Artemis.Core.ScriptingProviders;
using Artemis.Storage; using Artemis.Storage;
using DryIoc;
using HidSharp; using HidSharp;
using Ninject;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
@ -20,7 +20,6 @@ namespace Artemis.Core.Services;
/// </summary> /// </summary>
internal class CoreService : ICoreService internal class CoreService : ICoreService
{ {
internal static IKernel? Kernel;
private readonly Stopwatch _frameStopWatch; private readonly Stopwatch _frameStopWatch;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly PluginSetting<LogEventLevel> _loggingLevel; private readonly PluginSetting<LogEventLevel> _loggingLevel;
@ -36,7 +35,7 @@ internal class CoreService : ICoreService
private DateTime _lastFrameRateSample; private DateTime _lastFrameRateSample;
// ReSharper disable UnusedParameter.Local // ReSharper disable UnusedParameter.Local
public CoreService(IKernel kernel, public CoreService(IContainer container,
ILogger logger, ILogger logger,
StorageMigrationService _1, // injected to ensure migration runs early StorageMigrationService _1, // injected to ensure migration runs early
ISettingsService settingsService, ISettingsService settingsService,
@ -47,8 +46,7 @@ internal class CoreService : ICoreService
IScriptingService scriptingService, IScriptingService scriptingService,
IProcessMonitorService _2) IProcessMonitorService _2)
{ {
Kernel = kernel; Constants.CorePlugin.Container = container;
Constants.CorePlugin.Kernel = kernel;
_logger = logger; _logger = logger;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
@ -85,19 +83,19 @@ internal class CoreService : ICoreService
if (parts.Length == 2 && Enum.TryParse(typeof(LogEventLevel), parts[1], true, out object? logLevelArgument)) if (parts.Length == 2 && Enum.TryParse(typeof(LogEventLevel), parts[1], true, out object? logLevelArgument))
{ {
_logger.Information("Setting logging level to {loggingLevel} from startup argument", (LogEventLevel) logLevelArgument!); _logger.Information("Setting logging level to {loggingLevel} from startup argument", (LogEventLevel) logLevelArgument!);
LoggerProvider.LoggingLevelSwitch.MinimumLevel = (LogEventLevel) logLevelArgument; LoggerFactory.LoggingLevelSwitch.MinimumLevel = (LogEventLevel) logLevelArgument;
} }
else else
{ {
_logger.Warning("Failed to set log level from startup argument {argument}", argument); _logger.Warning("Failed to set log level from startup argument {argument}", argument);
_logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value); _logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value; LoggerFactory.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
} }
} }
else else
{ {
_logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value); _logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value; LoggerFactory.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
} }
} }
@ -195,12 +193,6 @@ internal class CoreService : ICoreService
public bool ProfileRenderingDisabled { get; set; } public bool ProfileRenderingDisabled { get; set; }
public bool IsElevated { get; set; } public bool IsElevated { get; set; }
public void Dispose()
{
// Dispose services
_pluginManagementService.Dispose();
}
public bool IsInitialized { get; set; } public bool IsInitialized { get; set; }
public void Initialize() public void Initialize()

View File

@ -5,7 +5,7 @@ namespace Artemis.Core.Services;
/// <summary> /// <summary>
/// A service that initializes the Core and manages the render loop /// A service that initializes the Core and manages the render loop
/// </summary> /// </summary>
public interface ICoreService : IArtemisService, IDisposable public interface ICoreService : IArtemisService
{ {
/// <summary> /// <summary>
/// Gets whether the or not the core has been initialized /// Gets whether the or not the core has been initialized

View File

@ -5,8 +5,8 @@ using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Artemis.Storage.Entities.Profile.Nodes; using Artemis.Storage.Entities.Profile.Nodes;
using DryIoc;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ninject;
using SkiaSharp; using SkiaSharp;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
@ -15,17 +15,17 @@ internal class NodeService : INodeService
{ {
#region Constants #region Constants
private static readonly Type TYPE_NODE = typeof(INode); private static readonly Type TypeNode = typeof(INode);
#endregion #endregion
private readonly IKernel _kernel; private readonly IContainer _container;
#region Constructors #region Constructors
public NodeService(IKernel kernel) public NodeService(IContainer container)
{ {
_kernel = kernel; _container = container;
} }
#endregion #endregion
@ -69,7 +69,7 @@ internal class NodeService : INodeService
if (plugin == null) throw new ArgumentNullException(nameof(plugin)); if (plugin == null) throw new ArgumentNullException(nameof(plugin));
if (nodeType == null) throw new ArgumentNullException(nameof(nodeType)); if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
if (!TYPE_NODE.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType)); if (!TypeNode.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>(); NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
string name = nodeAttribute?.Name ?? nodeType.Name; string name = nodeAttribute?.Name ?? nodeType.Name;
@ -106,8 +106,10 @@ internal class NodeService : INodeService
private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType) private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType)
{ {
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode"); INode node = _container.Resolve(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
if (node is Node concreteNode)
concreteNode.Container = _container;
if (entity != null) if (entity != null)
{ {
node.X = entity.X; node.X = entity.X;

View File

@ -5,19 +5,15 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.DeviceProviders; using Artemis.Core.DeviceProviders;
using Artemis.Core.Ninject; using Artemis.Core.DryIoc;
using Artemis.Storage.Entities.General; using Artemis.Storage.Entities.General;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Entities.Surface; using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using DryIoc;
using McMaster.NETCore.Plugins; using McMaster.NETCore.Plugins;
using Ninject;
using Ninject.Extensions.ChildKernel;
using Ninject.Parameters;
using Ninject.Planning.Bindings.Resolvers;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog; using Serilog;
@ -29,7 +25,7 @@ namespace Artemis.Core.Services;
internal class PluginManagementService : IPluginManagementService internal class PluginManagementService : IPluginManagementService
{ {
private readonly IDeviceRepository _deviceRepository; private readonly IDeviceRepository _deviceRepository;
private readonly IKernel _kernel; private readonly IContainer _container;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IPluginRepository _pluginRepository; private readonly IPluginRepository _pluginRepository;
private readonly List<Plugin> _plugins; private readonly List<Plugin> _plugins;
@ -37,9 +33,9 @@ internal class PluginManagementService : IPluginManagementService
private bool _disposed; private bool _disposed;
private bool _isElevated; private bool _isElevated;
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository, IDeviceRepository deviceRepository, IQueuedActionRepository queuedActionRepository) public PluginManagementService(IContainer container, ILogger logger, IPluginRepository pluginRepository, IDeviceRepository deviceRepository, IQueuedActionRepository queuedActionRepository)
{ {
_kernel = kernel; _container = container;
_logger = logger; _logger = logger;
_pluginRepository = pluginRepository; _pluginRepository = pluginRepository;
_deviceRepository = deviceRepository; _deviceRepository = deviceRepository;
@ -198,6 +194,10 @@ internal class PluginManagementService : IPluginManagementService
public void Dispose() public void Dispose()
{ {
// Disposal happens manually before container disposal but the container doesn't know that so a 2nd call will be made
if (_disposed)
return;
_disposed = true; _disposed = true;
UnloadPlugins(); UnloadPlugins();
} }
@ -340,11 +340,11 @@ internal class PluginManagementService : IPluginManagementService
configure.IsUnloadable = true; configure.IsUnloadable = true;
configure.LoadInMemory = true; configure.LoadInMemory = true;
configure.PreferSharedTypes = true; configure.PreferSharedTypes = true;
// Resolving failed, try a loaded assembly but ignoring the version // Resolving failed, try a loaded assembly but ignoring the version
configure.DefaultContext.Resolving += (context, assemblyName) => context.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name); configure.DefaultContext.Resolving += (context, assemblyName) => context.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name);
}); });
try try
{ {
plugin.Assembly = plugin.PluginLoader.LoadDefaultAssembly(); plugin.Assembly = plugin.PluginLoader.LoadDefaultAssembly();
@ -406,7 +406,7 @@ internal class PluginManagementService : IPluginManagementService
OnPluginLoaded(new PluginEventArgs(plugin)); OnPluginLoaded(new PluginEventArgs(plugin));
return plugin; return plugin;
} }
public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock) public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock)
{ {
if (!plugin.Info.IsCompatible) if (!plugin.Info.IsCompatible)
@ -431,10 +431,17 @@ internal class PluginManagementService : IPluginManagementService
if (!plugin.Info.ArePrerequisitesMet()) if (!plugin.Info.ArePrerequisitesMet())
throw new ArtemisPluginPrerequisiteException(plugin.Info, "Cannot enable a plugin whose prerequisites aren't all met"); throw new ArtemisPluginPrerequisiteException(plugin.Info, "Cannot enable a plugin whose prerequisites aren't all met");
// Create the Ninject child kernel and load the module // Create a child container for the plugin, be a bit more forgiving about concrete types
plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin)); plugin.Container = _container.CreateChild(newRules: _container.Rules.WithConcreteTypeDynamicRegistrations());
// The kernel used by Core is unforgiving about missing bindings, no need to be so hard on plugin devs try
plugin.Kernel.Components.Add<IMissingBindingResolver, SelfBindingResolver>(); {
new PluginModule(plugin).Load(plugin.Container);
}
catch (Exception e)
{
_logger.Error(e, "Failed to register plugin services for plugin {plugin}, skipping enabling", plugin);
return;
}
OnPluginEnabling(new PluginEventArgs(plugin)); OnPluginEnabling(new PluginEventArgs(plugin));
@ -446,11 +453,8 @@ internal class PluginManagementService : IPluginManagementService
{ {
try try
{ {
plugin.Kernel.Bind(featureInfo.FeatureType).ToSelf().InSingletonScope(); plugin.Container.Register(featureInfo.FeatureType, reuse: Reuse.Singleton);
PluginFeature instance = (PluginFeature) plugin.Container.Resolve(featureInfo.FeatureType);
// Include Plugin as a parameter for the PluginSettingsProvider
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureInfo.FeatureType, parameters);
// Get the PluginFeature attribute which contains extra info on the feature // Get the PluginFeature attribute which contains extra info on the feature
featureInfo.Instance = instance; featureInfo.Instance = instance;
@ -526,8 +530,8 @@ internal class PluginManagementService : IPluginManagementService
plugin.SetEnabled(false); plugin.SetEnabled(false);
plugin.Kernel?.Dispose(); plugin.Container?.Dispose();
plugin.Kernel = null; plugin.Container = null;
GC.Collect(); GC.Collect();
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();

View File

@ -9,7 +9,7 @@ using Artemis.Core.Services.Models;
using Artemis.Core.SkiaSharp; using Artemis.Core.SkiaSharp;
using Artemis.Storage.Entities.Surface; using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using Ninject; using DryIoc;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog; using Serilog;
@ -20,31 +20,36 @@ namespace Artemis.Core.Services;
/// </summary> /// </summary>
internal class RgbService : IRgbService internal class RgbService : IRgbService
{ {
private readonly IDeviceRepository _deviceRepository;
private readonly List<ArtemisDevice> _devices;
private readonly List<ArtemisDevice> _enabledDevices;
private readonly IKernel _kernel;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly IDeviceRepository _deviceRepository;
private readonly LazyEnumerable<IGraphicsContextProvider> _graphicsContextProviders;
private readonly PluginSetting<string> _preferredGraphicsContext; private readonly PluginSetting<string> _preferredGraphicsContext;
private readonly PluginSetting<double> _renderScaleSetting; private readonly PluginSetting<double> _renderScaleSetting;
private readonly ISettingsService _settingsService;
private readonly PluginSetting<int> _targetFrameRateSetting; private readonly PluginSetting<int> _targetFrameRateSetting;
private readonly List<ArtemisDevice> _devices;
private readonly List<ArtemisDevice> _enabledDevices;
private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute}; private readonly SKTextureBrush _textureBrush = new(null) {CalculationMode = RenderMode.Absolute};
private Dictionary<Led, ArtemisLed> _ledMap; private Dictionary<Led, ArtemisLed> _ledMap;
private ListLedGroup? _surfaceLedGroup; private ListLedGroup? _surfaceLedGroup;
private SKTexture? _texture; private SKTexture? _texture;
public RgbService(ILogger logger, IKernel kernel, ISettingsService settingsService, IPluginManagementService pluginManagementService, IDeviceRepository deviceRepository) public RgbService(ILogger logger,
ISettingsService settingsService,
IPluginManagementService pluginManagementService,
IDeviceRepository deviceRepository,
LazyEnumerable<IGraphicsContextProvider> graphicsContextProviders)
{ {
_logger = logger; _logger = logger;
_kernel = kernel;
_settingsService = settingsService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_deviceRepository = deviceRepository; _deviceRepository = deviceRepository;
_graphicsContextProviders = graphicsContextProviders;
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30); _targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25); _renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25);
_preferredGraphicsContext = _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software"); _preferredGraphicsContext = settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
Surface = new RGBSurface(); Surface = new RGBSurface();
Utilities.RenderScaleMultiplier = (int) (1 / _renderScaleSetting.Value); Utilities.RenderScaleMultiplier = (int) (1 / _renderScaleSetting.Value);
@ -226,7 +231,7 @@ internal class RgbService : IRgbService
{ {
_logger.Verbose("[AddDeviceProvider] Updating the LED group"); _logger.Verbose("[AddDeviceProvider] Updating the LED group");
UpdateLedGroup(); UpdateLedGroup();
_logger.Verbose("[AddDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name); _logger.Verbose("[AddDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name);
if (changedRenderPaused) if (changedRenderPaused)
SetRenderPaused(false); SetRenderPaused(false);
@ -257,7 +262,7 @@ internal class RgbService : IRgbService
{ {
_logger.Verbose("[RemoveDeviceProvider] Updating the LED group"); _logger.Verbose("[RemoveDeviceProvider] Updating the LED group");
UpdateLedGroup(); UpdateLedGroup();
_logger.Verbose("[RemoveDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name); _logger.Verbose("[RemoveDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name);
if (changedRenderPaused) if (changedRenderPaused)
SetRenderPaused(false); SetRenderPaused(false);
@ -372,7 +377,8 @@ internal class RgbService : IRgbService
return; return;
} }
List<IGraphicsContextProvider> providers = _kernel.Get<List<IGraphicsContextProvider>>();
List<IGraphicsContextProvider> providers = _graphicsContextProviders.ToList();
if (!providers.Any()) if (!providers.Any())
{ {
_logger.Warning("No graphics context provider found, defaulting to software rendering"); _logger.Warning("No graphics context provider found, defaulting to software rendering");

View File

@ -2,10 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reflection;
using Artemis.Core.ScriptingProviders; using Artemis.Core.ScriptingProviders;
using Ninject;
using Ninject.Parameters;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
@ -51,11 +48,7 @@ internal class ScriptingService : IScriptingService
if (provider == null) if (provider == null)
throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId})."); throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId}).");
script = (GlobalScript) provider.Plugin.Kernel!.Get( script = (GlobalScript) provider.Plugin.Resolve(provider.GlobalScriptType, scriptConfiguration);
provider.GlobalScriptType,
CreateScriptConstructorArgument(provider.GlobalScriptType, scriptConfiguration)
);
script.ScriptingProvider = provider; script.ScriptingProvider = provider;
script.ScriptingService = this; script.ScriptingService = this;
scriptConfiguration.Script = script; scriptConfiguration.Script = script;
@ -82,12 +75,7 @@ internal class ScriptingService : IScriptingService
if (provider == null) if (provider == null)
throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId})."); throw new ArtemisCoreException($"Can't create script instance as there is no matching scripting provider found for the script ({scriptConfiguration.ScriptingProviderId}).");
script = (ProfileScript) provider.Plugin.Kernel!.Get( script = (ProfileScript) provider.Plugin.Resolve(provider.ProfileScriptType, profile, scriptConfiguration);
provider.ProfileScriptType,
CreateScriptConstructorArgument(provider.ProfileScriptType, profile),
CreateScriptConstructorArgument(provider.ProfileScriptType, scriptConfiguration)
);
script.ScriptingProvider = provider; script.ScriptingProvider = provider;
scriptConfiguration.Script = script; scriptConfiguration.Script = script;
provider.InternalScripts.Add(script); provider.InternalScripts.Add(script);
@ -113,21 +101,6 @@ internal class ScriptingService : IScriptingService
} }
} }
private ConstructorArgument CreateScriptConstructorArgument(Type scriptType, object value)
{
// Limit to one constructor, there's no need to have more and it complicates things anyway
ConstructorInfo[] constructors = scriptType.GetConstructors();
if (constructors.Length != 1)
throw new ArtemisCoreException("Scripts must have exactly one constructor");
// Find the ScriptConfiguration parameter, it is required by the base constructor so its there for sure
ParameterInfo? configurationParameter = constructors.First().GetParameters().FirstOrDefault(p => value.GetType().IsAssignableFrom(p.ParameterType));
if (configurationParameter?.Name == null)
throw new ArtemisCoreException($"Couldn't find a valid constructor argument on {scriptType.Name} with type {value.GetType().Name}");
return new ConstructorArgument(configurationParameter.Name, value);
}
private void InitializeProfileScripts(Profile profile) private void InitializeProfileScripts(Profile profile)
{ {
// Initialize the scripts on the profile // Initialize the scripts on the profile

View File

@ -1,6 +1,5 @@
using System; using System;
using EmbedIO.WebApi; using EmbedIO.WebApi;
using Ninject;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
@ -8,7 +7,7 @@ internal class WebApiControllerRegistration<T> : WebApiControllerRegistration wh
{ {
public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T)) public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
{ {
Factory = () => feature.Plugin.Kernel!.Get<T>(); Factory = () => feature.Plugin.Resolve<T>();
} }
public Func<T> Factory { get; set; } public Func<T> Factory { get; set; }

View File

@ -1,6 +1,6 @@
using System; using System;
using EmbedIO; using EmbedIO;
using Ninject; using DryIoc;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
@ -27,7 +27,7 @@ internal class WebModuleRegistration
if (Create != null) if (Create != null)
return Create(); return Create();
if (WebModuleType != null) if (WebModuleType != null)
return (IWebModule) Feature.Plugin.Kernel!.Get(WebModuleType); return (IWebModule) Feature.Plugin.Resolve(WebModuleType);
throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :("); throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :(");
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using Artemis.Storage.Entities.Profile.Nodes; using Artemis.Storage.Entities.Profile.Nodes;
using Castle.Core.Internal;
namespace Artemis.Core; namespace Artemis.Core;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Artemis.Core.Events; using Artemis.Core.Events;
using DryIoc;
namespace Artemis.Core; namespace Artemis.Core;
@ -103,6 +104,8 @@ public abstract class Node : BreakableModel, INode
/// <inheritdoc /> /// <inheritdoc />
public override string BrokenDisplayName => Name; public override string BrokenDisplayName => Name;
internal IContainer Container { get; set; } = null!;
#endregion #endregion
#region Construtors #region Construtors

View File

@ -1,8 +1,5 @@
using System.Linq;
using System.Reflection;
using Artemis.Core; using Artemis.Core;
using Ninject; using DryIoc;
using Ninject.Parameters;
/// <summary> /// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type /// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
@ -22,26 +19,13 @@ public abstract class Node<TStorage, TViewModel> : Node<TStorage>, ICustomViewMo
{ {
} }
[Inject]
internal IKernel Kernel { get; set; } = null!;
/// <summary> /// <summary>
/// Called when a view model is required /// Called when a view model is required
/// </summary> /// </summary>
/// <param name="nodeScript"></param> /// <param name="nodeScript"></param>
public virtual TViewModel GetViewModel(NodeScript nodeScript) public virtual TViewModel GetViewModel(NodeScript nodeScript)
{ {
// Limit to one constructor, there's no need to have more and it complicates things anyway return Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
ConstructorInfo[] constructors = typeof(TViewModel).GetConstructors();
if (constructors.Length != 1)
throw new ArtemisCoreException("Node VMs must have exactly one constructor");
// Find the ScriptConfiguration parameter, it is required by the base constructor so its there for sure
ParameterInfo? configurationParameter = constructors.First().GetParameters().FirstOrDefault(p => GetType().IsAssignableFrom(p.ParameterType));
if (configurationParameter?.Name == null)
throw new ArtemisCoreException($"Couldn't find a valid constructor argument on {typeof(TViewModel).Name} with type {GetType().Name}");
return Kernel.Get<TViewModel>(new ConstructorArgument(configurationParameter.Name, this), new ConstructorArgument("script", nodeScript));
} }
/// <summary> /// <summary>

View File

@ -10,10 +10,10 @@ namespace Artemis.Storage;
public class StorageMigrationService public class StorageMigrationService
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly List<IStorageMigration> _migrations; private readonly IList<IStorageMigration> _migrations;
private readonly LiteRepository _repository; private readonly LiteRepository _repository;
public StorageMigrationService(ILogger logger, LiteRepository repository, List<IStorageMigration> migrations) public StorageMigrationService(ILogger logger, LiteRepository repository, IList<IStorageMigration> migrations)
{ {
_logger = logger; _logger = logger;
_repository = repository; _repository = repository;

View File

@ -5,7 +5,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
using Ninject; using DryIoc;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Linux; namespace Artemis.UI.Linux;
@ -13,12 +13,12 @@ namespace Artemis.UI.Linux;
public class App : Application public class App : Application
{ {
private ApplicationStateManager? _applicationStateManager; private ApplicationStateManager? _applicationStateManager;
private StandardKernel? _kernel; private IContainer? _container;
public override void Initialize() public override void Initialize()
{ {
_kernel = ArtemisBootstrapper.Bootstrap(this); _container = ArtemisBootstrapper.Bootstrap(this);
Program.CreateLogger(_kernel); Program.CreateLogger(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
@ -32,15 +32,15 @@ public class App : Application
ArtemisBootstrapper.Initialize(); ArtemisBootstrapper.Initialize();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args); _applicationStateManager = new ApplicationStateManager(_container!, desktop.Args);
} }
private void RegisterProviders() private void RegisterProviders()
{ {
IInputService inputService = _kernel.Get<IInputService>(); IInputService inputService = _container.Resolve<IInputService>();
try try
{ {
inputService.AddInputProvider(_kernel.Get<LinuxInputProvider>()); inputService.AddInputProvider(_container.Resolve<InputProvider>(LinuxInputProvider.Id));
} }
catch catch
{ {

View File

@ -11,7 +11,7 @@ using Artemis.UI.Shared.Services;
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading; using Avalonia.Threading;
using Ninject; using DryIoc;
namespace Artemis.UI.Linux; namespace Artemis.UI.Linux;
@ -22,23 +22,23 @@ public class ApplicationStateManager
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released // ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
private Mutex? _artemisMutex; private Mutex? _artemisMutex;
public ApplicationStateManager(IKernel kernel, string[] startupArguments) public ApplicationStateManager(IContainer container, string[] startupArguments)
{ {
_windowService = kernel.Get<IWindowService>(); _windowService = container.Resolve<IWindowService>();
StartupArguments = startupArguments; StartupArguments = startupArguments;
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested; Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested; Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
// On OS shutdown dispose the kernel just so device providers get a chance to clean up // On OS shutdown dispose the IOC container just so device providers get a chance to clean up
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime) if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
controlledApplicationLifetime.Exit += (_, _) => controlledApplicationLifetime.Exit += (_, _) =>
{ {
RunForcedShutdownIfEnabled(); RunForcedShutdownIfEnabled();
// Dispose plugins before disposing the kernel because plugins might access services during dispose // Dispose plugins before disposing the IOC container because plugins might access services during dispose
kernel.Get<IPluginManagementService>().Dispose(); container.Resolve<IPluginManagementService>().Dispose();
kernel.Dispose(); container.Dispose();
}; };
} }

View File

@ -0,0 +1,17 @@
using Artemis.Core.DryIoc;
using Artemis.Core.Services;
using Artemis.UI.Linux.Providers.Input;
using DryIoc;
namespace Artemis.UI.Linux.DryIoc;
public class LinuxModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
builder.Register<InputProvider, LinuxInputProvider>(serviceKey: LinuxInputProvider.Id);
}
}

View File

@ -1,7 +1,7 @@
using System; using System;
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Ninject; using DryIoc;
using Serilog; using Serilog;
namespace Artemis.UI.Linux; namespace Artemis.UI.Linux;
@ -33,8 +33,8 @@ internal class Program
return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI(); return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI();
} }
public static void CreateLogger(IKernel kernel) public static void CreateLogger(IContainer container)
{ {
Logger = kernel.Get<ILogger>().ForContext<Program>(); Logger = container.Resolve<ILogger>().ForContext<Program>();
} }
} }

View File

@ -26,6 +26,8 @@ public class LinuxInputProvider : InputProvider
_readers.Add(reader); _readers.Add(reader);
} }
} }
public static Guid Id { get; } = new("72a6fe5c-b11e-4886-bd48-b3ff5d9006c1");
#region Overrides of InputProvider #region Overrides of InputProvider

View File

@ -2,19 +2,19 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
using Ninject; using DryIoc;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.MacOS; namespace Artemis.UI.MacOS;
public class App : Application public class App : Application
{ {
private StandardKernel? _kernel; private IContainer? _container;
public override void Initialize() public override void Initialize()
{ {
_kernel = ArtemisBootstrapper.Bootstrap(this); _container = ArtemisBootstrapper.Bootstrap(this);
Program.CreateLogger(_kernel); Program.CreateLogger(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }

View File

@ -1,7 +1,7 @@
using System; using System;
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Ninject; using DryIoc;
using Serilog; using Serilog;
namespace Artemis.UI.MacOS; namespace Artemis.UI.MacOS;
@ -33,8 +33,8 @@ internal class Program
return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI(); return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI();
} }
public static void CreateLogger(IKernel kernel) public static void CreateLogger(IContainer container)
{ {
Logger = kernel.Get<ILogger>().ForContext<Program>(); Logger = container.Resolve<ILogger>().ForContext<Program>();
} }
} }

View File

@ -0,0 +1,19 @@
using System.Reflection;
using Artemis.Core.DryIoc;
using Artemis.UI.Shared.Services;
using DryIoc;
namespace Artemis.UI.Shared.DryIoc;
/// <summary>
/// The main <see cref="IModule" /> of the Artemis Shared UI toolkit that binds all services
/// </summary>
public class SharedUIModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
Assembly artemisShared = typeof(IArtemisSharedUIService).GetAssembly();
builder.RegisterMany(new[] { artemisShared }, type => type.IsAssignableTo<IArtemisSharedUIService>(), Reuse.Singleton);
}
}

View File

@ -1,30 +0,0 @@
using System;
using Artemis.UI.Shared.Services;
using Ninject.Extensions.Conventions;
using Ninject.Modules;
namespace Artemis.UI.Shared.Ninject;
/// <summary>
/// The main <see cref="NinjectModule" /> of the Artemis Shared UI toolkit that binds all services
/// </summary>
public class SharedUIModule : NinjectModule
{
/// <inheritdoc />
public override void Load()
{
if (Kernel == null)
throw new ArgumentNullException("Kernel shouldn't be null here.");
// Bind all shared UI services as singletons
Kernel.Bind(x =>
{
x.FromAssemblyContaining<IArtemisSharedUIService>()
.IncludingNonPublicTypes()
.SelectAllClasses()
.InheritedFrom<IArtemisSharedUIService>()
.BindAllInterfaces()
.Configure(c => c.InSingletonScope());
});
}
}

View File

@ -3,9 +3,8 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls; using Avalonia.Controls;
using DryIoc;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ninject;
using Ninject.Parameters;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Shared.Services.Builders; namespace Artemis.UI.Shared.Services.Builders;
@ -16,13 +15,13 @@ namespace Artemis.UI.Shared.Services.Builders;
public class ContentDialogBuilder public class ContentDialogBuilder
{ {
private readonly ContentDialog _contentDialog; private readonly ContentDialog _contentDialog;
private readonly IKernel _kernel; private readonly IContainer _container;
private readonly Window _parent; private readonly Window _parent;
private ContentDialogViewModelBase? _viewModel; private ContentDialogViewModelBase? _viewModel;
internal ContentDialogBuilder(IKernel kernel, Window parent) internal ContentDialogBuilder(IContainer container, Window parent)
{ {
_kernel = kernel; _container = container;
_parent = parent; _parent = parent;
_contentDialog = new ContentDialog _contentDialog = new ContentDialog
{ {
@ -129,12 +128,11 @@ public class ContentDialogBuilder
/// </summary> /// </summary>
/// <typeparam name="T">The type of the view model to host.</typeparam> /// <typeparam name="T">The type of the view model to host.</typeparam>
/// <param name="viewModel">The resulting view model.</param> /// <param name="viewModel">The resulting view model.</param>
/// <param name="parameters">Optional parameters to pass to the constructor of the view model, case and order sensitive.</param> /// <param name="parameters">Optional parameters to pass to the constructor of the view model.</param>
/// <returns>The builder that can be used to further build the dialog.</returns> /// <returns>The builder that can be used to further build the dialog.</returns>
public ContentDialogBuilder WithViewModel<T>(out T viewModel, params (string name, object? value)[] parameters) where T : ContentDialogViewModelBase public ContentDialogBuilder WithViewModel<T>(out T viewModel, params object[] parameters) where T : ContentDialogViewModelBase
{ {
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray(); viewModel = _container.Resolve<T>(parameters);
viewModel = _kernel.Get<T>(paramsArray);
viewModel.ContentDialog = _contentDialog; viewModel.ContentDialog = _contentDialog;
_contentDialog.Content = viewModel; _contentDialog.Content = viewModel;

View File

@ -8,22 +8,21 @@ using Artemis.Core.Services;
using Artemis.UI.Shared.DataModelVisualization; using Artemis.UI.Shared.DataModelVisualization;
using Artemis.UI.Shared.DataModelVisualization.Shared; using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.DefaultTypes.DataModel.Display; using Artemis.UI.Shared.DefaultTypes.DataModel.Display;
using Ninject; using DryIoc;
using Ninject.Parameters;
namespace Artemis.UI.Shared.Services; namespace Artemis.UI.Shared.Services;
internal class DataModelUIService : IDataModelUIService internal class DataModelUIService : IDataModelUIService
{ {
private readonly IDataModelService _dataModelService; private readonly IDataModelService _dataModelService;
private readonly IKernel _kernel; private readonly IContainer _container;
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays; private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors; private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
public DataModelUIService(IDataModelService dataModelService, IKernel kernel) public DataModelUIService(IDataModelService dataModelService, IContainer container)
{ {
_dataModelService = dataModelService; _dataModelService = dataModelService;
_kernel = kernel; _container = container;
_registeredDataModelEditors = new List<DataModelVisualizationRegistration>(); _registeredDataModelEditors = new List<DataModelVisualizationRegistration>();
_registeredDataModelDisplays = new List<DataModelVisualizationRegistration>(); _registeredDataModelDisplays = new List<DataModelVisualizationRegistration>();
@ -36,18 +35,8 @@ internal class DataModelUIService : IDataModelUIService
// This assumes the type can be converted, that has been checked when the VM was created // This assumes the type can be converted, that has been checked when the VM was created
if (initialValue != null && initialValue.GetType() != registration.SupportedType) if (initialValue != null && initialValue.GetType() != registration.SupportedType)
initialValue = Convert.ChangeType(initialValue, registration.SupportedType); initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
IParameter[] parameters = DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Resolve(registration.ViewModelType, description, initialValue);
{
new ConstructorArgument("targetDescription", description),
new ConstructorArgument("initialValue", initialValue)
};
// If this ever happens something is likely wrong with the plugin unload detection
if (registration.Plugin.Kernel == null)
throw new ArtemisSharedUIException("Cannot InstantiateDataModelInputViewModel for a registration by an uninitialized plugin");
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Kernel.Get(registration.ViewModelType, parameters);
viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes; viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes;
return viewModel; return viewModel;
} }
@ -133,7 +122,7 @@ internal class DataModelUIService : IDataModelUIService
return existing; return existing;
} }
_kernel.Bind(viewModelType).ToSelf(); _container.Register(viewModelType, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
// Create the registration // Create the registration
DataModelVisualizationRegistration registration = new(this, RegistrationType.Input, plugin, supportedType, viewModelType) DataModelVisualizationRegistration registration = new(this, RegistrationType.Input, plugin, supportedType, viewModelType)
@ -162,7 +151,7 @@ internal class DataModelUIService : IDataModelUIService
return existing; return existing;
} }
_kernel.Bind(viewModelType).ToSelf(); _container.Register(viewModelType);
DataModelVisualizationRegistration registration = new(this, RegistrationType.Display, plugin, supportedType, viewModelType); DataModelVisualizationRegistration registration = new(this, RegistrationType.Display, plugin, supportedType, viewModelType);
_registeredDataModelDisplays.Add(registration); _registeredDataModelDisplays.Add(registration);
return registration; return registration;
@ -178,7 +167,8 @@ internal class DataModelUIService : IDataModelUIService
registration.Unsubscribe(); registration.Unsubscribe();
_registeredDataModelEditors.Remove(registration); _registeredDataModelEditors.Remove(registration);
_kernel.Unbind(registration.ViewModelType); _container.Unregister(registration.ViewModelType);
_container.ClearCache(registration.ViewModelType);
} }
} }
} }
@ -192,7 +182,8 @@ internal class DataModelUIService : IDataModelUIService
registration.Unsubscribe(); registration.Unsubscribe();
_registeredDataModelDisplays.Remove(registration); _registeredDataModelDisplays.Remove(registration);
_kernel.Unbind(registration.ViewModelType); _container.Unregister(registration.ViewModelType);
_container.ClearCache(registration.ViewModelType);
} }
} }
} }
@ -205,21 +196,11 @@ internal class DataModelUIService : IDataModelUIService
DataModelVisualizationRegistration? match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType); DataModelVisualizationRegistration? match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
if (match != null) if (match != null)
{ result = (DataModelDisplayViewModel) match.Plugin.Resolve(match.ViewModelType);
// If this ever happens something is likely wrong with the plugin unload detection
if (match.Plugin.Kernel == null)
throw new ArtemisSharedUIException("Cannot GetDataModelDisplayViewModel for a registration by an uninitialized plugin");
result = (DataModelDisplayViewModel) match.Plugin.Kernel.Get(match.ViewModelType);
}
else if (!fallBackToDefault) else if (!fallBackToDefault)
{
result = null; result = null;
}
else else
{ result = _container.Resolve<DefaultDataModelDisplayViewModel>();
result = _kernel.Get<DefaultDataModelDisplayViewModel>();
}
if (result != null) if (result != null)
result.PropertyDescription = description; result.PropertyDescription = description;

View File

@ -16,7 +16,7 @@ public interface IWindowService : IArtemisSharedUIService
/// </summary> /// </summary>
/// <typeparam name="TViewModel">The type of view model to create</typeparam> /// <typeparam name="TViewModel">The type of view model to create</typeparam>
/// <returns>The created view model</returns> /// <returns>The created view model</returns>
TViewModel ShowWindow<TViewModel>(params (string name, object value)[] parameters); TViewModel ShowWindow<TViewModel>(params object[] parameters);
/// <summary> /// <summary>
/// Given a ViewModel, show its corresponding View as a window /// Given a ViewModel, show its corresponding View as a window
@ -37,7 +37,7 @@ public interface IWindowService : IArtemisSharedUIService
/// </summary> /// </summary>
/// <typeparam name="TViewModel">The type of view model to create</typeparam> /// <typeparam name="TViewModel">The type of view model to create</typeparam>
/// <returns>The created view model</returns> /// <returns>The created view model</returns>
Task<TViewModel> ShowDialogAsync<TViewModel>(params (string name, object value)[] parameters); Task<TViewModel> ShowDialogAsync<TViewModel>(params object[] parameters);
/// <summary> /// <summary>
/// Given a ViewModel, show its corresponding View as a dialog /// Given a ViewModel, show its corresponding View as a dialog
@ -60,7 +60,7 @@ public interface IWindowService : IArtemisSharedUIService
/// <typeparam name="TViewModel">The view model type</typeparam> /// <typeparam name="TViewModel">The view model type</typeparam>
/// <typeparam name="TResult">The return type</typeparam> /// <typeparam name="TResult">The return type</typeparam>
/// <returns>A task containing the return value of type <typeparamref name="TResult" /></returns> /// <returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>; Task<TResult> ShowDialogAsync<TViewModel, TResult>(params object[] parameters) where TViewModel : DialogViewModelBase<TResult>;
/// <summary> /// <summary>
/// Shows a content dialog asking the user to confirm an action /// Shows a content dialog asking the user to confirm an action

View File

@ -1,5 +1,4 @@
using System; using System;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.NodeEditor.Commands; namespace Artemis.UI.Shared.Services.NodeEditor.Commands;

View File

@ -3,19 +3,18 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Ninject; using DryIoc;
using Ninject.Parameters;
namespace Artemis.UI.Shared.Services.PropertyInput; namespace Artemis.UI.Shared.Services.PropertyInput;
internal class PropertyInputService : IPropertyInputService internal class PropertyInputService : IPropertyInputService
{ {
private readonly IKernel _kernel; private readonly IContainer _container;
private readonly List<PropertyInputRegistration> _registeredPropertyEditors; private readonly List<PropertyInputRegistration> _registeredPropertyEditors;
public PropertyInputService(IKernel kernel) public PropertyInputService(IContainer container)
{ {
_kernel = kernel; _container = container;
_registeredPropertyEditors = new List<PropertyInputRegistration>(); _registeredPropertyEditors = new List<PropertyInputRegistration>();
RegisteredPropertyEditors = new ReadOnlyCollection<PropertyInputRegistration>(_registeredPropertyEditors); RegisteredPropertyEditors = new ReadOnlyCollection<PropertyInputRegistration>(_registeredPropertyEditors);
} }
@ -55,7 +54,7 @@ internal class PropertyInputService : IPropertyInputService
return existing; return existing;
} }
_kernel.Bind(viewModelType).ToSelf(); _container.Register(viewModelType);
PropertyInputRegistration registration = new(this, plugin, supportedType, viewModelType); PropertyInputRegistration registration = new(this, plugin, supportedType, viewModelType);
_registeredPropertyEditors.Add(registration); _registeredPropertyEditors.Add(registration);
return registration; return registration;
@ -71,7 +70,8 @@ internal class PropertyInputService : IPropertyInputService
registration.Unsubscribe(); registration.Unsubscribe();
_registeredPropertyEditors.Remove(registration); _registeredPropertyEditors.Remove(registration);
_kernel.Unbind(registration.ViewModelType); _container.Unregister(registration.ViewModelType);
_container.ClearCache(registration.ViewModelType);
} }
} }
} }
@ -110,10 +110,9 @@ internal class PropertyInputService : IPropertyInputService
if (viewModelType == null) if (viewModelType == null)
return null; return null;
ConstructorArgument parameter = new("layerProperty", layerProperty);
// ReSharper disable once InconsistentlySynchronizedField // ReSharper disable once InconsistentlySynchronizedField
// When you've just spent the last 2 hours trying to figure out a deadlock and reach this line, I'm so, so sorry. I thought this would be fine. // When you've just spent the last 2 hours trying to figure out a deadlock and reach this line, I'm so, so sorry. I thought this would be fine.
IKernel kernel = registration?.Plugin.Kernel ?? _kernel; IContainer container = registration?.Plugin.Container ?? _container;
return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter); return (PropertyInputViewModel<T>) container.Resolve(viewModelType, args: new object[] { layerProperty });
} }
} }

View File

@ -6,26 +6,27 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading; using Avalonia.Threading;
using DryIoc;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ninject;
using Ninject.Parameters;
namespace Artemis.UI.Shared.Services; namespace Artemis.UI.Shared.Services;
internal class WindowService : IWindowService internal class WindowService : IWindowService
{ {
private readonly IKernel _kernel; private readonly IContainer _container;
private bool _exceptionDialogOpen; private bool _exceptionDialogOpen;
public WindowService(IKernel kernel) public WindowService(IContainer container)
{ {
_kernel = kernel; _container = container;
} }
public T ShowWindow<T>(params (string name, object value)[] parameters) public T ShowWindow<T>(params object[] parameters)
{ {
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray(); T viewModel = _container.Resolve<T>(parameters);
T viewModel = _kernel.Get<T>(paramsArray)!; if (viewModel == null)
throw new ArtemisSharedUIException($"Failed to show window for VM of type {typeof(T).Name}, could not create instance.");
ShowWindow(viewModel); ShowWindow(viewModel);
return viewModel; return viewModel;
} }
@ -53,10 +54,12 @@ internal class WindowService : IWindowService
return window; return window;
} }
public async Task<T> ShowDialogAsync<T>(params (string name, object value)[] parameters) public async Task<T> ShowDialogAsync<T>(params object[] parameters)
{ {
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray(); T viewModel = _container.Resolve<T>(parameters);
T viewModel = _kernel.Get<T>(paramsArray)!; if (viewModel == null)
throw new ArtemisSharedUIException($"Failed to show window for VM of type {typeof(T).Name}, could not create instance.");
await ShowDialogAsync(viewModel); await ShowDialogAsync(viewModel);
return viewModel; return viewModel;
} }
@ -79,10 +82,12 @@ internal class WindowService : IWindowService
await window.ShowDialog(parent); await window.ShowDialog(parent);
} }
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult> public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params object[] parameters) where TViewModel : DialogViewModelBase<TResult>
{ {
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray(); TViewModel viewModel = _container.Resolve<TViewModel>(parameters);
TViewModel viewModel = _kernel.Get<TViewModel>(paramsArray)!; if (viewModel == null)
throw new ArtemisSharedUIException($"Failed to show window for VM of type {typeof(TViewModel).Name}, could not create instance.");
return await ShowDialogAsync(viewModel); return await ShowDialogAsync(viewModel);
} }
@ -129,7 +134,7 @@ internal class WindowService : IWindowService
{ {
try try
{ {
await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _kernel.Get<INotificationService>())); await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _container.Resolve<INotificationService>()));
} }
finally finally
{ {
@ -143,7 +148,7 @@ internal class WindowService : IWindowService
Window? currentWindow = GetCurrentWindow(); Window? currentWindow = GetCurrentWindow();
if (currentWindow == null) if (currentWindow == null)
throw new ArtemisSharedUIException("Can't show a content dialog without any windows being shown."); throw new ArtemisSharedUIException("Can't show a content dialog without any windows being shown.");
return new ContentDialogBuilder(_kernel, currentWindow); return new ContentDialogBuilder(_container, currentWindow);
} }
public OpenFolderDialogBuilder CreateOpenFolderDialog() public OpenFolderDialogBuilder CreateOpenFolderDialog()

View File

@ -7,21 +7,21 @@ using System.Net.Http;
using System.Threading; using System.Threading;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Windows.Ninject; using Artemis.UI.Windows.DryIoc;
using Artemis.UI.Windows.Providers.Input; using Artemis.UI.Windows.Providers.Input;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
using Ninject; using DryIoc;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Windows; namespace Artemis.UI.Windows;
public class App : Application public class App : Application
{ {
private StandardKernel? _kernel; private IContainer? _container;
private bool _shutDown; private bool _shutDown;
public override void Initialize() public override void Initialize()
@ -33,8 +33,8 @@ public class App : Application
Environment.Exit(1); Environment.Exit(1);
} }
_kernel = ArtemisBootstrapper.Bootstrap(this, new WindowsModule()); _container = ArtemisBootstrapper.Bootstrap(this, new WindowsModule());
Program.CreateLogger(_kernel); Program.CreateLogger(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
@ -45,14 +45,14 @@ public class App : Application
return; return;
ArtemisBootstrapper.Initialize(); ArtemisBootstrapper.Initialize();
_applicationStateManager = new ApplicationStateManager(_kernel!, desktop.Args); _applicationStateManager = new ApplicationStateManager(_container!, desktop.Args);
RegisterProviders(_kernel!); RegisterProviders(_container!);
} }
private void RegisterProviders(StandardKernel standardKernel) private void RegisterProviders(IContainer container)
{ {
IInputService inputService = standardKernel.Get<IInputService>(); IInputService inputService = container.Resolve<IInputService>();
inputService.AddInputProvider(standardKernel.Get<WindowsInputProvider>()); inputService.AddInputProvider(container.Resolve<InputProvider>(serviceKey: WindowsInputProvider.Id));
} }
private bool FocusExistingInstance() private bool FocusExistingInstance()

View File

@ -10,7 +10,7 @@ using Artemis.UI.Windows.Utilities;
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading; using Avalonia.Threading;
using Ninject; using DryIoc;
namespace Artemis.UI.Windows; namespace Artemis.UI.Windows;
@ -18,7 +18,7 @@ public class ApplicationStateManager
{ {
private const int SM_SHUTTINGDOWN = 0x2000; private const int SM_SHUTTINGDOWN = 0x2000;
public ApplicationStateManager(IKernel kernel, string[] startupArguments) public ApplicationStateManager(IContainer container, string[] startupArguments)
{ {
StartupArguments = startupArguments; StartupArguments = startupArguments;
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
@ -26,19 +26,19 @@ public class ApplicationStateManager
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested; Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested; Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
// On Windows shutdown dispose the kernel just so device providers get a chance to clean up // On Windows shutdown dispose the IOC container just so device providers get a chance to clean up
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime) if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
controlledApplicationLifetime.Exit += (_, _) => controlledApplicationLifetime.Exit += (_, _) =>
{ {
RunForcedShutdownIfEnabled(); RunForcedShutdownIfEnabled();
// Dispose plugins before disposing the kernel because plugins might access services during dispose // Dispose plugins before disposing the IOC container because plugins might access services during dispose
kernel.Get<IPluginManagementService>().Dispose(); container.Resolve<IPluginManagementService>().Dispose();
kernel.Dispose(); container.Dispose();
}; };
// Inform the Core about elevation status // Inform the Core about elevation status
kernel.Get<ICoreService>().IsElevated = IsElevated; container.Resolve<ICoreService>().IsElevated = IsElevated;
} }
public string[] StartupArguments { get; } public string[] StartupArguments { get; }

View File

@ -0,0 +1,22 @@
using Artemis.Core.DryIoc;
using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Windows.Providers;
using Artemis.UI.Windows.Providers.Input;
using DryIoc;
namespace Artemis.UI.Windows.DryIoc;
public class WindowsModule : IModule
{
/// <inheritdoc />
public void Load(IRegistrator builder)
{
builder.Register<ICursorProvider, CursorProvider>(Reuse.Singleton);
builder.Register<IGraphicsContextProvider, GraphicsContextProvider>(Reuse.Singleton);
builder.Register<IUpdateProvider, UpdateProvider>(Reuse.Singleton);
builder.Register<IAutoRunProvider, AutoRunProvider>();
builder.Register<InputProvider, WindowsInputProvider>(serviceKey: WindowsInputProvider.Id);
}
}

View File

@ -1,22 +0,0 @@
using Artemis.Core.Providers;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Windows.Providers;
using Ninject.Modules;
namespace Artemis.UI.Windows.Ninject;
public class WindowsModule : NinjectModule
{
#region Overrides of NinjectModule
/// <inheritdoc />
public override void Load()
{
Kernel!.Bind<ICursorProvider>().To<CursorProvider>().InSingletonScope();
Kernel!.Bind<IGraphicsContextProvider>().To<GraphicsContextProvider>().InSingletonScope();
Kernel!.Bind<IUpdateProvider>().To<UpdateProvider>().InSingletonScope();
Kernel!.Bind<IAutoRunProvider>().To<AutoRunProvider>();
}
#endregion
}

View File

@ -1,7 +1,7 @@
using System; using System;
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Ninject; using DryIoc;
using Serilog; using Serilog;
namespace Artemis.UI.Windows; namespace Artemis.UI.Windows;
@ -41,8 +41,8 @@ internal class Program
.UseReactiveUI(); .UseReactiveUI();
} }
public static void CreateLogger(IKernel kernel) public static void CreateLogger(IContainer container)
{ {
Logger = kernel.Get<ILogger>().ForContext<Program>(); Logger = container.Resolve<ILogger>().ForContext<Program>();
} }
} }

View File

@ -37,6 +37,8 @@ public class WindowsInputProvider : InputProvider
RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _sponge.Handle.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.InputSink, _sponge.Handle.Handle);
} }
public static Guid Id { get; } = new("6737b204-ffb1-4cd9-8776-9fb851db303a");
#region Overrides of InputProvider #region Overrides of InputProvider

View File

@ -201,7 +201,7 @@ public class UpdateProvider : IUpdateProvider, IDisposable
{ {
if (windowOpen) if (windowOpen)
{ {
bool update = await _windowService.ShowDialogAsync<UpdateDialogViewModel, bool>(("channel", channel)); bool update = await _windowService.ShowDialogAsync<UpdateDialogViewModel, bool>(channel);
if (update) if (update)
await RunInstaller(channel, false); await RunInstaller(channel, false);
} }

View File

@ -22,6 +22,7 @@
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" /> <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.18" /> <PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.18" />
<PackageReference Include="DryIoc.dll" Version="5.3.1" />
<PackageReference Include="DynamicData" Version="7.9.14" /> <PackageReference Include="DynamicData" Version="7.9.14" />
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" /> <PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
<PackageReference Include="Flurl.Http" Version="3.2.4" /> <PackageReference Include="Flurl.Http" Version="3.2.4" />
@ -32,7 +33,7 @@
<PackageReference Include="RGB.NET.Core" Version="1.0.0" /> <PackageReference Include="RGB.NET.Core" Version="1.0.0" />
<PackageReference Include="RGB.NET.Layout" Version="1.0.0" /> <PackageReference Include="RGB.NET.Layout" Version="1.0.0" />
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" /> <PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
<PackageReference Include="Splat.Ninject" Version="14.4.1" /> <PackageReference Include="Splat.DryIoc" Version="14.6.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,53 +3,54 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Reactive; using System.Reactive;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Ninject; using Artemis.Core.DryIoc;
using Artemis.UI.DryIoc;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Ninject;
using Artemis.UI.Screens.Root; using Artemis.UI.Screens.Root;
using Artemis.UI.Shared.DataModelPicker; using Artemis.UI.Shared.DataModelPicker;
using Artemis.UI.Shared.Ninject; using Artemis.UI.Shared.DryIoc;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.VisualScripting.Ninject; using Artemis.VisualScripting.DryIoc;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Ninject; using DryIoc;
using Ninject.Modules;
using ReactiveUI; using ReactiveUI;
using Splat.Ninject; using Splat.DryIoc;
namespace Artemis.UI; namespace Artemis.UI;
public static class ArtemisBootstrapper public static class ArtemisBootstrapper
{ {
private static StandardKernel? _kernel; private static Container? _container;
private static Application? _application; private static Application? _application;
public static StandardKernel Bootstrap(Application application, params INinjectModule[] modules) public static IContainer Bootstrap(Application application, params IModule[] modules)
{ {
if (_application != null || _kernel != null) if (_application != null || _container != null)
throw new ArtemisUIException("UI already bootstrapped"); throw new ArtemisUIException("UI already bootstrapped");
Utilities.PrepareFirstLaunch(); Utilities.PrepareFirstLaunch();
_application = application; _application = application;
_kernel = new StandardKernel(); _container = new Container(rules => rules.WithConcreteTypeDynamicRegistrations()
_kernel.Settings.InjectNonPublic = true; .WithoutThrowOnRegisteringDisposableTransient());
_kernel.Load<CoreModule>(); new CoreModule().Load(_container);
_kernel.Load<UIModule>(); new UIModule().Load(_container);
_kernel.Load<SharedUIModule>(); new SharedUIModule().Load(_container);
_kernel.Load<NoStringNinjectModule>(); new NoStringDryIocModule().Load(_container);
_kernel.Load(modules); foreach (IModule module in modules)
_kernel.UseNinjectDependencyResolver(); module.Load(_container);
return _kernel; _container.UseDryIocDependencyResolver();
return _container;
} }
public static void Initialize() public static void Initialize()
{ {
if (_application == null || _kernel == null) if (_application == null || _container == null)
throw new ArtemisUIException("UI not yet bootstrapped"); throw new ArtemisUIException("UI not yet bootstrapped");
if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
return; return;
@ -59,16 +60,16 @@ public static class ArtemisBootstrapper
// Don't shut down when the last window closes, we might still be active in the tray // Don't shut down when the last window closes, we might still be active in the tray
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
// Create the root view model that drives the UI // Create the root view model that drives the UI
RootViewModel rootViewModel = _kernel.Get<RootViewModel>(); RootViewModel rootViewModel = _container.Resolve<RootViewModel>();
// Apply the root view model to the data context of the application so that tray icon commands work // Apply the root view model to the data context of the application so that tray icon commands work
_application.DataContext = rootViewModel; _application.DataContext = rootViewModel;
RxApp.DefaultExceptionHandler = Observer.Create<Exception>(DisplayUnhandledException); RxApp.DefaultExceptionHandler = Observer.Create<Exception>(DisplayUnhandledException);
DataModelPicker.DataModelUIService = _kernel.Get<IDataModelUIService>(); DataModelPicker.DataModelUIService = _container.Resolve<IDataModelUIService>();
} }
private static void DisplayUnhandledException(Exception exception) private static void DisplayUnhandledException(Exception exception)
{ {
_kernel?.Get<IWindowService>().ShowExceptionDialog("Exception", exception); _container?.Resolve<IWindowService>().ShowExceptionDialog("Exception", exception);
} }
} }

View File

@ -59,14 +59,14 @@ public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushRefe
/// <inheritdoc /> /// <inheritdoc />
protected override void ApplyInputValue() protected override void ApplyInputValue()
{ {
if (LayerProperty.ProfileElement is not Layer layer || SelectedDescriptor == null) if (LayerProperty.ProfileElement is not Layer layer || layer.LayerBrush == null || SelectedDescriptor == null)
return; return;
_profileEditorService.ExecuteCommand(new ChangeLayerBrush(layer, SelectedDescriptor)); _profileEditorService.ExecuteCommand(new ChangeLayerBrush(layer, SelectedDescriptor));
if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any()) if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any())
Dispatcher.UIThread.InvokeAsync(() => _windowService.CreateContentDialog() Dispatcher.UIThread.InvokeAsync(() => _windowService.CreateContentDialog()
.WithTitle("Select preset") .WithTitle("Select preset")
.WithViewModel(out LayerBrushPresetViewModel _, ("layerBrush", layer.LayerBrush)) .WithViewModel(out LayerBrushPresetViewModel _, layer.LayerBrush)
.WithDefaultButton(ContentDialogButton.Close) .WithDefaultButton(ContentDialogButton.Close)
.WithCloseButtonText("Use defaults") .WithCloseButtonText("Use defaults")
.ShowAsync()); .ShowAsync());

View File

@ -0,0 +1,477 @@
using System.Collections.ObjectModel;
using System.Reactive;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.ScriptingProviders;
using Artemis.UI.Screens.Device;
using Artemis.UI.Screens.Plugins;
using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Screens.Scripting;
using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Screens.VisualScripting.Pins;
using DryIoc;
using ReactiveUI;
namespace Artemis.UI.DryIoc.Factories;
public interface IVmFactory
{
}
public interface IDeviceVmFactory : IVmFactory
{
DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device);
DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel);
DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device);
DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device);
DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device);
DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
}
public class DeviceFactory : IDeviceVmFactory
{
private readonly IContainer _container;
public DeviceFactory(IContainer container)
{
_container = container;
}
public DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device)
{
return _container.Resolve<DevicePropertiesViewModel>(new object[] { device });
}
public DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel)
{
return _container.Resolve<DeviceSettingsViewModel>(new object[] { device, devicesTabViewModel });
}
public DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device)
{
return _container.Resolve<DeviceDetectInputViewModel>(new object[] { device });
}
public DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device)
{
return _container.Resolve<DevicePropertiesTabViewModel>(new object[] { device });
}
public DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device)
{
return _container.Resolve<DeviceInfoTabViewModel>(new object[] { device });
}
public DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds)
{
return _container.Resolve<DeviceLedsTabViewModel>(new object[] { device, selectedLeds });
}
public InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds)
{
return _container.Resolve<InputMappingsTabViewModel>(new object[] { device, selectedLeds });
}
}
public interface ISettingsVmFactory : IVmFactory
{
PluginSettingsViewModel PluginSettingsViewModel(Plugin plugin);
PluginViewModel PluginViewModel(Plugin plugin, ReactiveCommand<Unit, Unit>? reload);
PluginFeatureViewModel PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
}
public class SettingsVmFactory : ISettingsVmFactory
{
private readonly IContainer _container;
public SettingsVmFactory(IContainer container)
{
_container = container;
}
public PluginSettingsViewModel PluginSettingsViewModel(Plugin plugin)
{
return _container.Resolve<PluginSettingsViewModel>(new object[] { plugin });
}
public PluginViewModel PluginViewModel(Plugin plugin, ReactiveCommand<Unit, Unit>? reload)
{
return _container.Resolve<PluginViewModel>(new object?[] { plugin, reload });
}
public PluginFeatureViewModel PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield)
{
return _container.Resolve<PluginFeatureViewModel>(new object[] { pluginFeatureInfo, showShield });
}
}
public interface ISidebarVmFactory : IVmFactory
{
SidebarViewModel? SidebarViewModel(IScreen hostScreen);
SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory);
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration);
}
public class SidebarVmFactory : ISidebarVmFactory
{
private readonly IContainer _container;
public SidebarVmFactory(IContainer container)
{
_container = container;
}
public SidebarViewModel? SidebarViewModel(IScreen hostScreen)
{
return _container.Resolve<SidebarViewModel>(new object[] { hostScreen });
}
public SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory)
{
return _container.Resolve<SidebarCategoryViewModel>(new object[] { profileCategory });
}
public SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration)
{
return _container.Resolve<SidebarProfileConfigurationViewModel>(new object[] { profileConfiguration });
}
}
public interface ISurfaceVmFactory : IVmFactory
{
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
}
public class SurfaceVmFactory : ISurfaceVmFactory
{
private readonly IContainer _container;
public SurfaceVmFactory(IContainer container)
{
_container = container;
}
public SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel)
{
return _container.Resolve<SurfaceDeviceViewModel>(new object[] { device, surfaceEditorViewModel });
}
public ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel)
{
return _container.Resolve<ListDeviceViewModel>(new object[] { device, surfaceEditorViewModel });
}
}
public interface IPrerequisitesVmFactory : IVmFactory
{
PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall);
}
public class PrerequisitesVmFactory : IPrerequisitesVmFactory
{
private readonly IContainer _container;
public PrerequisitesVmFactory(IContainer container)
{
_container = container;
}
public PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall)
{
return _container.Resolve<PluginPrerequisiteViewModel>(new object[] { pluginPrerequisite, uninstall });
}
}
public interface IProfileEditorVmFactory : IVmFactory
{
ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen);
FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder);
LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer);
LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer);
LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer);
}
public class ProfileEditorVmFactory : IProfileEditorVmFactory
{
private readonly IContainer _container;
public ProfileEditorVmFactory(IContainer container)
{
_container = container;
}
public FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder)
{
return _container.Resolve<FolderTreeItemViewModel>(new object?[] { parent, folder });
}
public LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer)
{
return _container.Resolve<LayerShapeVisualizerViewModel>(new object[] { layer });
}
public LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer)
{
return _container.Resolve<LayerTreeItemViewModel>(new object?[] { parent, layer });
}
public LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer)
{
return _container.Resolve<LayerVisualizerViewModel>(new object[] { layer });
}
public ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen)
{
return _container.Resolve<ProfileEditorViewModel>(new object[] { hostScreen });
}
}
public interface ILayerPropertyVmFactory : IVmFactory
{
PropertyViewModel PropertyViewModel(ILayerProperty layerProperty);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect);
TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
TimelineViewModel TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels);
TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
}
public class LayerPropertyVmFactory : ILayerPropertyVmFactory
{
private readonly IContainer _container;
public LayerPropertyVmFactory(IContainer container)
{
_container = container;
}
public PropertyViewModel PropertyViewModel(ILayerProperty layerProperty)
{
return _container.Resolve<PropertyViewModel>(new object[] { layerProperty });
}
public PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup)
{
return _container.Resolve<PropertyGroupViewModel>(new object[] { layerPropertyGroup });
}
public PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush)
{
return _container.Resolve<PropertyGroupViewModel>(new object[] { layerPropertyGroup, layerBrush });
}
public PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect)
{
return _container.Resolve<PropertyGroupViewModel>(new object[] { layerPropertyGroup, layerEffect });
}
public TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel)
{
return _container.Resolve<TreeGroupViewModel>(new object[] { propertyGroupViewModel });
}
public TimelineViewModel TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels)
{
return _container.Resolve<TimelineViewModel>(new object[] { propertyGroupViewModels });
}
public TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel)
{
return _container.Resolve<TimelineGroupViewModel>(new object[] { propertyGroupViewModel });
}
}
public interface IDataBindingVmFactory : IVmFactory
{
DataBindingViewModel DataBindingViewModel();
}
public class DataBindingVmFactory : IDataBindingVmFactory
{
private readonly IContainer _container;
public DataBindingVmFactory(IContainer container)
{
_container = container;
}
public DataBindingViewModel DataBindingViewModel()
{
return _container.Resolve<DataBindingViewModel>();
}
}
public interface IPropertyVmFactory
{
ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
}
public interface INodeVmFactory : IVmFactory
{
NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview);
NodePickerViewModel NodePickerViewModel(NodeScript nodeScript);
NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node);
CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to);
DragCableViewModel DragCableViewModel(PinViewModel pinViewModel);
InputPinViewModel InputPinViewModel(IPin inputPin, NodeScriptViewModel nodeScriptViewModel);
OutputPinViewModel OutputPinViewModel(IPin outputPin, NodeScriptViewModel nodeScriptViewModel);
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
}
public class NodeVmFactory : INodeVmFactory
{
private readonly IContainer _container;
public NodeVmFactory(IContainer container)
{
_container = container;
}
public NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview)
{
return _container.Resolve<NodeScriptViewModel>(new object[] { nodeScript, isPreview });
}
public NodePickerViewModel NodePickerViewModel(NodeScript nodeScript)
{
return _container.Resolve<NodePickerViewModel>(new object[] { nodeScript });
}
public NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node)
{
return _container.Resolve<NodeViewModel>(new object[] { nodeScriptViewModel, node });
}
public CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to)
{
return _container.Resolve<CableViewModel>(new object[] { nodeScriptViewModel, from, to });
}
public DragCableViewModel DragCableViewModel(PinViewModel pinViewModel)
{
return _container.Resolve<DragCableViewModel>(new object[] { pinViewModel });
}
public InputPinViewModel InputPinViewModel(IPin inputPin, NodeScriptViewModel nodeScriptViewModel)
{
return _container.Resolve<InputPinViewModel>(new object[] { inputPin, nodeScriptViewModel });
}
public OutputPinViewModel OutputPinViewModel(IPin outputPin, NodeScriptViewModel nodeScriptViewModel)
{
return _container.Resolve<OutputPinViewModel>(new object[] { outputPin, nodeScriptViewModel });
}
public InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel)
{
return _container.Resolve<InputPinCollectionViewModel>(new object[] { inputPinCollection, nodeScriptViewModel });
}
public OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel)
{
return _container.Resolve<OutputPinCollectionViewModel>(new object[] { outputPinCollection, nodeScriptViewModel });
}
}
public interface IConditionVmFactory : IVmFactory
{
AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition);
PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition);
StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
}
public class ConditionVmFactory : IConditionVmFactory
{
private readonly IContainer _container;
public ConditionVmFactory(IContainer container)
{
_container = container;
}
public AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition)
{
return _container.Resolve<AlwaysOnConditionViewModel>(new object[] { alwaysOnCondition });
}
public PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition)
{
return _container.Resolve<PlayOnceConditionViewModel>(new object[] { playOnceCondition });
}
public StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition)
{
return _container.Resolve<StaticConditionViewModel>(new object[] { staticCondition });
}
public EventConditionViewModel EventConditionViewModel(EventCondition eventCondition)
{
return _container.Resolve<EventConditionViewModel>(new object[] { eventCondition });
}
}
public interface ILayerHintVmFactory : IVmFactory
{
CategoryAdaptionHintViewModel CategoryAdaptionHintViewModel(Layer layer, CategoryAdaptionHint adaptionHint);
DeviceAdaptionHintViewModel DeviceAdaptionHintViewModel(Layer layer, DeviceAdaptionHint adaptionHint);
KeyboardSectionAdaptionHintViewModel KeyboardSectionAdaptionHintViewModel(Layer layer, KeyboardSectionAdaptionHint adaptionHint);
}
public class LayerHintVmFactory : ILayerHintVmFactory
{
private readonly IContainer _container;
public LayerHintVmFactory(IContainer container)
{
_container = container;
}
public CategoryAdaptionHintViewModel CategoryAdaptionHintViewModel(Layer layer, CategoryAdaptionHint adaptionHint)
{
return _container.Resolve<CategoryAdaptionHintViewModel>(new object[] { layer, adaptionHint });
}
public DeviceAdaptionHintViewModel DeviceAdaptionHintViewModel(Layer layer, DeviceAdaptionHint adaptionHint)
{
return _container.Resolve<DeviceAdaptionHintViewModel>(new object[] { layer, adaptionHint });
}
public KeyboardSectionAdaptionHintViewModel KeyboardSectionAdaptionHintViewModel(Layer layer, KeyboardSectionAdaptionHint adaptionHint)
{
return _container.Resolve<KeyboardSectionAdaptionHintViewModel>(new object[] { layer, adaptionHint });
}
}
public interface IScriptVmFactory : IVmFactory
{
ScriptConfigurationViewModel ScriptConfigurationViewModel(ScriptConfiguration scriptConfiguration);
ScriptConfigurationViewModel ScriptConfigurationViewModel(Profile profile, ScriptConfiguration scriptConfiguration);
}
public class ScriptVmFactory : IScriptVmFactory
{
private readonly IContainer _container;
public ScriptVmFactory(IContainer container)
{
_container = container;
}
public ScriptConfigurationViewModel ScriptConfigurationViewModel(ScriptConfiguration scriptConfiguration)
{
return _container.Resolve<ScriptConfigurationViewModel>(new object[] { scriptConfiguration });
}
public ScriptConfigurationViewModel ScriptConfigurationViewModel(Profile profile, ScriptConfiguration scriptConfiguration)
{
return _container.Resolve<ScriptConfigurationViewModel>(new object[] { profile, scriptConfiguration });
}
}

View File

@ -0,0 +1,47 @@
using System;
using Artemis.Core;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree;
using DryIoc;
namespace Artemis.UI.DryIoc.InstanceProviders;
public class PropertyVmFactory : IPropertyVmFactory
{
private readonly IContainer _container;
public PropertyVmFactory(IContainer container)
{
_container = container;
}
public ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel)
{
// Find LayerProperty type
Type? layerPropertyType = layerProperty.GetType();
while (layerPropertyType != null && (!layerPropertyType.IsGenericType || layerPropertyType.GetGenericTypeDefinition() != typeof(LayerProperty<>)))
layerPropertyType = layerPropertyType.BaseType;
if (layerPropertyType == null)
throw new ArtemisUIException("Could not find the LayerProperty type");
Type? genericType = typeof(TimelinePropertyViewModel<>).MakeGenericType(layerPropertyType.GetGenericArguments());
return (ITimelinePropertyViewModel)_container.Resolve(genericType, new object[] { layerProperty, propertyViewModel });
}
public ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel)
{
// Find LayerProperty type
Type? layerPropertyType = layerProperty.GetType();
while (layerPropertyType != null && (!layerPropertyType.IsGenericType || layerPropertyType.GetGenericTypeDefinition() != typeof(LayerProperty<>)))
layerPropertyType = layerPropertyType.BaseType;
if (layerPropertyType == null)
throw new ArtemisUIException("Could not find the LayerProperty type");
Type? genericType = typeof(TreePropertyViewModel<>).MakeGenericType(layerPropertyType.GetGenericArguments());
return (ITreePropertyViewModel)_container.Resolve(genericType, new object[] { layerProperty, propertyViewModel });
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using Artemis.Core.DryIoc;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.DryIoc.InstanceProviders;
using Artemis.UI.Screens;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using DryIoc;
namespace Artemis.UI.DryIoc;
public class UIModule : IModule
{
public void Load(IRegistrator builder)
{
Assembly thisAssembly = typeof(UIModule).Assembly;
builder.RegisterInstance(new AssetLoader(), IfAlreadyRegistered.Throw);
builder.Register<IAssetLoader, AssetLoader>(Reuse.Singleton);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<ViewModelBase>());
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<MainScreenViewModel>(), ifAlreadyRegistered: IfAlreadyRegistered.Replace);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<IToolViewModel>() && type.IsInterface);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<IVmFactory>() && type != typeof(PropertyVmFactory));
builder.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton);
builder.Register<IPropertyVmFactory, PropertyVmFactory>(Reuse.Singleton);
builder.RegisterMany(new[] { thisAssembly }, type => type.IsAssignableTo<IArtemisUIService>(), Reuse.Singleton);
}
}

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Storage.Entities.Profile.Nodes; using Artemis.Storage.Entities.Profile.Nodes;
using FluentAvalonia.Core;
namespace Artemis.UI.Models; namespace Artemis.UI.Models;

View File

@ -1,133 +0,0 @@
using System.Collections.ObjectModel;
using System.Reactive;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.ScriptingProviders;
using Artemis.UI.Screens.Device;
using Artemis.UI.Screens.Plugins;
using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Screens.Scripting;
using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Screens.VisualScripting.Pins;
using ReactiveUI;
namespace Artemis.UI.Ninject.Factories;
public interface IVmFactory
{
}
public interface IDeviceVmFactory : IVmFactory
{
DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device);
DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel);
DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device);
DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device);
DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device);
DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
}
public interface ISettingsVmFactory : IVmFactory
{
PluginSettingsViewModel PluginSettingsViewModel(Plugin plugin);
PluginViewModel PluginViewModel(Plugin plugin, ReactiveCommand<Unit, Unit>? reload);
PluginFeatureViewModel PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
}
public interface ISidebarVmFactory : IVmFactory
{
SidebarViewModel? SidebarViewModel(IScreen hostScreen);
SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory);
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration);
}
public interface ISurfaceVmFactory : IVmFactory
{
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
}
public interface IPrerequisitesVmFactory : IVmFactory
{
PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall);
}
public interface IProfileEditorVmFactory : IVmFactory
{
ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen);
FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder);
LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer);
LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer);
LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer);
}
public interface ILayerPropertyVmFactory : IVmFactory
{
PropertyViewModel PropertyViewModel(ILayerProperty layerProperty);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect);
TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
TimelineViewModel TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels);
TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
}
public interface IDataBindingVmFactory : IVmFactory
{
DataBindingViewModel DataBindingViewModel();
}
public interface IPropertyVmFactory
{
ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
}
public interface INodeVmFactory : IVmFactory
{
NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview);
NodePickerViewModel NodePickerViewModel(NodeScript nodeScript);
NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node);
CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to);
DragCableViewModel DragCableViewModel(PinViewModel pinViewModel);
InputPinViewModel InputPinViewModel(IPin inputPin, NodeScriptViewModel nodeScriptViewModel);
OutputPinViewModel OutputPinViewModel(IPin outputPin, NodeScriptViewModel nodeScriptViewModel);
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
}
public interface IConditionVmFactory : IVmFactory
{
AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition);
PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition);
StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
}
public interface ILayerHintVmFactory : IVmFactory
{
CategoryAdaptionHintViewModel CategoryAdaptionHintViewModel(Layer layer, CategoryAdaptionHint adaptionHint);
DeviceAdaptionHintViewModel DeviceAdaptionHintViewModel(Layer layer, DeviceAdaptionHint adaptionHint);
KeyboardSectionAdaptionHintViewModel KeyboardSectionAdaptionHintViewModel(Layer layer, KeyboardSectionAdaptionHint adaptionHint);
}
public interface IScriptVmFactory : IVmFactory
{
ScriptConfigurationViewModel ScriptConfigurationViewModel(ScriptConfiguration scriptConfiguration);
ScriptConfigurationViewModel ScriptConfigurationViewModel(Profile profile, ScriptConfiguration scriptConfiguration);
}

View File

@ -1,31 +0,0 @@
using System;
using System.Reflection;
using Artemis.Core;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree;
using Ninject.Extensions.Factory;
namespace Artemis.UI.Ninject.InstanceProviders;
public class LayerPropertyViewModelInstanceProvider : StandardInstanceProvider
{
protected override Type GetType(MethodInfo methodInfo, object[] arguments)
{
if (methodInfo.ReturnType != typeof(ITreePropertyViewModel) && methodInfo.ReturnType != typeof(ITimelinePropertyViewModel))
return base.GetType(methodInfo, arguments);
// Find LayerProperty type
Type? layerPropertyType = arguments[0].GetType();
while (layerPropertyType != null && (!layerPropertyType.IsGenericType || layerPropertyType.GetGenericTypeDefinition() != typeof(LayerProperty<>)))
layerPropertyType = layerPropertyType.BaseType;
if (layerPropertyType == null)
return base.GetType(methodInfo, arguments);
if (methodInfo.ReturnType == typeof(ITreePropertyViewModel))
return typeof(TreePropertyViewModel<>).MakeGenericType(layerPropertyType.GetGenericArguments());
if (methodInfo.ReturnType == typeof(ITimelinePropertyViewModel))
return typeof(TimelinePropertyViewModel<>).MakeGenericType(layerPropertyType.GetGenericArguments());
return base.GetType(methodInfo, arguments);
}
}

View File

@ -1,75 +0,0 @@
using System;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Ninject.InstanceProviders;
using Artemis.UI.Screens;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Factory;
using Ninject.Modules;
using Ninject.Planning.Bindings.Resolvers;
namespace Artemis.UI.Ninject;
public class UIModule : NinjectModule
{
public override void Load()
{
if (Kernel == null)
throw new ArgumentNullException("Kernel shouldn't be null here.");
Kernel.Components.Add<IMissingBindingResolver, SelfBindingResolver>();
Kernel.Bind<IAssetLoader>().ToConstant(new AssetLoader());
Kernel.Bind(x =>
{
x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ViewModelBase>()
.BindToSelf();
});
Kernel.Bind(x =>
{
x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<MainScreenViewModel>()
.BindAllBaseClasses();
});
Kernel.Bind(x =>
{
x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<IToolViewModel>()
.BindAllInterfaces();
});
// Bind UI factories
Kernel.Bind(x =>
{
x.FromThisAssembly()
.SelectAllInterfaces()
.InheritedFrom<IVmFactory>()
.BindToFactory();
});
Kernel.Bind<NodeScriptWindowViewModelBase>().To<NodeScriptWindowViewModel>();
Kernel.Bind<IPropertyVmFactory>().ToFactory(() => new LayerPropertyViewModelInstanceProvider());
// Bind all UI services as singletons
Kernel.Bind(x =>
{
x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<IArtemisUIService>()
.BindAllInterfaces()
.Configure(c => c.InSingletonScope());
});
}
}

View File

@ -4,7 +4,7 @@ using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using ReactiveUI; using ReactiveUI;
using RGB.NET.Core; using RGB.NET.Core;

View File

@ -2,7 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.Settings;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -82,7 +82,7 @@ public class DeviceSettingsViewModel : ActivatableViewModelBase
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle($"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input") .WithTitle($"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input")
.WithViewModel<DeviceDetectInputViewModel>(out DeviceDetectInputViewModel? viewModel, ("device", Device)) .WithViewModel(out DeviceDetectInputViewModel viewModel, Device)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.ShowAsync(); .ShowAsync();

View File

@ -54,7 +54,7 @@ public class DeviceLogicalLayoutDialogViewModel : ContentDialogViewModelBase
{ {
await windowService.CreateContentDialog() await windowService.CreateContentDialog()
.WithTitle("Select logical layout") .WithTitle("Select logical layout")
.WithViewModel(out DeviceLogicalLayoutDialogViewModel vm, ("device", device)) .WithViewModel(out DeviceLogicalLayoutDialogViewModel vm, device)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary) .WithDefaultButton(ContentDialogButton.Primary)
.HavingPrimaryButton(b => b.WithText("Select").WithCommand(vm.ApplyLogicalLayout)) .HavingPrimaryButton(b => b.WithText("Select").WithCommand(vm.ApplyLogicalLayout))

View File

@ -25,7 +25,7 @@ public class DevicePhysicalLayoutDialogViewModel : ContentDialogViewModelBase
{ {
await windowService.CreateContentDialog() await windowService.CreateContentDialog()
.WithTitle("Select physical layout") .WithTitle("Select physical layout")
.WithViewModel(out DevicePhysicalLayoutDialogViewModel vm, ("device", device)) .WithViewModel(out DevicePhysicalLayoutDialogViewModel vm, device)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.ShowAsync(); .ShowAsync();

View File

@ -7,7 +7,7 @@ using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Avalonia.Threading; using Avalonia.Threading;
@ -89,7 +89,7 @@ public class PluginPrerequisitesInstallDialogViewModel : ContentDialogViewModelB
{ {
await windowService.CreateContentDialog() await windowService.CreateContentDialog()
.WithTitle("Plugin prerequisites") .WithTitle("Plugin prerequisites")
.WithViewModel(out PluginPrerequisitesInstallDialogViewModel vm, ("subjects", subjects)) .WithViewModel(out PluginPrerequisitesInstallDialogViewModel vm, subjects)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.HavingPrimaryButton(b => b.WithText("Install").WithCommand(vm.Install)) .HavingPrimaryButton(b => b.WithText("Install").WithCommand(vm.Install))
.WithDefaultButton(ContentDialogButton.Primary) .WithDefaultButton(ContentDialogButton.Primary)

View File

@ -8,7 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Avalonia.Threading; using Avalonia.Threading;
@ -74,7 +74,7 @@ public class PluginPrerequisitesUninstallDialogViewModel : ContentDialogViewMode
{ {
await windowService.CreateContentDialog() await windowService.CreateContentDialog()
.WithTitle("Plugin prerequisites") .WithTitle("Plugin prerequisites")
.WithViewModel(out PluginPrerequisitesUninstallDialogViewModel vm, ("subjects", subjects)) .WithViewModel(out PluginPrerequisitesUninstallDialogViewModel vm, subjects)
.WithCloseButtonText(cancelLabel) .WithCloseButtonText(cancelLabel)
.HavingPrimaryButton(b => b.WithText("Uninstall").WithCommand(vm.Uninstall)) .HavingPrimaryButton(b => b.WithText("Uninstall").WithCommand(vm.Uninstall))
.WithDefaultButton(ContentDialogButton.Primary) .WithDefaultButton(ContentDialogButton.Primary)

View File

@ -4,7 +4,7 @@ using System.Reactive;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using ReactiveUI; using ReactiveUI;

View File

@ -13,8 +13,8 @@ using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using DryIoc;
using Material.Icons; using Material.Icons;
using Ninject;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.Plugins; namespace Artemis.UI.Screens.Plugins;
@ -209,8 +209,7 @@ public class PluginViewModel : ActivatableViewModelBase
try try
{ {
PluginConfigurationViewModel? viewModel = Plugin.Kernel!.Get(Plugin.ConfigurationDialog.Type) as PluginConfigurationViewModel; if (Plugin.Resolve(Plugin.ConfigurationDialog.Type) is not PluginConfigurationViewModel viewModel)
if (viewModel == null)
throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}"); throw new ArtemisUIException($"The type of a plugin configuration dialog must inherit {nameof(PluginConfigurationViewModel)}");
_window = _windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel)); _window = _windowService.ShowWindow(new PluginSettingsWindowViewModel(viewModel));

View File

@ -78,7 +78,7 @@ public class EventConditionViewModel : ActivatableViewModelBase
private async Task ExecuteOpenEditor() private async Task ExecuteOpenEditor()
{ {
await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(("nodeScript", _eventCondition.Script)); await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(_eventCondition.Script);
await _profileEditorService.SaveProfileAsync(); await _profileEditorService.SaveProfileAsync();
} }
} }

View File

@ -51,7 +51,7 @@ public class StaticConditionViewModel : ActivatableViewModelBase
private async Task ExecuteOpenEditor() private async Task ExecuteOpenEditor()
{ {
await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(("nodeScript", _staticCondition.Script)); await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(_staticCondition.Script);
await _profileEditorService.SaveProfileAsync(); await _profileEditorService.SaveProfileAsync();
} }
} }

View File

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands; using Artemis.UI.Shared.Services.ProfileEditor.Commands;

View File

@ -137,10 +137,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
if (ProfileConfiguration == null) if (ProfileConfiguration == null)
return; return;
await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>( await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>(ProfileConfiguration.Category, ProfileConfiguration);
("profileCategory", ProfileConfiguration.Category),
("profileConfiguration", ProfileConfiguration)
);
} }
private async Task ExecuteViewScripts() private async Task ExecuteViewScripts()
@ -148,7 +145,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
if (ProfileConfiguration?.Profile == null) if (ProfileConfiguration?.Profile == null)
return; return;
await _windowService.ShowDialogAsync<ScriptsDialogViewModel, object?>(("profile", ProfileConfiguration.Profile)); await _windowService.ShowDialogAsync<ScriptsDialogViewModel, object?>(ProfileConfiguration.Profile);
await _profileEditorService.SaveProfileAsync(); await _profileEditorService.SaveProfileAsync();
} }

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints; using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;

View File

@ -4,8 +4,8 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands; using Artemis.UI.Shared.Services.ProfileEditor.Commands;

View File

@ -4,8 +4,8 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands; using Artemis.UI.Shared.Services.ProfileEditor.Commands;

View File

@ -7,7 +7,7 @@ using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using ReactiveUI; using ReactiveUI;

View File

@ -7,8 +7,8 @@ using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs; using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -274,7 +274,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
if (ProfileElement is not Layer layer) if (ProfileElement is not Layer layer)
return; return;
await _windowService.ShowDialogAsync<LayerHintsDialogViewModel, bool>(("layer", layer)); await _windowService.ShowDialogAsync<LayerHintsDialogViewModel, bool>(layer);
await ProfileEditorService.SaveProfileAsync(); await ProfileEditorService.SaveProfileAsync();
} }

View File

@ -4,8 +4,8 @@ using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.VisualScripting; using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -88,7 +88,7 @@ public class DataBindingViewModel : ActivatableViewModelBase
try try
{ {
_editorOpen = true; _editorOpen = true;
await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(("nodeScript", LayerProperty.BaseDataBinding.Script)); await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(LayerProperty.BaseDataBinding.Script);
await _profileEditorService.SaveProfileAsync(); await _profileEditorService.SaveProfileAsync();
} }
finally finally

View File

@ -10,7 +10,7 @@ using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.ProfileEditor.Playback; using Artemis.UI.Screens.ProfileEditor.Playback;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding; using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Dialogs; using Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
@ -124,7 +124,7 @@ public class PropertiesViewModel : ActivatableViewModelBase
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle("Add layer effect") .WithTitle("Add layer effect")
.WithViewModel(out AddEffectViewModel _, ("renderProfileElement", ProfileElement)) .WithViewModel(out AddEffectViewModel _, ProfileElement)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.ShowAsync(); .ShowAsync();
} }

View File

@ -6,7 +6,7 @@ using System.Reflection;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree; using Artemis.UI.Screens.ProfileEditor.Properties.Tree;

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree; using Artemis.UI.Screens.ProfileEditor.Properties.Tree;
using DynamicData; using DynamicData;

View File

@ -225,7 +225,7 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
{ {
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle("Edit segment length") .WithTitle("Edit segment length")
.WithViewModel(out TimelineSegmentEditViewModel vm, ("segmentLength", Length)) .WithViewModel(out TimelineSegmentEditViewModel vm, Length)
.HavingPrimaryButton(b => b.WithText("Save").WithAction(() => .HavingPrimaryButton(b => b.WithText("Save").WithAction(() =>
{ {
if (_profileElement != null) if (_profileElement != null)

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reflection; using System.Reflection;
@ -18,8 +17,6 @@ using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands; using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Ninject;
using Ninject.Parameters;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree; namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree;
@ -88,12 +85,7 @@ public class TreeGroupViewModel : ActivatableViewModelBase
if (constructors.Length != 1) if (constructors.Length != 1)
throw new ArtemisUIException("Brush configuration dialogs must have exactly one constructor"); throw new ArtemisUIException("Brush configuration dialogs must have exactly one constructor");
// Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure BrushConfigurationViewModel viewModel = (BrushConfigurationViewModel) LayerBrush.Descriptor.Provider.Plugin.Resolve(configurationViewModel.Type, LayerBrush);
ParameterInfo brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
ConstructorArgument argument = new(brushParameter.Name!, LayerBrush);
BrushConfigurationViewModel viewModel =
(BrushConfigurationViewModel) LayerBrush.Descriptor.Provider.Plugin.Kernel!.Get(configurationViewModel.Type, argument);
_brushConfigurationWindowViewModel = new BrushConfigurationWindowViewModel(viewModel, configurationViewModel); _brushConfigurationWindowViewModel = new BrushConfigurationWindowViewModel(viewModel, configurationViewModel);
await _windowService.ShowDialogAsync(_brushConfigurationWindowViewModel); await _windowService.ShowDialogAsync(_brushConfigurationWindowViewModel);
@ -118,12 +110,7 @@ public class TreeGroupViewModel : ActivatableViewModelBase
if (constructors.Length != 1) if (constructors.Length != 1)
throw new ArtemisUIException("Effect configuration dialogs must have exactly one constructor"); throw new ArtemisUIException("Effect configuration dialogs must have exactly one constructor");
// Find the BaseLayerEffect parameter, it is required by the base constructor so its there for sure EffectConfigurationViewModel viewModel = (EffectConfigurationViewModel) LayerEffect.Descriptor.Provider.Plugin.Resolve(configurationViewModel.Type, LayerEffect);
ParameterInfo effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
ConstructorArgument argument = new(effectParameter.Name!, LayerEffect);
EffectConfigurationViewModel viewModel =
(EffectConfigurationViewModel) LayerEffect.Descriptor.Provider.Plugin.Kernel!.Get(configurationViewModel.Type, argument);
_effectConfigurationWindowViewModel = new EffectConfigurationWindowViewModel(viewModel, configurationViewModel); _effectConfigurationWindowViewModel = new EffectConfigurationWindowViewModel(viewModel, configurationViewModel);
await _windowService.ShowDialogAsync(_effectConfigurationWindowViewModel); await _windowService.ShowDialogAsync(_effectConfigurationWindowViewModel);
@ -143,7 +130,7 @@ public class TreeGroupViewModel : ActivatableViewModelBase
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle("Rename layer effect") .WithTitle("Rename layer effect")
.WithViewModel(out LayerEffectRenameViewModel vm, ("layerEffect", LayerEffect)) .WithViewModel(out LayerEffectRenameViewModel vm, LayerEffect)
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm)) .HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary) .WithDefaultButton(ContentDialogButton.Primary)

View File

@ -7,7 +7,7 @@ using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers; using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;

View File

@ -3,15 +3,14 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Models; using Artemis.UI.Models;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Sidebar; using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.MainWindow; using Artemis.UI.Shared.Services.MainWindow;
using Avalonia; using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;

View File

@ -44,7 +44,7 @@ public class ScriptConfigurationViewModel : ActivatableViewModelBase
{ {
ContentDialogResult contentDialogResult = await _windowService.CreateContentDialog() ContentDialogResult contentDialogResult = await _windowService.CreateContentDialog()
.WithTitle("Edit script") .WithTitle("Edit script")
.WithViewModel(out ScriptConfigurationEditViewModel vm, ("scriptConfiguration", scriptConfiguration)) .WithViewModel(out ScriptConfigurationEditViewModel vm, scriptConfiguration)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Submit)) .HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Submit))
.HavingSecondaryButton(b => b.WithText("Delete")) .HavingSecondaryButton(b => b.WithText("Delete"))

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.ScriptingProviders; using Artemis.Core.ScriptingProviders;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Scripting.Dialogs; using Artemis.UI.Screens.Scripting.Dialogs;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -191,6 +191,7 @@
<StackPanel Grid.Row="0" Grid.Column="0"> <StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Classes="library-name">Avalonia</TextBlock> <TextBlock Classes="library-name">Avalonia</TextBlock>
<TextBlock Classes="library-name">DryIoc</TextBlock>
<TextBlock Classes="library-name">FluentAvalonia</TextBlock> <TextBlock Classes="library-name">FluentAvalonia</TextBlock>
<TextBlock Classes="library-name">EmbedIO</TextBlock> <TextBlock Classes="library-name">EmbedIO</TextBlock>
<TextBlock Classes="library-name">Furl.Http</TextBlock> <TextBlock Classes="library-name">Furl.Http</TextBlock>
@ -198,7 +199,6 @@
<TextBlock Classes="library-name">LiteDB</TextBlock> <TextBlock Classes="library-name">LiteDB</TextBlock>
<TextBlock Classes="library-name">McMaster.NETCore.Plugins</TextBlock> <TextBlock Classes="library-name">McMaster.NETCore.Plugins</TextBlock>
<TextBlock Classes="library-name">Newtonsoft.Json</TextBlock> <TextBlock Classes="library-name">Newtonsoft.Json</TextBlock>
<TextBlock Classes="library-name">Ninject</TextBlock>
<TextBlock Classes="library-name">RGB.NET</TextBlock> <TextBlock Classes="library-name">RGB.NET</TextBlock>
<TextBlock Classes="library-name">Serilog</TextBlock> <TextBlock Classes="library-name">Serilog</TextBlock>
<TextBlock Classes="library-name">SkiaSharp</TextBlock> <TextBlock Classes="library-name">SkiaSharp</TextBlock>
@ -208,6 +208,9 @@
<controls:HyperlinkButton NavigateUri="https://avaloniaui.net/"> <controls:HyperlinkButton NavigateUri="https://avaloniaui.net/">
https://avaloniaui.net/ https://avaloniaui.net/
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/dadhi/DryIoc">
https://github.com/dadhi/DryIoc
</controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/amwx/FluentAvalonia"> <controls:HyperlinkButton NavigateUri="https://github.com/amwx/FluentAvalonia">
https://github.com/amwx/FluentAvalonia https://github.com/amwx/FluentAvalonia
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -229,9 +232,6 @@
<controls:HyperlinkButton NavigateUri="https://www.newtonsoft.com/json"> <controls:HyperlinkButton NavigateUri="https://www.newtonsoft.com/json">
https://www.newtonsoft.com/json https://www.newtonsoft.com/json
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="http://www.ninject.org/">
http://www.ninject.org/
</controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/DarthAffe/RGB.NET"> <controls:HyperlinkButton NavigateUri="https://github.com/DarthAffe/RGB.NET">
https://github.com/DarthAffe/RGB.NET https://github.com/DarthAffe/RGB.NET
</controls:HyperlinkButton> </controls:HyperlinkButton>

View File

@ -6,7 +6,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Device; using Artemis.UI.Screens.Device;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -16,8 +16,8 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers; using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Avalonia.Threading; using Avalonia.Threading;
using DryIoc;
using DynamicData; using DynamicData;
using Ninject;
using ReactiveUI; using ReactiveUI;
using Serilog.Events; using Serilog.Events;
@ -33,7 +33,7 @@ public class GeneralTabViewModel : ActivatableViewModelBase
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
private bool _startupWizardOpen; private bool _startupWizardOpen;
public GeneralTabViewModel(IKernel kernel, public GeneralTabViewModel(IContainer container,
ISettingsService settingsService, ISettingsService settingsService,
IPluginManagementService pluginManagementService, IPluginManagementService pluginManagementService,
IDebugService debugService, IDebugService debugService,
@ -45,10 +45,10 @@ public class GeneralTabViewModel : ActivatableViewModelBase
_debugService = debugService; _debugService = debugService;
_windowService = windowService; _windowService = windowService;
_updateService = updateService; _updateService = updateService;
_autoRunProvider = kernel.TryGet<IAutoRunProvider>(); _autoRunProvider = container.Resolve<IAutoRunProvider>(IfUnresolved.ReturnDefault);
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>(); List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
List<IGraphicsContextProvider> graphicsContextProviders = kernel.Get<List<IGraphicsContextProvider>>(); List<IGraphicsContextProvider> graphicsContextProviders = container.Resolve<List<IGraphicsContextProvider>>();
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors)); LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
GraphicsContexts = new ObservableCollection<string> {"Software"}; GraphicsContexts = new ObservableCollection<string> {"Software"};
GraphicsContexts.AddRange(graphicsContextProviders.Select(p => p.GraphicsContextName)); GraphicsContexts.AddRange(graphicsContextProviders.Select(p => p.GraphicsContextName));

View File

@ -7,7 +7,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Plugins; using Artemis.UI.Screens.Plugins;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -15,10 +15,10 @@ public class SidebarCategoryEditViewModel : ContentDialogViewModelBase
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private string? _categoryName; private string? _categoryName;
public SidebarCategoryEditViewModel(IProfileService profileService, ProfileCategory? category) public SidebarCategoryEditViewModel(IProfileService profileService, ProfileCategory category)
{ {
_profileService = profileService; _profileService = profileService;
_category = category; _category = category == ProfileCategory.Empty ? null : category;
if (_category != null) if (_category != null)
_categoryName = _category.Name; _categoryName = _category.Name;

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.VisualScripting; using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -16,7 +16,6 @@ using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Threading; using Avalonia.Threading;
using Material.Icons; using Material.Icons;
using Newtonsoft.Json;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.Sidebar; namespace Artemis.UI.Screens.Sidebar;
@ -43,7 +42,7 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase<ProfileConf
public ProfileConfigurationEditViewModel( public ProfileConfigurationEditViewModel(
ProfileCategory profileCategory, ProfileCategory profileCategory,
ProfileConfiguration? profileConfiguration, ProfileConfiguration profileConfiguration,
IWindowService windowService, IWindowService windowService,
IProfileService profileService, IProfileService profileService,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
@ -54,7 +53,10 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase<ProfileConf
_windowService = windowService; _windowService = windowService;
_profileService = profileService; _profileService = profileService;
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_profileConfiguration = profileConfiguration ?? profileService.CreateProfileConfiguration(profileCategory, "New profile", Enum.GetValues<MaterialIconKind>().First().ToString());
_profileConfiguration = profileConfiguration == ProfileConfiguration.Empty
? profileService.CreateProfileConfiguration(profileCategory, "New profile", Enum.GetValues<MaterialIconKind>().First().ToString())
: profileConfiguration;
_profileName = _profileConfiguration.Name; _profileName = _profileConfiguration.Name;
_iconType = _profileConfiguration.Icon.IconType; _iconType = _profileConfiguration.Icon.IconType;
_hotkeyMode = _profileConfiguration.HotkeyMode; _hotkeyMode = _profileConfiguration.HotkeyMode;
@ -64,11 +66,9 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase<ProfileConf
if (_profileConfiguration.DisableHotkey != null) if (_profileConfiguration.DisableHotkey != null)
_disableHotkey = new Hotkey {Key = _profileConfiguration.DisableHotkey.Key, Modifiers = _profileConfiguration.DisableHotkey.Modifiers}; _disableHotkey = new Hotkey {Key = _profileConfiguration.DisableHotkey.Key, Modifiers = _profileConfiguration.DisableHotkey.Modifiers};
IsNew = profileConfiguration == null; IsNew = profileConfiguration == ProfileConfiguration.Empty;
DisplayName = IsNew ? "Artemis | Add profile" : "Artemis | Edit profile properties"; DisplayName = IsNew ? "Artemis | Add profile" : "Artemis | Edit profile properties";
Modules = new ObservableCollection<ProfileModuleViewModel?>( Modules = new ObservableCollection<ProfileModuleViewModel?>(pluginManagementService.GetFeaturesOfType<Module>().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m)));
pluginManagementService.GetFeaturesOfType<Module>().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m))
);
Modules.Insert(0, null); Modules.Insert(0, null);
_selectedModule = Modules.FirstOrDefault(m => m?.Module == _profileConfiguration.Module); _selectedModule = Modules.FirstOrDefault(m => m?.Module == _profileConfiguration.Module);
@ -258,7 +258,7 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase<ProfileConf
private async Task ExecuteOpenConditionEditor() private async Task ExecuteOpenConditionEditor()
{ {
await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(("nodeScript", ProfileConfiguration.ActivationCondition)); await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(ProfileConfiguration.ActivationCondition);
} }
#endregion #endregion

View File

@ -9,7 +9,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
@ -146,7 +146,7 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase
{ {
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle("Edit category") .WithTitle("Edit category")
.WithViewModel(out SidebarCategoryEditViewModel vm, ("category", ProfileCategory)) .WithViewModel(out SidebarCategoryEditViewModel vm, ProfileCategory)
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm)) .HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary) .WithDefaultButton(ContentDialogButton.Primary)
@ -165,10 +165,7 @@ public class SidebarCategoryViewModel : ActivatableViewModelBase
private async Task ExecuteAddProfile() private async Task ExecuteAddProfile()
{ {
ProfileConfiguration? result = await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>( ProfileConfiguration? result = await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>(ProfileCategory, ProfileConfiguration.Empty);
("profileCategory", ProfileCategory),
("profileConfiguration", null)
);
if (result != null) if (result != null)
{ {
SidebarProfileConfigurationViewModel viewModel = _vmFactory.SidebarProfileConfigurationViewModel(result); SidebarProfileConfigurationViewModel viewModel = _vmFactory.SidebarProfileConfigurationViewModel(result);

View File

@ -57,10 +57,7 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase
private async Task ExecuteEditProfile() private async Task ExecuteEditProfile()
{ {
await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>( await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>(ProfileConfiguration.Category, ProfileConfiguration);
("profileCategory", ProfileConfiguration.Category),
("profileConfiguration", ProfileConfiguration)
);
} }
private void ExecuteToggleSuspended() private void ExecuteToggleSuspended()

View File

@ -1,8 +1,7 @@
using System; using System;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using DryIoc;
using Material.Icons; using Material.Icons;
using Ninject;
using Ninject.Parameters;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.Sidebar; namespace Artemis.UI.Screens.Sidebar;
@ -15,9 +14,9 @@ public class SidebarScreenViewModel<T> : SidebarScreenViewModel where T : MainSc
public override Type ScreenType => typeof(T); public override Type ScreenType => typeof(T);
public override MainScreenViewModel CreateInstance(IKernel kernel, IScreen screen) public override MainScreenViewModel CreateInstance(IContainer container, IScreen screen)
{ {
return kernel.Get<T>(new ConstructorArgument("hostScreen", screen)); return container.Resolve<T>(new object[] { screen });
} }
} }
@ -32,5 +31,5 @@ public abstract class SidebarScreenViewModel : ViewModelBase
public MaterialIconKind Icon { get; } public MaterialIconKind Icon { get; }
public abstract Type ScreenType { get; } public abstract Type ScreenType { get; }
public abstract MainScreenViewModel CreateInstance(IKernel kernel, IScreen screen); public abstract MainScreenViewModel CreateInstance(IContainer container, IScreen screen);
} }

View File

@ -7,7 +7,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Home; using Artemis.UI.Screens.Home;
using Artemis.UI.Screens.ProfileEditor; using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.Settings;
@ -18,10 +18,10 @@ using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia.Threading; using Avalonia.Threading;
using DryIoc;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using Material.Icons; using Material.Icons;
using Ninject;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.Sidebar; namespace Artemis.UI.Screens.Sidebar;
@ -29,7 +29,7 @@ namespace Artemis.UI.Screens.Sidebar;
public class SidebarViewModel : ActivatableViewModelBase public class SidebarViewModel : ActivatableViewModelBase
{ {
private readonly IScreen _hostScreen; private readonly IScreen _hostScreen;
private readonly IKernel _kernel; private readonly IContainer _container;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IProfileEditorVmFactory _profileEditorVmFactory; private readonly IProfileEditorVmFactory _profileEditorVmFactory;
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
@ -37,7 +37,7 @@ public class SidebarViewModel : ActivatableViewModelBase
private ReadOnlyObservableCollection<SidebarCategoryViewModel> _sidebarCategories = new(new ObservableCollection<SidebarCategoryViewModel>()); private ReadOnlyObservableCollection<SidebarCategoryViewModel> _sidebarCategories = new(new ObservableCollection<SidebarCategoryViewModel>());
public SidebarViewModel(IScreen hostScreen, public SidebarViewModel(IScreen hostScreen,
IKernel kernel, IContainer container,
IProfileService profileService, IProfileService profileService,
IWindowService windowService, IWindowService windowService,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
@ -45,7 +45,7 @@ public class SidebarViewModel : ActivatableViewModelBase
IProfileEditorVmFactory profileEditorVmFactory) IProfileEditorVmFactory profileEditorVmFactory)
{ {
_hostScreen = hostScreen; _hostScreen = hostScreen;
_kernel = kernel; _container = container;
_windowService = windowService; _windowService = windowService;
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_profileEditorVmFactory = profileEditorVmFactory; _profileEditorVmFactory = profileEditorVmFactory;
@ -120,7 +120,7 @@ public class SidebarViewModel : ActivatableViewModelBase
{ {
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle("Add new category") .WithTitle("Add new category")
.WithViewModel(out SidebarCategoryEditViewModel vm, ("category", null)) .WithViewModel(out SidebarCategoryEditViewModel vm, ProfileCategory.Empty)
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm)) .HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary) .WithDefaultButton(ContentDialogButton.Primary)
@ -137,7 +137,7 @@ public class SidebarViewModel : ActivatableViewModelBase
private void NavigateToScreen(SidebarScreenViewModel sidebarScreenViewModel) private void NavigateToScreen(SidebarScreenViewModel sidebarScreenViewModel)
{ {
_hostScreen.Router.Navigate.Execute(sidebarScreenViewModel.CreateInstance(_kernel, _hostScreen)); _hostScreen.Router.Navigate.Execute(sidebarScreenViewModel.CreateInstance(_container, _hostScreen));
_profileEditorService.ChangeCurrentProfileConfiguration(null); _profileEditorService.ChangeCurrentProfileConfiguration(null);
} }
} }

View File

@ -8,13 +8,13 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DeviceProviders; using Artemis.Core.DeviceProviders;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Plugins; using Artemis.UI.Screens.Plugins;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers; using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Ninject; using DryIoc;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.StartupWizard; namespace Artemis.UI.Screens.StartupWizard;
@ -31,14 +31,14 @@ public class StartupWizardViewModel : DialogViewModelBase<bool>
private bool _showFinish; private bool _showFinish;
private bool _showGoBack; private bool _showGoBack;
public StartupWizardViewModel(IKernel kernel, ISettingsService settingsService, IRgbService rgbService, IPluginManagementService pluginManagementService, IWindowService windowService, public StartupWizardViewModel(IContainer container, ISettingsService settingsService, IRgbService rgbService, IPluginManagementService pluginManagementService, IWindowService windowService,
IUpdateService updateService, ISettingsVmFactory settingsVmFactory) IUpdateService updateService, ISettingsVmFactory settingsVmFactory)
{ {
_settingsService = settingsService; _settingsService = settingsService;
_rgbService = rgbService; _rgbService = rgbService;
_windowService = windowService; _windowService = windowService;
_updateService = updateService; _updateService = updateService;
_autoRunProvider = kernel.TryGet<IAutoRunProvider>(); _autoRunProvider = container.Resolve<IAutoRunProvider>(IfUnresolved.ReturnDefault);
Continue = ReactiveCommand.Create(ExecuteContinue); Continue = ReactiveCommand.Create(ExecuteContinue);
GoBack = ReactiveCommand.Create(ExecuteGoBack); GoBack = ReactiveCommand.Create(ExecuteGoBack);

View File

@ -57,7 +57,7 @@ public class ListDeviceViewModel : ViewModelBase
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle($"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input") .WithTitle($"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input")
.WithViewModel<DeviceDetectInputViewModel>(out DeviceDetectInputViewModel? viewModel, ("device", Device)) .WithViewModel(out DeviceDetectInputViewModel viewModel, Device)
.WithCloseButtonText("Cancel") .WithCloseButtonText("Cancel")
.ShowAsync(); .ShowAsync();

Some files were not shown because too many files have changed in this diff Show More