mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Plugins - Gave each plugin its own child Ninject kernel
Plugins - Added support for validation in VMs
This commit is contained in:
parent
693ea29600
commit
2547b6a8bf
19
src/Artemis.Core/Ninject/PluginModule.cs
Normal file
19
src/Artemis.Core/Ninject/PluginModule.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Ninject.Modules;
|
||||
|
||||
namespace Artemis.Core.Ninject
|
||||
{
|
||||
internal class PluginModule : NinjectModule
|
||||
{
|
||||
public PluginModule(PluginInfo pluginInfo)
|
||||
{
|
||||
PluginInfo = pluginInfo ?? throw new ArgumentNullException(nameof(pluginInfo));
|
||||
}
|
||||
|
||||
public PluginInfo PluginInfo { get; }
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,16 @@ namespace Artemis.Core.LayerBrushes
|
||||
LayerBrush = layerBrush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="BrushConfigurationViewModel" /> class with a validator
|
||||
/// </summary>
|
||||
/// <param name="layerBrush"></param>
|
||||
/// <param name="validator"></param>
|
||||
protected BrushConfigurationViewModel(BaseLayerBrush layerBrush, IModelValidator validator) : base(validator)
|
||||
{
|
||||
LayerBrush = layerBrush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layer brush this view model is associated with
|
||||
/// </summary>
|
||||
|
||||
@ -62,7 +62,7 @@ namespace Artemis.Core.LayerBrushes
|
||||
if (layer.LayerBrush != null)
|
||||
throw new ArtemisCoreException("Layer already has an instantiated layer brush");
|
||||
|
||||
var brush = (BaseLayerBrush) CoreService.Kernel.Get(LayerBrushType);
|
||||
var brush = (BaseLayerBrush) LayerBrushProvider.PluginInfo.Kernel.Get(LayerBrushType);
|
||||
brush.Layer = layer;
|
||||
brush.Descriptor = this;
|
||||
brush.Initialize();
|
||||
|
||||
@ -16,6 +16,16 @@ namespace Artemis.Core.LayerEffects
|
||||
LayerEffect = layerEffect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EffectConfigurationViewModel" /> class with a validator
|
||||
/// </summary>
|
||||
/// <param name="layerEffect"></param>
|
||||
/// <param name="validator"></param>
|
||||
protected EffectConfigurationViewModel(BaseLayerEffect layerEffect, IModelValidator validator) : base(validator)
|
||||
{
|
||||
LayerEffect = layerEffect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layer effect this view model is associated with
|
||||
/// </summary>
|
||||
|
||||
@ -46,7 +46,7 @@ namespace Artemis.Core.LayerEffects
|
||||
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
|
||||
/// </summary>
|
||||
public LayerEffectProvider LayerEffectProvider { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
|
||||
/// </summary>
|
||||
@ -67,7 +67,7 @@ namespace Artemis.Core.LayerEffects
|
||||
return;
|
||||
}
|
||||
|
||||
var effect = (BaseLayerEffect)CoreService.Kernel.Get(LayerEffectType);
|
||||
var effect = (BaseLayerEffect) LayerEffectProvider.PluginInfo.Kernel.Get(LayerEffectType);
|
||||
effect.ProfileElement = renderElement;
|
||||
effect.EntityId = entity.Id;
|
||||
effect.Order = entity.Order;
|
||||
|
||||
@ -16,6 +16,16 @@ namespace Artemis.Core
|
||||
Plugin = plugin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="PluginConfigurationViewModel" /> class with a validator
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <param name="validator"></param>
|
||||
protected PluginConfigurationViewModel(Plugin plugin, IModelValidator validator) : base(validator)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin this configuration view model is associated with
|
||||
/// </summary>
|
||||
|
||||
@ -4,6 +4,8 @@ using System.Reflection;
|
||||
using Artemis.Storage.Entities.Plugins;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using Newtonsoft.Json;
|
||||
using Ninject;
|
||||
using Ninject.Extensions.ChildKernel;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.Core
|
||||
@ -135,13 +137,18 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// The assembly the plugin code lives in
|
||||
/// </summary>
|
||||
internal Assembly Assembly { get; set; }
|
||||
public Assembly Assembly { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Ninject kernel of the plugin
|
||||
/// </summary>
|
||||
public IKernel Kernel { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity representing the plugin
|
||||
/// </summary>
|
||||
internal PluginEntity PluginEntity { get; set; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@ -41,6 +41,8 @@ namespace Artemis.Core.Services
|
||||
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
|
||||
{
|
||||
Kernel = kernel;
|
||||
Constants.CorePluginInfo.Kernel = kernel;
|
||||
|
||||
_logger = logger;
|
||||
_pluginService = pluginService;
|
||||
_rgbService = rgbService;
|
||||
|
||||
@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.Storage.Entities.Plugins;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using McMaster.NETCore.Plugins;
|
||||
@ -25,7 +26,6 @@ namespace Artemis.Core.Services
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPluginRepository _pluginRepository;
|
||||
private readonly List<PluginInfo> _plugins;
|
||||
private IKernel _childKernel;
|
||||
|
||||
public PluginService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
|
||||
{
|
||||
@ -110,9 +110,6 @@ namespace Artemis.Core.Services
|
||||
// Unload all currently loaded plugins first
|
||||
UnloadPlugins();
|
||||
|
||||
// Create a child kernel and app domain that will only contain the plugins
|
||||
_childKernel = new ChildKernel(_kernel);
|
||||
|
||||
// Load the plugin assemblies into the plugin context
|
||||
var pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins"));
|
||||
foreach (var subDirectory in pluginDirectory.EnumerateDirectories())
|
||||
@ -160,13 +157,6 @@ namespace Artemis.Core.Services
|
||||
while (_plugins.Count > 0)
|
||||
UnloadPlugin(_plugins[0]);
|
||||
|
||||
// Dispose the child kernel and therefore any leftover plugins instantiated with it
|
||||
if (_childKernel != null)
|
||||
{
|
||||
_childKernel.Dispose();
|
||||
_childKernel = null;
|
||||
}
|
||||
|
||||
_plugins.Clear();
|
||||
}
|
||||
}
|
||||
@ -232,7 +222,9 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
new Parameter("PluginInfo", pluginInfo, false)
|
||||
};
|
||||
pluginInfo.Instance = (Plugin) _childKernel.Get(pluginType, constraint: null, parameters: parameters);
|
||||
pluginInfo.Kernel = new ChildKernel(_kernel);
|
||||
pluginInfo.Kernel.Load(new PluginModule(pluginInfo));
|
||||
pluginInfo.Instance = (Plugin) pluginInfo.Kernel.Get(pluginType, constraint: null, parameters: parameters);
|
||||
pluginInfo.Instance.PluginInfo = pluginInfo;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -262,10 +254,10 @@ namespace Artemis.Core.Services
|
||||
OnPluginDisabled(new PluginEventArgs(pluginInfo));
|
||||
}
|
||||
|
||||
_childKernel.Unbind(pluginInfo.Instance.GetType());
|
||||
|
||||
pluginInfo.Instance.Dispose();
|
||||
pluginInfo.PluginLoader.Dispose();
|
||||
pluginInfo.Kernel.Dispose();
|
||||
|
||||
_plugins.Remove(pluginInfo);
|
||||
|
||||
OnPluginUnloaded(new PluginEventArgs(pluginInfo));
|
||||
|
||||
@ -58,7 +58,7 @@ namespace Artemis.UI.Shared.Services
|
||||
|
||||
return mainDataModel;
|
||||
}
|
||||
|
||||
|
||||
var dataModel = _dataModelService.GetPluginDataModel(plugin);
|
||||
if (dataModel == null)
|
||||
return null;
|
||||
@ -71,7 +71,7 @@ namespace Artemis.UI.Shared.Services
|
||||
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
|
||||
public DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes = null) where T : DataModelInputViewModel
|
||||
{
|
||||
if (compatibleConversionTypes == null)
|
||||
@ -165,7 +165,7 @@ namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
var match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
|
||||
if (match != null)
|
||||
return (DataModelDisplayViewModel) _kernel.Get(match.ViewModelType);
|
||||
return (DataModelDisplayViewModel) match.PluginInfo.Kernel.Get(match.ViewModelType);
|
||||
return !fallBackToDefault ? null : _kernel.Get<DefaultDataModelDisplayViewModel>();
|
||||
}
|
||||
}
|
||||
@ -222,7 +222,7 @@ namespace Artemis.UI.Shared.Services
|
||||
new ConstructorArgument("description", description),
|
||||
new ConstructorArgument("initialValue", initialValue)
|
||||
};
|
||||
var viewModel = (DataModelInputViewModel) _kernel.Get(registration.ViewModelType, parameters);
|
||||
var viewModel = (DataModelInputViewModel) registration.PluginInfo.Kernel.Get(registration.ViewModelType, parameters);
|
||||
viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes;
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
// TODO: Become plugin-aware and use plugin kernel if injected into a plugin
|
||||
internal class DialogService : IDialogService
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
|
||||
@ -273,7 +273,8 @@ namespace Artemis.UI.Shared.Services
|
||||
return null;
|
||||
|
||||
var parameter = new ConstructorArgument("layerProperty", layerProperty);
|
||||
return (PropertyInputViewModel<T>) Kernel.Get(viewModelType, parameter);
|
||||
var kernel = registration != null ? registration.PluginInfo.Kernel : Kernel;
|
||||
return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter);
|
||||
}
|
||||
|
||||
public ProfileModule GetCurrentModule()
|
||||
|
||||
32
src/Artemis.UI/Ninject/PluginUIModule.cs
Normal file
32
src/Artemis.UI/Ninject/PluginUIModule.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Stylet;
|
||||
using FluentValidation;
|
||||
using Ninject.Extensions.Conventions;
|
||||
using Ninject.Modules;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Ninject
|
||||
{
|
||||
public class PluginUIModule : NinjectModule
|
||||
{
|
||||
public PluginUIModule(PluginInfo pluginInfo)
|
||||
{
|
||||
PluginInfo = pluginInfo ?? throw new ArgumentNullException(nameof(pluginInfo));
|
||||
}
|
||||
|
||||
public PluginInfo PluginInfo { get; }
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
Bind(typeof(IModelValidator<>)).To(typeof(FluentValidationAdapter<>));
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.From(PluginInfo.Assembly)
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IValidator>()
|
||||
.BindAllInterfaces();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,6 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Ninject
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public class UIModule : NinjectModule
|
||||
{
|
||||
public override void Load()
|
||||
|
||||
@ -10,16 +10,14 @@ namespace Artemis.UI.Screens.Modules
|
||||
{
|
||||
public class ModuleRootViewModel : Conductor<Screen>.Collection.OneActive
|
||||
{
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IModuleVmFactory _moduleVmFactory;
|
||||
|
||||
public ModuleRootViewModel(Module module, IModuleVmFactory moduleVmFactory, IKernel kernel)
|
||||
public ModuleRootViewModel(Module module, IModuleVmFactory moduleVmFactory)
|
||||
{
|
||||
DisplayName = module?.DisplayName;
|
||||
Module = module;
|
||||
|
||||
_moduleVmFactory = moduleVmFactory;
|
||||
_kernel = kernel;
|
||||
}
|
||||
|
||||
public Module Module { get; }
|
||||
@ -47,7 +45,7 @@ namespace Artemis.UI.Screens.Modules
|
||||
var module = new ConstructorArgument("module", Module);
|
||||
var displayName = new ConstructorArgument("displayName", DisplayName);
|
||||
|
||||
var viewModel = (ModuleViewModel) _kernel.Get(moduleTab.Type, module, displayName);
|
||||
var viewModel = (ModuleViewModel) Module.PluginInfo.Kernel.Get(moduleTab.Type, module, displayName);
|
||||
Items.Add(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
// Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure
|
||||
var brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
|
||||
var argument = new ConstructorArgument(brushParameter.Name, layerBrush);
|
||||
var viewModel = (BrushConfigurationViewModel) _kernel.Get(configurationViewModel.Type, argument);
|
||||
var viewModel = (BrushConfigurationViewModel) layerBrush.PluginInfo.Kernel.Get(configurationViewModel.Type, argument);
|
||||
|
||||
_windowManager.ShowDialog(new LayerBrushSettingsWindowViewModel(viewModel));
|
||||
}
|
||||
@ -86,7 +86,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
|
||||
var effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
|
||||
var argument = new ConstructorArgument(effectParameter.Name, layerEffect);
|
||||
var viewModel = (EffectConfigurationViewModel) _kernel.Get(configurationViewModel.Type, argument);
|
||||
var viewModel = (EffectConfigurationViewModel) layerEffect.PluginInfo.Kernel.Get(configurationViewModel.Type, argument);
|
||||
_windowManager.ShowDialog(new LayerEffectSettingsWindowViewModel(viewModel));
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@ -23,7 +23,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
public class PluginSettingsViewModel : PropertyChangedBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPluginService _pluginService;
|
||||
private readonly ISnackbarMessageQueue _snackbarMessageQueue;
|
||||
@ -33,7 +32,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
private PluginInfo _pluginInfo;
|
||||
|
||||
public PluginSettingsViewModel(Plugin plugin,
|
||||
IKernel kernel,
|
||||
ILogger logger,
|
||||
IWindowManager windowManager,
|
||||
IDialogService dialogService,
|
||||
@ -43,7 +41,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
Plugin = plugin;
|
||||
PluginInfo = plugin.PluginInfo;
|
||||
|
||||
_kernel = kernel;
|
||||
_logger = logger;
|
||||
_windowManager = windowManager;
|
||||
_dialogService = dialogService;
|
||||
@ -96,7 +93,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
|
||||
var pluginParameter = constructors.First().GetParameters().First(p => typeof(Plugin).IsAssignableFrom(p.ParameterType));
|
||||
var plugin = new ConstructorArgument(pluginParameter.Name, Plugin);
|
||||
var viewModel = (PluginConfigurationViewModel) _kernel.Get(configurationViewModel.Type, plugin);
|
||||
var viewModel = (PluginConfigurationViewModel) PluginInfo.Kernel.Get(configurationViewModel.Type, plugin);
|
||||
_windowManager.ShowDialog(new PluginSettingsWindowViewModel(viewModel, Icon));
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.DefaultTypes.DataModel.Display;
|
||||
using Artemis.UI.DefaultTypes.DataModel.Input;
|
||||
using Artemis.UI.Ninject;
|
||||
using Artemis.UI.PropertyInput;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared.Services;
|
||||
@ -11,14 +13,19 @@ namespace Artemis.UI.Services
|
||||
{
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IPluginService _pluginService;
|
||||
private bool _registeredBuiltInDataModelDisplays;
|
||||
private bool _registeredBuiltInDataModelInputs;
|
||||
private bool _registeredBuiltInPropertyEditors;
|
||||
|
||||
public RegistrationService(IDataModelUIService dataModelUIService, IProfileEditorService profileEditorService)
|
||||
public RegistrationService(IDataModelUIService dataModelUIService, IProfileEditorService profileEditorService, IPluginService pluginService)
|
||||
{
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_profileEditorService = profileEditorService;
|
||||
_pluginService = pluginService;
|
||||
|
||||
LoadPluginModules();
|
||||
pluginService.PluginLoaded += PluginServiceOnPluginLoaded;
|
||||
}
|
||||
|
||||
public void RegisterBuiltInDataModelDisplays()
|
||||
@ -63,6 +70,17 @@ namespace Artemis.UI.Services
|
||||
|
||||
_registeredBuiltInPropertyEditors = true;
|
||||
}
|
||||
|
||||
private void PluginServiceOnPluginLoaded(object? sender, PluginEventArgs e)
|
||||
{
|
||||
e.PluginInfo.Kernel.Load(new[] { new PluginUIModule(e.PluginInfo) });
|
||||
}
|
||||
|
||||
private void LoadPluginModules()
|
||||
{
|
||||
foreach (var pluginInfo in _pluginService.GetAllPluginInfo())
|
||||
pluginInfo.Kernel.Load(new[] { new PluginUIModule(pluginInfo) });
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRegistrationService : IArtemisUIService
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user