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

Plugins - Implemented features

Core - Removed Stylet dependency for #500
This commit is contained in:
SpoinkyNL 2020-11-11 23:53:52 +01:00
parent b56966c585
commit e812929215
139 changed files with 1635 additions and 1106 deletions

View File

@ -50,7 +50,6 @@
<PackageReference Include="Serilog.Sinks.Debug" Version="1.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="SkiaSharp" Version="1.68.3" />
<PackageReference Include="Stylet" Version="1.3.4" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Reflection.Metadata" Version="1.8.0" />

View File

@ -42,6 +42,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayerproperties_005Ctypes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayershapes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mvvm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=placeholders/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cattributes/@EntryIndexedValue">True</s:Boolean>

View File

@ -12,7 +12,7 @@ namespace Artemis.Core
/// <summary>
/// The full path to the Artemis application folder
/// </summary>
public static readonly string ApplicationFolder = Path.GetDirectoryName(typeof(Constants).Assembly.Location);
public static readonly string ApplicationFolder = Path.GetDirectoryName(typeof(Constants).Assembly.Location)!;
/// <summary>
/// The full path to the Artemis executable
@ -34,11 +34,16 @@ namespace Artemis.Core
/// </summary>
public static readonly PluginInfo CorePluginInfo = new PluginInfo
{
Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", IsEnabled = true
Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"
};
internal static readonly CorePluginImplementation CorePluginImplementation = new CorePluginImplementation {PluginInfo = CorePluginInfo};
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {PluginInfo = CorePluginInfo};
/// <summary>
/// The plugin used by core components of Artemis
/// </summary>
public static readonly Plugin CorePlugin = new Plugin(CorePluginInfo, new DirectoryInfo(ApplicationFolder));
internal static readonly CorePluginFeature CorePluginFeature = new CorePluginFeature {Plugin = CorePlugin};
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {Plugin = CorePlugin};
/// <summary>
/// A read-only collection containing all primitive numeric types

View File

@ -0,0 +1,18 @@
using System;
namespace Artemis.Core
{
public class PluginFeatureEventArgs : EventArgs
{
public PluginFeatureEventArgs()
{
}
public PluginFeatureEventArgs(PluginFeature pluginFeature)
{
PluginFeature = pluginFeature;
}
public PluginFeature PluginFeature { get; }
}
}

View File

@ -1,18 +0,0 @@
using System;
namespace Artemis.Core
{
public class PluginImplementationEventArgs : EventArgs
{
public PluginImplementationEventArgs()
{
}
public PluginImplementationEventArgs(PluginImplementation pluginImplementation)
{
PluginImplementation = pluginImplementation;
}
public PluginImplementation PluginImplementation { get; }
}
}

View File

@ -0,0 +1,24 @@
using System;
namespace Artemis.Core
{
public class ArtemisPluginFeatureException : Exception
{
public PluginFeature PluginFeature { get; }
public ArtemisPluginFeatureException(PluginFeature pluginFeature)
{
PluginFeature = pluginFeature;
}
public ArtemisPluginFeatureException(PluginFeature pluginFeature, string message) : base(message)
{
PluginFeature = pluginFeature;
}
public ArtemisPluginFeatureException(PluginFeature pluginFeature, string message, Exception inner) : base(message, inner)
{
PluginFeature = pluginFeature;
}
}
}

View File

@ -0,0 +1,72 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Artemis.Core
{
/// <summary>
/// Represents a basic bindable class which notifies when a property value changes.
/// </summary>
public abstract class CorePropertyChanged : INotifyPropertyChanged
{
#region Events
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
/// <summary>
/// Checks if the property already matches the desirec value or needs to be updated.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual bool RequiresUpdate<T>(ref T storage, T value)
{
return !Equals(storage, value);
}
/// <summary>
/// Checks if the property already matches the desired value and updates it if not.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute" />
/// .
/// </param>
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
protected virtual bool SetAndNotify<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (!RequiresUpdate(ref storage, value)) return false;
storage = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Triggers the <see cref="PropertyChanged" />-event when a a property value has changed.
/// </summary>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute" />
/// .
/// </param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@ -5,7 +5,6 @@ using System.Linq;
using System.Runtime.CompilerServices;
using Artemis.Core.Properties;
using SkiaSharp;
using Stylet;
namespace Artemis.Core
{
@ -19,13 +18,13 @@ namespace Artemis.Core
/// </summary>
public ColorGradient()
{
Stops = new BindableCollection<ColorGradientStop>();
Stops = new List<ColorGradientStop>();
}
/// <summary>
/// Gets a list of all the <see cref="ColorGradientStop" />s in the gradient
/// </summary>
public BindableCollection<ColorGradientStop> Stops { get; }
public List<ColorGradientStop> Stops { get; }
/// <summary>
/// Gets all the colors in the color gradient

View File

@ -22,10 +22,10 @@ namespace Artemis.Core
public abstract string Icon { get; }
/// <summary>
/// Gets the plugin info this condition operator belongs to
/// Gets the plugin this condition operator belongs to
/// <para>Note: Not set until after registering</para>
/// </summary>
public PluginInfo PluginInfo { get; internal set; }
public Plugin Plugin { get; internal set; }
/// <summary>
/// Gets the left side type of this condition operator

View File

@ -346,7 +346,7 @@ namespace Artemis.Core
if (Operator != null)
{
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
Entity.OperatorPluginGuid = Operator.Plugin.Guid;
Entity.OperatorType = Operator.GetType().Name;
}
}
@ -358,7 +358,7 @@ namespace Artemis.Core
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
{
BaseConditionOperator conditionOperator = e.Registration.ConditionOperator;
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
if (Entity.OperatorPluginGuid == conditionOperator.Plugin.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
UpdateOperator(conditionOperator);
}

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
{
internal EventPredicateWrapperDataModel()
{
Implementation = Constants.CorePluginInfo;
Feature = Constants.CorePluginFeature;
}
[DataModelIgnore]

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
{
internal ListPredicateWrapperDataModel()
{
Implementation = Constants.CorePluginInfo;
Feature = Constants.CorePluginFeature;
}
[DataModelIgnore]

View File

@ -12,10 +12,10 @@ namespace Artemis.Core
public abstract class BaseDataBindingModifierType
{
/// <summary>
/// Gets the plugin info this data binding modifier belongs to
/// Gets the plugin this data binding modifier belongs to
/// <para>Note: Not set until after registering</para>
/// </summary>
public PluginInfo PluginInfo { get; internal set; }
public Plugin Plugin { get; internal set; }
/// <summary>
/// Gets the value type of this modifier type

View File

@ -238,7 +238,7 @@ namespace Artemis.Core
if (ModifierType != null)
{
Entity.ModifierType = ModifierType.GetType().Name;
Entity.ModifierTypePluginGuid = ModifierType.PluginInfo.Guid;
Entity.ModifierTypePluginGuid = ModifierType.Plugin.Guid;
}
// General
@ -286,7 +286,7 @@ namespace Artemis.Core
return;
BaseDataBindingModifierType modifierType = e.TypeRegistration.DataBindingModifierType;
if (modifierType.PluginInfo.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType)
if (modifierType.Plugin.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType)
UpdateModifierType(modifierType);
}

View File

@ -91,9 +91,9 @@ namespace Artemis.Core
public DataModel? Target { get; private set; }
/// <summary>
/// Gets the data model GUID of the <see cref="Target" /> if it is a <see cref="DataModel" />
/// Gets the data model ID of the <see cref="Target" /> if it is a <see cref="DataModel" />
/// </summary>
public Guid? DataModelGuid => Target?.Implementation.Guid;
public string? DataModelId => Target?.Feature.Id;
/// <summary>
/// Gets the point-separated path associated with this <see cref="DataModelPath" />
@ -267,8 +267,8 @@ namespace Artemis.Core
{
Path = Entity.Path;
if (Target == null && Entity.DataModelGuid != null)
Target = DataModelStore.Get(Entity.DataModelGuid.Value)?.DataModel;
if (Target == null && Entity.DataModelId != null)
Target = DataModelStore.Get(Entity.DataModelId)?.DataModel;
}
/// <inheritdoc />
@ -279,7 +279,7 @@ namespace Artemis.Core
return;
Entity.Path = Path;
Entity.DataModelGuid = DataModelGuid;
Entity.DataModelId = DataModelId;
Entity.WrapperType = Target switch
{
@ -295,7 +295,7 @@ namespace Artemis.Core
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
{
if (e.Registration.DataModel.Implementation.Guid != Entity.DataModelGuid)
if (e.Registration.DataModel.Feature.Id != Entity.DataModelId)
return;
Target = e.Registration.DataModel;
@ -304,7 +304,7 @@ namespace Artemis.Core
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
{
if (e.Registration.DataModel.Implementation.Guid != Entity.DataModelGuid)
if (e.Registration.DataModel.Feature.Id != Entity.DataModelId)
return;
Target = null;

View File

@ -190,9 +190,9 @@ namespace Artemis.Core
typeof(PropertyGroupDescriptionAttribute)
);
General.GroupDescription = (PropertyGroupDescriptionAttribute) generalAttribute;
General.Initialize(this, "General.", Constants.CorePluginInfo);
General.Initialize(this, "General.", Constants.CorePluginFeature);
Transform.GroupDescription = (PropertyGroupDescriptionAttribute) transformAttribute;
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo);
Transform.Initialize(this, "Transform.", Constants.CorePluginFeature);
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
ApplyShapeType();
@ -619,7 +619,7 @@ namespace Artemis.Core
BaseLayerBrush brush = LayerBrush;
DeactivateLayerBrush();
LayerEntity.PropertyEntities.RemoveAll(p => p.PluginGuid == brush.PluginInfo.Guid && p.Path.StartsWith("LayerBrush."));
LayerEntity.PropertyEntities.RemoveAll(p => p.FeatureId == brush.ProviderId && p.Path.StartsWith("LayerBrush."));
}
internal void ActivateLayerBrush()
@ -628,7 +628,7 @@ namespace Artemis.Core
if (current == null)
return;
LayerBrushDescriptor descriptor = LayerBrushStore.Get(current.BrushPluginGuid, current.BrushType)?.LayerBrushDescriptor;
LayerBrushDescriptor? descriptor = LayerBrushStore.Get(current.LayerBrushProviderId, current.BrushType)?.LayerBrushDescriptor;
descriptor?.CreateInstance(this);
OnLayerBrushUpdated();
@ -662,7 +662,7 @@ namespace Artemis.Core
return;
LayerBrushReference current = General.BrushReference.CurrentValue;
if (e.Registration.PluginImplementation.PluginInfo.Guid == current.BrushPluginGuid &&
if (e.Registration.PluginFeature.Id == current.LayerBrushProviderId &&
e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
ActivateLayerBrush();
}

View File

@ -1,5 +1,4 @@
using System;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerBrushes;
namespace Artemis.Core
{
@ -8,24 +7,24 @@ namespace Artemis.Core
/// </summary>
public class LayerBrushReference
{
/// <summary>
/// The GUID of the plugin the brush descriptor resides in
/// </summary>
public Guid BrushPluginGuid { get; set; }
/// <summary>
/// The full type name of the brush descriptor
/// </summary>
public string BrushType { get; set; }
public LayerBrushReference()
{
}
public LayerBrushReference(LayerBrushDescriptor descriptor)
{
BrushPluginGuid = descriptor.LayerBrushProvider.PluginInfo.Guid;
LayerBrushProviderId = descriptor.Provider.Id;
BrushType = descriptor.LayerBrushType.Name;
}
/// <summary>
/// The ID of the layer brush provided the brush was provided by
/// </summary>
public string LayerBrushProviderId { get; set; }
/// <summary>
/// The full type name of the brush descriptor
/// </summary>
public string BrushType { get; set; }
}
}

View File

@ -1,12 +1,11 @@
using System;
using Stylet;
namespace Artemis.Core
{
/// <summary>
/// Represents a keyframe on a <see cref="LayerProperty{T}" /> containing a value and a timestamp
/// </summary>
public class LayerPropertyKeyframe<T> : PropertyChangedBase
public class LayerPropertyKeyframe<T> : CorePropertyChanged
{
private LayerProperty<T> _layerProperty;
private TimeSpan _position;

View File

@ -28,11 +28,11 @@ namespace Artemis.Core
/// Gets the description of this group
/// </summary>
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
/// <summary>
/// Gets the info of the plugin this group is associated with
/// Gets the plugin feature this group is associated with
/// </summary>
public PluginInfo PluginInfo { get; internal set; }
public PluginFeature Feature { get; set; }
/// <summary>
/// Gets the profile element (such as layer or folder) this group is associated with
@ -145,18 +145,18 @@ namespace Artemis.Core
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
}
internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginInfo pluginInfo)
internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginFeature feature)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (pluginInfo == null)
throw new ArgumentNullException(nameof(pluginInfo));
if (feature == null)
throw new ArgumentNullException(nameof(feature));
// Doubt this will happen but let's make sure
if (PropertiesInitialized)
throw new ArtemisCoreException("Layer property group already initialized, wut");
PluginInfo = pluginInfo;
Feature = feature;
ProfileElement = profileElement;
Path = path.TrimEnd('.');
@ -246,7 +246,7 @@ namespace Artemis.Core
instance.GroupDescription = propertyGroupDescription;
instance.LayerBrush = LayerBrush;
instance.LayerEffect = LayerEffect;
instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", PluginInfo);
instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", Feature);
propertyInfo.SetValue(this, instance);
_layerPropertyGroups.Add(instance);
@ -254,11 +254,11 @@ namespace Artemis.Core
private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage)
{
PropertyEntity entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == PluginInfo.Guid && p.Path == path);
PropertyEntity entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path);
fromStorage = entity != null;
if (entity == null)
{
entity = new PropertyEntity {PluginGuid = PluginInfo.Guid, Path = path};
entity = new PropertyEntity {FeatureId = Feature.Id, Path = path};
profileElement.RenderElementEntity.PropertyEntities.Add(entity);
}

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
using SkiaSharp;
namespace Artemis.Core
@ -147,7 +146,7 @@ namespace Artemis.Core
throw new ObjectDisposedException("Profile");
ProfileEntity.Id = EntityId;
ProfileEntity.PluginGuid = Module.PluginInfo.Guid;
ProfileEntity.ModuleId = Module.Id;
ProfileEntity.Name = Name;
ProfileEntity.IsActive = IsActivated;

View File

@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using SkiaSharp;
using Stylet;
namespace Artemis.Core
{
public abstract class ProfileElement : PropertyChangedBase, IDisposable
public abstract class ProfileElement : CorePropertyChanged, IDisposable
{
private bool _enabled;
private Guid _entityId;

View File

@ -44,7 +44,7 @@ namespace Artemis.Core
LayerEffectEntity layerEffectEntity = new LayerEffectEntity
{
Id = layerEffect.EntityId,
PluginGuid = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.PluginInfo.Guid,
ProviderId = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.ProviderId,
EffectType = layerEffect.GetEffectTypeName(),
Name = layerEffect.Name,
Enabled = layerEffect.Enabled,
@ -215,11 +215,11 @@ namespace Artemis.Core
foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects)
{
// If there is a non-placeholder existing effect, skip this entity
BaseLayerEffect existing = _layerEffects.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id);
BaseLayerEffect? existing = _layerEffects.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id);
if (existing != null && existing.Descriptor.PlaceholderFor == null)
continue;
LayerEffectDescriptor descriptor = LayerEffectStore.Get(layerEffectEntity.PluginGuid, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
LayerEffectDescriptor? descriptor = LayerEffectStore.Get(layerEffectEntity.ProviderId, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
if (descriptor != null)
{
// If a descriptor is found but there is an existing placeholder, remove the placeholder
@ -235,7 +235,7 @@ namespace Artemis.Core
else if (existing == null)
{
// If no descriptor was found and there was no existing placeholder, create a placeholder
descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.PluginGuid);
descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId);
descriptor.CreateInstance(this, layerEffectEntity);
}
}
@ -253,22 +253,22 @@ namespace Artemis.Core
private void LayerEffectStoreOnLayerEffectRemoved(object sender, LayerEffectStoreEvent e)
{
// If effects provided by the plugin are on the element, replace them with placeholders
List<BaseLayerEffect> pluginEffects = _layerEffects.Where(ef => ef.Descriptor.LayerEffectProvider != null &&
ef.PluginInfo.Guid == e.Registration.PluginImplementation.PluginInfo.Guid).ToList();
List<BaseLayerEffect> pluginEffects = _layerEffects.Where(ef => ef.Descriptor.Provider != null &&
ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
foreach (BaseLayerEffect pluginEffect in pluginEffects)
{
LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId);
_layerEffects.Remove(pluginEffect);
pluginEffect.Dispose();
LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.PluginInfo.Guid);
LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.ProviderId);
descriptor.CreateInstance(this, entity);
}
}
private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e)
{
if (RenderElementEntity.LayerEffects.Any(ef => ef.PluginGuid == e.Registration.PluginImplementation.PluginInfo.Guid))
if (RenderElementEntity.LayerEffects.Any(ef => ef.ProviderId == e.Registration.PluginFeature.Id))
ActivateEffects();
}

View File

@ -3,14 +3,13 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Storage.Entities.Profile;
using Stylet;
namespace Artemis.Core
{
/// <summary>
/// Represents a timeline used by profile elements
/// </summary>
public class Timeline : PropertyChangedBase, IStorageModel
public class Timeline : CorePropertyChanged, IStorageModel
{
private const int MaxExtraTimelines = 15;
@ -275,20 +274,20 @@ namespace Artemis.Core
if (segment <= TimelineSegment.End)
{
if (startUpdated || segment < TimelineSegment.End)
NotifyOfPropertyChange(nameof(EndSegmentStartPosition));
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
OnPropertyChanged(nameof(EndSegmentStartPosition));
OnPropertyChanged(nameof(EndSegmentEndPosition));
}
if (segment <= TimelineSegment.Main)
{
if (startUpdated || segment < TimelineSegment.Main)
NotifyOfPropertyChange(nameof(MainSegmentStartPosition));
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
OnPropertyChanged(nameof(MainSegmentStartPosition));
OnPropertyChanged(nameof(MainSegmentEndPosition));
}
if (segment <= TimelineSegment.Start) NotifyOfPropertyChange(nameof(StartSegmentEndPosition));
if (segment <= TimelineSegment.Start) OnPropertyChanged(nameof(StartSegmentEndPosition));
NotifyOfPropertyChange(nameof(Length));
OnPropertyChanged(nameof(Length));
}
#endregion

View File

@ -4,20 +4,19 @@ using System.Linq;
using Artemis.Storage.Entities.Surface;
using RGB.NET.Core;
using SkiaSharp;
using Stylet;
namespace Artemis.Core
{
public class ArtemisDevice : PropertyChangedBase
public class ArtemisDevice : CorePropertyChanged
{
private ReadOnlyCollection<ArtemisLed> _leds;
private SKPath _renderPath;
private SKRect _renderRectangle;
internal ArtemisDevice(IRGBDevice rgbDevice, PluginImplementation pluginImplementation, ArtemisSurface surface)
internal ArtemisDevice(IRGBDevice rgbDevice, PluginFeature pluginFeature, ArtemisSurface surface)
{
RgbDevice = rgbDevice;
PluginImplementation = pluginImplementation;
PluginFeature = pluginFeature;
Surface = surface;
DeviceEntity = new DeviceEntity();
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
@ -30,10 +29,10 @@ namespace Artemis.Core
CalculateRenderProperties();
}
internal ArtemisDevice(IRGBDevice rgbDevice, PluginImplementation pluginImplementation, ArtemisSurface surface, DeviceEntity deviceEntity)
internal ArtemisDevice(IRGBDevice rgbDevice, PluginFeature pluginFeature, ArtemisSurface surface, DeviceEntity deviceEntity)
{
RgbDevice = rgbDevice;
PluginImplementation = pluginImplementation;
PluginFeature = pluginFeature;
Surface = surface;
DeviceEntity = deviceEntity;
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
@ -52,7 +51,7 @@ namespace Artemis.Core
}
public IRGBDevice RgbDevice { get; }
public PluginImplementation PluginImplementation { get; }
public PluginFeature PluginFeature { get; }
public ArtemisSurface Surface { get; }
public DeviceEntity DeviceEntity { get; }
@ -68,7 +67,7 @@ namespace Artemis.Core
set
{
DeviceEntity.X = value;
NotifyOfPropertyChange(nameof(X));
OnPropertyChanged(nameof(X));
}
}
@ -78,7 +77,7 @@ namespace Artemis.Core
set
{
DeviceEntity.Y = value;
NotifyOfPropertyChange(nameof(Y));
OnPropertyChanged(nameof(Y));
}
}
@ -88,7 +87,7 @@ namespace Artemis.Core
set
{
DeviceEntity.Rotation = value;
NotifyOfPropertyChange(nameof(Rotation));
OnPropertyChanged(nameof(Rotation));
}
}
@ -98,7 +97,7 @@ namespace Artemis.Core
set
{
DeviceEntity.Scale = value;
NotifyOfPropertyChange(nameof(Scale));
OnPropertyChanged(nameof(Scale));
}
}
@ -108,7 +107,7 @@ namespace Artemis.Core
set
{
DeviceEntity.ZIndex = value;
NotifyOfPropertyChange(nameof(ZIndex));
OnPropertyChanged(nameof(ZIndex));
}
}

View File

@ -1,10 +1,9 @@
using RGB.NET.Core;
using SkiaSharp;
using Stylet;
namespace Artemis.Core
{
public class ArtemisLed : PropertyChangedBase
public class ArtemisLed : CorePropertyChanged
{
private SKRect _absoluteRenderRectangle;
private SKRect _renderRectangle;

View File

@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Entities.Surface;
using RGB.NET.Core;
using Stylet;
namespace Artemis.Core
{
public class ArtemisSurface : PropertyChangedBase
public class ArtemisSurface : CorePropertyChanged
{
private List<ArtemisDevice> _devices;
private bool _isActive;

View File

@ -6,6 +6,7 @@ using Ninject.Activation;
namespace Artemis.Core.Ninject
{
// TODO: Investigate if this can't just be set as a constant on the plugin child kernel
internal class PluginSettingsProvider : Provider<PluginSettings>
{
private static readonly List<PluginSettings> PluginSettings = new List<PluginSettings>();
@ -25,21 +26,22 @@ namespace Artemis.Core.Ninject
throw new ArtemisCoreException("PluginSettings couldn't be injected, failed to get the injection parent request");
// First try by PluginInfo parameter
PluginInfo pluginInfo = parentRequest.Parameters.FirstOrDefault(p => p.Name == "PluginInfo")?.GetValue(context, null) as PluginInfo;
if (pluginInfo == null)
pluginInfo = _pluginManagementService.GetPluginByAssembly(parentRequest.Service.Assembly)?.PluginInfo;
Plugin? plugin = parentRequest.Parameters.FirstOrDefault(p => p.Name == "Plugin")?.GetValue(context, null) as Plugin;
// Fall back to assembly based detection
if (pluginInfo == null)
if (plugin == null)
plugin = _pluginManagementService.GetPluginByAssembly(parentRequest.Service.Assembly);
if (plugin == null)
throw new ArtemisCoreException("PluginSettings can only be injected with the PluginInfo parameter provided " +
"or into a class defined in a plugin assembly");
lock (PluginSettings)
{
PluginSettings? existingSettings = PluginSettings.FirstOrDefault(p => p.PluginInfo == pluginInfo);
PluginSettings? existingSettings = PluginSettings.FirstOrDefault(p => p.Plugin == plugin);
if (existingSettings != null)
return existingSettings;
PluginSettings? settings = new PluginSettings(pluginInfo, _pluginRepository);
PluginSettings? settings = new PluginSettings(plugin, _pluginRepository);
PluginSettings.Add(settings);
return settings;
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core.Ninject
protected override ISettingsService CreateInstance(IContext context)
{
IRequest parentRequest = context.Request.ParentRequest;
if (parentRequest == null || typeof(PluginImplementation).IsAssignableFrom(parentRequest.Service))
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

@ -14,10 +14,10 @@ namespace Artemis.Core.DataModelExpansions
private readonly Dictionary<string, DataModel> _dynamicDataModels = new Dictionary<string, DataModel>();
/// <summary>
/// Gets the plugin implementation this data model belongs to
/// Gets the plugin feature this data model belongs to
/// </summary>
[DataModelIgnore]
public DataModelPluginImplementation Implementation { get; internal set; }
public DataModelPluginFeature Feature { get; internal set; }
/// <summary>
/// Gets the <see cref="DataModelPropertyAttribute" /> describing this data model
@ -43,9 +43,9 @@ namespace Artemis.Core.DataModelExpansions
/// <returns></returns>
public ReadOnlyCollection<PropertyInfo> GetHiddenProperties()
{
if (Implementation is ProfileModule profileModule)
if (Feature is ProfileModule profileModule)
return profileModule.HiddenProperties;
if (Implementation is BaseDataModelExpansion dataModelExpansion)
if (Feature is BaseDataModelExpansion dataModelExpansion)
return dataModelExpansion.HiddenProperties;
return new List<PropertyInfo>().AsReadOnly();
@ -81,7 +81,7 @@ namespace Artemis.Core.DataModelExpansions
throw new ArtemisCoreException($"Cannot add a dynamic data model with key '{key}' " +
"because the key is already in use by a static property on this data model.");
dynamicDataModel.Implementation = Implementation;
dynamicDataModel.Feature = Feature;
dynamicDataModel.DataModelDescription = new DataModelPropertyAttribute
{
Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name,

View File

@ -45,7 +45,7 @@ namespace Artemis.Core.DataModelExpansions
internal override void InternalEnable()
{
DataModel = Activator.CreateInstance<T>();
DataModel.Implementation = PluginInfo;
DataModel.Feature = this;
DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable();
}

View File

@ -8,7 +8,7 @@ namespace Artemis.Core.DataModelExpansions
/// For internal use only, to implement your own layer property type, extend <see cref="DataModelExpansion{T}" />
/// instead.
/// </summary>
public abstract class BaseDataModelExpansion : DataModelPluginImplementation
public abstract class BaseDataModelExpansion : DataModelPluginFeature
{
/// <summary>
/// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c>
@ -35,7 +35,7 @@ namespace Artemis.Core.DataModelExpansions
/// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription()
{
return new DataModelPropertyAttribute {Name = PluginInfo.Name, Description = PluginInfo.Description};
return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
}
}
}

View File

@ -4,9 +4,9 @@ using System.Threading.Tasks;
namespace Artemis.Core
{
/// <summary>
/// Represents an implementation of a certain type provided by a plugin with support for data models
/// Represents an feature of a certain type provided by a plugin with support for data models
/// </summary>
public abstract class DataModelPluginImplementation : PluginImplementation
public abstract class DataModelPluginFeature : PluginFeature
{
/// <summary>
/// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="action" /> at the
@ -23,11 +23,11 @@ namespace Artemis.Core
{
if (action == null)
throw new ArgumentNullException(nameof(action));
return new TimedUpdateRegistration(PluginInfo, interval, action);
return new TimedUpdateRegistration(this, interval, action);
}
/// <summary>
/// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="action" /> at the
/// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="asyncAction" /> at the
/// provided
/// <paramref name="interval" />
/// </summary>
@ -41,7 +41,7 @@ namespace Artemis.Core
{
if (asyncAction == null)
throw new ArgumentNullException(nameof(asyncAction));
return new TimedUpdateRegistration(PluginInfo, interval, asyncAction);
return new TimedUpdateRegistration(this, interval, asyncAction);
}
}
}

View File

@ -10,7 +10,7 @@ namespace Artemis.Core.DeviceProviders
/// <summary>
/// Allows you to implement and register your own device provider
/// </summary>
public abstract class DeviceProvider : PluginImplementation
public abstract class DeviceProvider : PluginFeature
{
/// <summary>
/// Creates a new instance of the <see cref="DeviceProvider" /> class
@ -50,9 +50,9 @@ namespace Artemis.Core.DeviceProviders
{
// Start from the plugin directory
if (e.RelativePart != null && e.FileName != null)
e.FinalPath = Path.Combine(PluginInfo.Directory.FullName, e.RelativePart, e.FileName);
e.FinalPath = Path.Combine(Plugin.Directory.FullName, e.RelativePart, e.FileName);
else if (e.RelativePath != null)
e.FinalPath = Path.Combine(PluginInfo.Directory.FullName, e.RelativePath);
e.FinalPath = Path.Combine(Plugin.Directory.FullName, e.RelativePath);
IRGBDeviceInfo deviceInfo = ((IRGBDevice) sender).DeviceInfo;
if (e.FileName != null && !File.Exists(e.FinalPath))

View File

@ -0,0 +1,20 @@
namespace Artemis.Core
{
/// <summary>
/// An optional entry point for your plugin
/// </summary>
public interface IPluginBootstrapper
{
/// <summary>
/// Called when the plugin is activated
/// </summary>
/// <param name="plugin">The plugin instance of your plugin</param>
void Enable(Plugin plugin);
/// <summary>
/// Called when the plugin is deactivated or when Artemis shuts down
/// </summary>
/// <param name="plugin">The plugin instance of your plugin</param>
void Disable(Plugin plugin);
}
}

View File

@ -0,0 +1,7 @@
namespace Artemis.Core
{
public interface IPluginConfigurationDialog
{
}
}

View File

@ -0,0 +1,10 @@
namespace Artemis.Core
{
/// <summary>
/// Represents a view model for a plugin configuration window
/// </summary>
public interface IPluginConfigurationViewModel
{
}
}

View File

@ -0,0 +1,9 @@
namespace Artemis.Core.LayerBrushes
{
/// <summary>
/// Represents the configuration dialog of a layer brush
/// </summary>
public interface ILayerBrushConfigurationDialog
{
}
}

View File

@ -1,17 +1,15 @@
using System;
using Artemis.Core.Services;
using SkiaSharp;
using Stylet;
namespace Artemis.Core.LayerBrushes
{
/// <summary>
/// For internal use only, please use <see cref="LayerBrush{T}" /> or <see cref="RgbNetLayerBrush{T}" /> or instead
/// </summary>
public abstract class BaseLayerBrush : PropertyChangedBase, IDisposable
public abstract class BaseLayerBrush : CorePropertyChanged, IDisposable
{
private LayerBrushType _brushType;
private LayerBrushConfigurationDialog _configurationDialog;
private ILayerBrushConfigurationDialog _configurationDialog;
private LayerBrushDescriptor _descriptor;
private Layer _layer;
private bool _supportsTransformation = true;
@ -37,7 +35,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary>
/// Gets or sets a configuration dialog complementing the regular properties
/// </summary>
public LayerBrushConfigurationDialog ConfigurationDialog
public ILayerBrushConfigurationDialog ConfigurationDialog
{
get => _configurationDialog;
protected set => SetAndNotify(ref _configurationDialog, value);
@ -53,9 +51,9 @@ namespace Artemis.Core.LayerBrushes
}
/// <summary>
/// Gets the plugin info that defined this brush
/// Gets the ID of the <see cref="LayerBrushProvider" /> that provided this effect
/// </summary>
public PluginInfo PluginInfo => Descriptor.LayerBrushProvider.PluginInfo;
public string ProviderId => Descriptor?.Provider?.Id;
/// <summary>
/// Gets a reference to the layer property group without knowing it's type
@ -72,21 +70,11 @@ namespace Artemis.Core.LayerBrushes
protected set
{
if (value && BrushType == LayerBrushType.RgbNet)
throw new ArtemisPluginException(PluginInfo, "An RGB.NET brush cannot support transformation");
throw new ArtemisPluginFeatureException(Descriptor.Provider, "An RGB.NET brush cannot support transformation");
_supportsTransformation = value;
}
}
/// <inheritdoc />
public void Dispose()
{
DisableLayerBrush();
BaseProperties.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Called when the layer brush is activated
/// </summary>
@ -112,6 +100,16 @@ namespace Artemis.Core.LayerBrushes
internal virtual void Dispose(bool disposing)
{
}
/// <inheritdoc />
public void Dispose()
{
DisableLayerBrush();
BaseProperties.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
}
/// <summary>

View File

@ -37,7 +37,7 @@ namespace Artemis.Core.LayerBrushes
Properties = Activator.CreateInstance<T>();
Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description};
Properties.LayerBrush = this;
Properties.Initialize(Layer, "LayerBrush.", PluginInfo);
Properties.Initialize(Layer, "LayerBrush.", Descriptor.Provider);
PropertiesInitialized = true;
EnableLayerBrush();

View File

@ -1,5 +1,4 @@
using System;
using Artemis.Core.Services;
using Ninject;
namespace Artemis.Core.LayerBrushes
@ -9,13 +8,13 @@ namespace Artemis.Core.LayerBrushes
/// </summary>
public class LayerBrushDescriptor
{
internal LayerBrushDescriptor(string displayName, string description, string icon, Type layerBrushType, LayerBrushProvider layerBrushProvider)
internal LayerBrushDescriptor(string displayName, string description, string icon, Type layerBrushType, LayerBrushProvider provider)
{
DisplayName = displayName;
Description = description;
Icon = icon;
LayerBrushType = layerBrushType;
LayerBrushProvider = layerBrushProvider;
Provider = provider;
}
/// <summary>
@ -42,7 +41,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary>
/// The plugin that provided this <see cref="LayerBrushDescriptor" />
/// </summary>
public LayerBrushProvider LayerBrushProvider { get; }
public LayerBrushProvider Provider { get; }
/// <summary>
/// Determines whether the provided <paramref name="reference" /> references to a brush provided by this descriptor
@ -51,7 +50,8 @@ namespace Artemis.Core.LayerBrushes
{
if (reference == null)
return false;
return LayerBrushProvider.PluginInfo.Guid == reference.BrushPluginGuid && LayerBrushType.Name == reference.BrushType;
return Provider.Id == reference.LayerBrushProviderId && LayerBrushType.Name == reference.BrushType;
}
/// <summary>
@ -62,7 +62,7 @@ namespace Artemis.Core.LayerBrushes
if (layer.LayerBrush != null)
throw new ArtemisCoreException("Layer already has an instantiated layer brush");
BaseLayerBrush brush = (BaseLayerBrush) LayerBrushProvider.PluginInfo.Kernel.Get(LayerBrushType);
BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Kernel!.Get(LayerBrushType);
brush.Layer = layer;
brush.Descriptor = this;
brush.Initialize();

View File

@ -7,7 +7,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary>
/// Allows you to create one or more <see cref="LayerBrush{T}" />s usable by profile layers.
/// </summary>
public abstract class LayerBrushProvider : PluginImplementation
public abstract class LayerBrushProvider : PluginFeature
{
private readonly List<LayerBrushDescriptor> _layerBrushDescriptors;
@ -39,7 +39,7 @@ namespace Artemis.Core.LayerBrushes
protected void RegisterLayerBrushDescriptor<T>(string displayName, string description, string icon) where T : BaseLayerBrush
{
if (!IsEnabled)
throw new ArtemisPluginException(PluginInfo, "Can only add a layer brush descriptor when the plugin is enabled");
throw new ArtemisPluginException(Plugin, "Can only add a layer brush descriptor when the plugin is enabled");
LayerBrushDescriptor descriptor = new LayerBrushDescriptor(displayName, description, icon, typeof(T), this);
_layerBrushDescriptors.Add(descriptor);

View File

@ -0,0 +1,6 @@
namespace Artemis.Core.LayerEffects
{
public interface IEffectConfigurationViewModel
{
}
}

View File

@ -0,0 +1,6 @@
namespace Artemis.Core.LayerEffects
{
public interface ILayerEffectConfigurationDialog
{
}
}

View File

@ -1,16 +1,15 @@
using System;
using Artemis.Core.Services;
using SkiaSharp;
using Stylet;
namespace Artemis.Core.LayerEffects
{
/// <summary>
/// For internal use only, please use <see cref="LayerEffect{T}" /> instead
/// </summary>
public abstract class BaseLayerEffect : PropertyChangedBase, IDisposable
public abstract class BaseLayerEffect : CorePropertyChanged, IDisposable
{
private LayerEffectConfigurationDialog _configurationDialog;
private ILayerEffectConfigurationDialog _configurationDialog;
private LayerEffectDescriptor _descriptor;
private bool _enabled;
private Guid _entityId;
@ -77,7 +76,7 @@ namespace Artemis.Core.LayerEffects
/// <summary>
/// Gets the <see cref="LayerEffectDescriptor" /> that registered this effect
/// </summary>
public LayerEffectDescriptor Descriptor
public LayerEffectDescriptor? Descriptor
{
get => _descriptor;
internal set => SetAndNotify(ref _descriptor, value);
@ -86,16 +85,16 @@ namespace Artemis.Core.LayerEffects
/// <summary>
/// Gets or sets a configuration dialog complementing the regular properties
/// </summary>
public LayerEffectConfigurationDialog ConfigurationDialog
public ILayerEffectConfigurationDialog ConfigurationDialog
{
get => _configurationDialog;
protected set => SetAndNotify(ref _configurationDialog, value);
}
/// <summary>
/// Gets the plugin info that defined this effect
/// Gets the ID of the <see cref="LayerEffectProvider"/> that provided this effect
/// </summary>
public PluginInfo PluginInfo => Descriptor.LayerEffectProvider?.PluginInfo;
public string ProviderId => Descriptor?.Provider?.Id;
/// <summary>
/// Gets a reference to the layer property group without knowing it's type

View File

@ -37,7 +37,7 @@ namespace Artemis.Core.LayerEffects
{
Properties = Activator.CreateInstance<T>();
Properties.LayerEffect = this;
Properties.Initialize(ProfileElement, PropertyRootPath, PluginInfo);
Properties.Initialize(ProfileElement, PropertyRootPath, Descriptor.Provider);
PropertiesInitialized = true;
EnableLayerEffect();

View File

@ -1,7 +1,6 @@
using System;
using System.Linq;
using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
using Ninject;
@ -12,13 +11,13 @@ namespace Artemis.Core.LayerEffects
/// </summary>
public class LayerEffectDescriptor
{
internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider layerEffectProvider)
internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider provider)
{
DisplayName = displayName;
Description = description;
Icon = icon;
LayerEffectType = layerEffectType;
LayerEffectProvider = layerEffectProvider;
Provider = provider;
}
/// <summary>
@ -45,12 +44,12 @@ namespace Artemis.Core.LayerEffects
/// <summary>
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
/// </summary>
public LayerEffectProvider LayerEffectProvider { get; }
public LayerEffectProvider? Provider { get; }
/// <summary>
/// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
/// </summary>
public Guid? PlaceholderFor { get; internal set; }
public string? PlaceholderFor { get; internal set; }
/// <summary>
/// Creates an instance of the described effect and applies it to the render element
@ -67,7 +66,10 @@ namespace Artemis.Core.LayerEffects
return;
}
BaseLayerEffect effect = (BaseLayerEffect) LayerEffectProvider.PluginInfo.Kernel.Get(LayerEffectType);
if (Provider == null)
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its provider");
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
effect.ProfileElement = renderElement;
effect.EntityId = entity.Id;
effect.Order = entity.Order;
@ -83,7 +85,7 @@ namespace Artemis.Core.LayerEffects
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
{
PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor.Value) {ProfileElement = renderElement, Descriptor = this};
PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor) {ProfileElement = renderElement, Descriptor = this};
effect.Initialize();
renderElement.ActivateLayerEffect(effect);
}

View File

@ -7,7 +7,7 @@ namespace Artemis.Core.LayerEffects
/// <summary>
/// Allows you to register one or more <see cref="LayerEffect{T}" />s usable by profile layers.
/// </summary>
public abstract class LayerEffectProvider : PluginImplementation
public abstract class LayerEffectProvider : PluginFeature
{
private readonly List<LayerEffectDescriptor> _layerEffectDescriptors;
@ -39,7 +39,7 @@ namespace Artemis.Core.LayerEffects
protected void RegisterLayerEffectDescriptor<T>(string displayName, string description, string icon) where T : BaseLayerEffect
{
if (!IsEnabled)
throw new ArtemisPluginException(PluginInfo, "Can only add a layer effect descriptor when the plugin is enabled");
throw new ArtemisPluginFeatureException(this, "Can only add a layer effect descriptor when the plugin is enabled");
LayerEffectDescriptor descriptor = new LayerEffectDescriptor(displayName, description, icon, typeof(T), this);
_layerEffectDescriptors.Add(descriptor);

View File

@ -1,5 +1,4 @@
using System;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile;
using SkiaSharp;
namespace Artemis.Core.LayerEffects.Placeholder
@ -9,7 +8,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
/// </summary>
internal class PlaceholderLayerEffect : LayerEffect<PlaceholderProperties>
{
internal PlaceholderLayerEffect(LayerEffectEntity originalEntity, Guid placeholderFor)
internal PlaceholderLayerEffect(LayerEffectEntity originalEntity, string placeholderFor)
{
OriginalEntity = originalEntity;
PlaceholderFor = placeholderFor;
@ -21,8 +20,9 @@ namespace Artemis.Core.LayerEffects.Placeholder
HasBeenRenamed = OriginalEntity.HasBeenRenamed;
}
public string PlaceholderFor { get; }
internal LayerEffectEntity OriginalEntity { get; }
public Guid PlaceholderFor { get; }
/// <inheritdoc />
public override void EnableLayerEffect()
@ -56,7 +56,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
}
/// <summary>
/// This is in place so that the UI has something to show
/// This is in place so that the UI has something to show
/// </summary>
internal class PlaceholderProperties : LayerPropertyGroup
{

View File

@ -1,14 +1,12 @@
using System;
namespace Artemis.Core.LayerEffects.Placeholder
namespace Artemis.Core.LayerEffects.Placeholder
{
internal static class PlaceholderLayerEffectDescriptor
{
public static LayerEffectDescriptor Create(Guid missingPluginGuid)
public static LayerEffectDescriptor Create(string missingProviderId)
{
LayerEffectDescriptor descriptor = new LayerEffectDescriptor("Missing effect", "This effect could not be loaded", "FileQuestion", null, Constants.EffectPlaceholderPlugin)
{
PlaceholderFor = missingPluginGuid
PlaceholderFor = missingProviderId
};
return descriptor;

View File

@ -0,0 +1,10 @@
namespace Artemis.Core.Modules
{
/// <summary>
/// The base class for any view model that belongs to a module
/// </summary>
public interface IModuleViewModel
{
}
}

View File

@ -42,13 +42,13 @@ namespace Artemis.Core.Modules
/// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription()
{
return new DataModelPropertyAttribute {Name = PluginInfo.Name, Description = PluginInfo.Description};
return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
}
internal override void InternalEnable()
{
DataModel = Activator.CreateInstance<T>();
DataModel.Implementation = PluginInfo;
DataModel.Feature = this;
DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable();
}
@ -64,7 +64,7 @@ namespace Artemis.Core.Modules
/// <summary>
/// Allows you to add support for new games/applications
/// </summary>
public abstract class Module : DataModelPluginImplementation
public abstract class Module : DataModelPluginFeature
{
/// <summary>
/// The modules display name that's shown in the menu
@ -236,7 +236,7 @@ namespace Artemis.Core.Modules
if (Entity == null)
Entity = new ModuleSettingsEntity();
Entity.PluginGuid = PluginInfo.Guid;
Entity.ModuleId = Id;
Entity.PriorityCategory = (int) PriorityCategory;
Entity.Priority = Priority;
}

View File

@ -3,7 +3,7 @@
namespace Artemis.Core.Modules
{
/// <inheritdoc />
public class ModuleTab<T> : ModuleTab where T : ModuleViewModel
public class ModuleTab<T> : ModuleTab where T : IModuleViewModel
{
/// <summary>
/// Initializes a new instance of the <see cref="ModuleTab{T}" /> class

View File

@ -46,7 +46,7 @@ namespace Artemis.Core.Modules
/// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription()
{
return new DataModelPropertyAttribute {Name = PluginInfo.Name, Description = PluginInfo.Description};
return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
}
/// <summary>
@ -74,7 +74,7 @@ namespace Artemis.Core.Modules
internal override void InternalEnable()
{
DataModel = Activator.CreateInstance<T>();
DataModel.Implementation = PluginInfo;
DataModel.Feature = this;
DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable();
}
@ -184,7 +184,7 @@ namespace Artemis.Core.Modules
internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface)
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}.");
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
@ -206,7 +206,7 @@ namespace Artemis.Core.Modules
internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface)
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}.");
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");

View File

@ -7,16 +7,15 @@ using System.Reflection;
using Artemis.Storage.Entities.Plugins;
using McMaster.NETCore.Plugins;
using Ninject;
using Stylet;
namespace Artemis.Core
{
/// <summary>
/// Represents a plugin
/// </summary>
public class Plugin : PropertyChangedBase, IDisposable
public class Plugin : CorePropertyChanged, IDisposable
{
private readonly List<PluginImplementation> _implementations;
private readonly List<PluginFeature> _features;
private bool _isEnabled;
@ -25,7 +24,7 @@ namespace Artemis.Core
Info = info;
Directory = directory;
_implementations = new List<PluginImplementation>();
_features = new List<PluginFeature>();
}
/// <summary>
@ -46,8 +45,8 @@ namespace Artemis.Core
/// <summary>
/// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins
/// </summary>
public PluginConfigurationDialog? ConfigurationDialog { get; protected set; }
public IPluginConfigurationDialog? ConfigurationDialog { get; set; }
/// <summary>
/// Indicates whether the user enabled the plugin or not
/// </summary>
@ -58,19 +57,24 @@ namespace Artemis.Core
}
/// <summary>
/// Gets a read-only collection of all implementations this plugin provides
/// Gets a read-only collection of all features this plugin provides
/// </summary>
public ReadOnlyCollection<PluginImplementation> Implementations => _implementations.AsReadOnly();
public ReadOnlyCollection<PluginFeature> Features => _features.AsReadOnly();
/// <summary>
/// The assembly the plugin code lives in
/// </summary>
internal Assembly? Assembly { get; set; }
public Assembly? Assembly { get; internal set; }
/// <summary>
/// Gets the plugin bootstrapper
/// </summary>
public IPluginBootstrapper? Bootstrapper { get; internal set; }
/// <summary>
/// The Ninject kernel of the plugin
/// </summary>
internal IKernel? Kernel { get; set; }
public IKernel? Kernel { get; internal set; }
/// <summary>
/// The PluginLoader backing this plugin
@ -80,7 +84,7 @@ namespace Artemis.Core
/// <summary>
/// The entity representing the plugin
/// </summary>
internal PluginEntity? Entity { get; set; }
internal PluginEntity Entity { get; set; }
/// <summary>
/// Resolves the relative path provided in the <paramref name="path" /> parameter to an absolute path
@ -92,25 +96,15 @@ namespace Artemis.Core
return path == null ? null : Path.Combine(Directory.FullName, path);
}
internal void ApplyToEntity()
/// <summary>
/// Looks up the instance of the feature of type <typeparamref name="T" />
/// <para>Note: This method only returns instances of enabled features</para>
/// </summary>
/// <typeparam name="T">The type of feature to find</typeparam>
/// <returns>If found, the instance of the feature</returns>
public T? GetFeature<T>() where T : PluginFeature
{
Entity.Id = Guid;
Entity.IsEnabled = IsEnabled;
}
internal void AddImplementation(PluginImplementation implementation)
{
implementation.Plugin = this;
_implementations.Add(implementation);
}
public void SetEnabled(bool enable)
{
if (IsEnabled == enable)
return;
if (!enable && Implementations.Any(e => e.IsEnabled))
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled implementations");
return _features.FirstOrDefault(i => i is T) as T;
}
/// <inheritdoc />
@ -119,13 +113,118 @@ namespace Artemis.Core
return Info.ToString();
}
internal void ApplyToEntity()
{
Entity.Id = Guid;
Entity.IsEnabled = IsEnabled;
}
internal void AddFeature(PluginFeature feature)
{
feature.Plugin = this;
_features.Add(feature);
OnFeatureAdded(new PluginFeatureEventArgs(feature));
}
internal void RemoveFeature(PluginFeature feature)
{
_features.Remove(feature);
feature.InternalDisable();
feature.Dispose();
OnFeatureRemoved(new PluginFeatureEventArgs(feature));
}
internal void SetEnabled(bool enable)
{
if (IsEnabled == enable)
return;
if (!enable && Features.Any(e => e.IsEnabled))
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features");
IsEnabled = enable;
if (enable)
{
Bootstrapper?.Enable(this);
OnEnabled();
}
else
{
Bootstrapper?.Disable(this);
OnDisabled();
}
}
public void Dispose()
{
foreach (PluginImplementation pluginImplementation in Implementations)
pluginImplementation.Dispose();
foreach (PluginFeature feature in Features)
feature.Dispose();
Kernel?.Dispose();
PluginLoader?.Dispose();
_features.Clear();
SetEnabled(false);
}
#region Events
/// <summary>
/// Occurs when the plugin is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the plugin is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Occurs when an feature is loaded and added to the plugin
/// </summary>
public event EventHandler<PluginFeatureEventArgs>? FeatureAdded;
/// <summary>
/// Occurs when an feature is disabled and removed from the plugin
/// </summary>
public event EventHandler<PluginFeatureEventArgs>? FeatureRemoved;
/// <summary>
/// Invokes the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
Enabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the Disabled event
/// </summary>
protected virtual void OnDisabled()
{
Disabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the FeatureAdded event
/// </summary>
protected virtual void OnFeatureAdded(PluginFeatureEventArgs e)
{
FeatureAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the FeatureRemoved event
/// </summary>
protected virtual void OnFeatureRemoved(PluginFeatureEventArgs e)
{
FeatureRemoved?.Invoke(this, e);
}
#endregion
}
}

View File

@ -1,24 +1,22 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Storage.Entities.Plugins;
using Stylet;
namespace Artemis.Core
{
/// <summary>
/// Represents an implementation of a certain type provided by a plugin
/// Represents an feature of a certain type provided by a plugin
/// </summary>
public abstract class PluginImplementation : PropertyChangedBase, IDisposable
public abstract class PluginFeature : CorePropertyChanged, IDisposable
{
private Exception? _loadException;
private bool _isEnabled;
private Exception? _loadException;
/// <summary>
/// Gets the plugin that provides this implementation
/// Gets the plugin that provides this feature
/// </summary>
public Plugin? Plugin { get; internal set; }
public Plugin Plugin { get; internal set; }
/// <summary>
/// Gets whether the plugin is enabled
@ -38,15 +36,20 @@ namespace Artemis.Core
internal set => SetAndNotify(ref _loadException, value);
}
internal PluginImplementationEntity? Entity { get; set; }
/// <summary>
/// Gets the identifier of this plugin feature
/// </summary>
public string Id => $"{GetType().FullName}-{Plugin.Guid.ToString().Substring(0, 8)}"; // Not as unique as a GUID but good enough and stays readable
internal PluginFeatureEntity Entity { get; set; }
/// <summary>
/// Called when the implementation is activated
/// Called when the feature is activated
/// </summary>
public abstract void Enable();
/// <summary>
/// Called when the implementation is deactivated or when Artemis shuts down
/// Called when the feature is deactivated or when Artemis shuts down
/// </summary>
public abstract void Disable();
@ -56,12 +59,12 @@ namespace Artemis.Core
return;
if (Plugin == null)
throw new ArtemisCoreException("Cannot enable a plugin implementation that is not associated with a plugin");
throw new ArtemisCoreException("Cannot enable a plugin feature that is not associated with a plugin");
lock (Plugin)
{
if (!Plugin.IsEnabled)
throw new ArtemisCoreException("Cannot enable a plugin implementation of a disabled plugin");
throw new ArtemisCoreException("Cannot enable a plugin feature of a disabled plugin");
if (!enable)
{
@ -72,7 +75,7 @@ namespace Artemis.Core
OnDisabled();
return;
}
try
{
if (isAutoEnable && GetLockFileCreated())
@ -89,19 +92,10 @@ namespace Artemis.Core
// Allow up to 15 seconds for plugins to activate.
// This means plugins that need more time should do their long running tasks in a background thread, which is intentional
ManualResetEvent wait = new ManualResetEvent(false);
Thread work = new Thread(() =>
{
InternalEnable();
wait.Set();
});
work.Start();
wait.WaitOne(TimeSpan.FromSeconds(15));
if (work.IsAlive)
{
work.Abort();
// This would've been a perfect match for Thread.Abort but that didn't make it into .NET Core
Task enableTask = Task.Run(InternalEnable);
if (!enableTask.Wait(TimeSpan.FromSeconds(15)))
throw new ArtemisPluginException(Plugin, "Plugin load timeout");
}
LoadException = null;
OnEnabled();
@ -144,7 +138,7 @@ namespace Artemis.Core
internal void CreateLockFile()
{
if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin");
throw new ArtemisCoreException("Cannot lock a plugin feature that is not associated with a plugin");
File.Create(Plugin.ResolveRelativePath($"{GetType().FullName}.lock")).Close();
}
@ -152,7 +146,7 @@ namespace Artemis.Core
internal void DeleteLockFile()
{
if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin");
throw new ArtemisCoreException("Cannot lock a plugin feature that is not associated with a plugin");
if (GetLockFileCreated())
File.Delete(Plugin.ResolveRelativePath($"{GetType().FullName}.lock"));
@ -161,7 +155,7 @@ namespace Artemis.Core
internal bool GetLockFileCreated()
{
if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin");
throw new ArtemisCoreException("Cannot lock a plugin feature that is not associated with a plugin");
return File.Exists(Plugin.ResolveRelativePath($"{GetType().FullName}.lock"));
}
@ -171,17 +165,17 @@ namespace Artemis.Core
#region Events
/// <summary>
/// Occurs when the implementation is enabled
/// Occurs when the feature is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the implementation is disabled
/// Occurs when the feature is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Triggers the PluginEnabled event
/// Triggers the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
@ -189,7 +183,7 @@ namespace Artemis.Core
}
/// <summary>
/// Triggers the PluginDisabled event
/// Triggers the Disabled event
/// </summary>
protected virtual void OnDisabled()
{

View File

@ -1,6 +1,5 @@
using System;
using Newtonsoft.Json;
using Stylet;
namespace Artemis.Core
{
@ -8,7 +7,7 @@ namespace Artemis.Core
/// Represents basic info about a plugin and contains a reference to the instance of said plugin
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
public class PluginInfo : PropertyChangedBase
public class PluginInfo : CorePropertyChanged
{
private string _description;
private Guid _guid;

View File

@ -2,7 +2,6 @@
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
using Stylet;
namespace Artemis.Core
{
@ -10,17 +9,18 @@ namespace Artemis.Core
/// Represents a setting tied to a plugin of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The value type of the setting</typeparam>
public class PluginSetting<T> : PropertyChangedBase
public class PluginSetting<T> : CorePropertyChanged
{
// ReSharper disable once NotAccessedField.Local
private readonly PluginInfo _pluginInfo;
// TODO: Why? Should have included that...
// ReSharper disable once NotAccessedField.Local
private readonly Plugin _plugin;
private readonly IPluginRepository _pluginRepository;
private readonly PluginSettingEntity _pluginSettingEntity;
private T _value;
internal PluginSetting(PluginInfo pluginInfo, IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity)
internal PluginSetting(Plugin plugin, IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity)
{
_pluginInfo = pluginInfo;
_plugin = plugin;
_pluginRepository = pluginRepository;
_pluginSettingEntity = pluginSettingEntity;
@ -52,7 +52,7 @@ namespace Artemis.Core
_value = value;
OnSettingChanged();
NotifyOfPropertyChange(nameof(Value));
OnPropertyChanged(nameof(Value));
if (AutoSave)
Save();

View File

@ -14,17 +14,17 @@ namespace Artemis.Core
private readonly IPluginRepository _pluginRepository;
private readonly Dictionary<string, object> _settingEntities;
internal PluginSettings(PluginInfo pluginInfo, IPluginRepository pluginRepository)
internal PluginSettings(Plugin plugin, IPluginRepository pluginRepository)
{
PluginInfo = pluginInfo;
Plugin = plugin;
_pluginRepository = pluginRepository;
_settingEntities = new Dictionary<string, object>();
}
/// <summary>
/// Gets the info of the plugin this setting belongs to
/// Gets the plugin these settings belong to
/// </summary>
public PluginInfo PluginInfo { get; }
public Plugin Plugin { get; }
/// <summary>
/// Gets the setting with the provided name. If the setting does not exist yet, it is created.
@ -41,15 +41,15 @@ namespace Artemis.Core
if (_settingEntities.ContainsKey(name))
return (PluginSetting<T>) _settingEntities[name];
// Try to find in database
PluginSettingEntity settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, PluginInfo.Guid);
PluginSettingEntity settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, Plugin.Guid);
// If not found, create a new one
if (settingEntity == null)
{
settingEntity = new PluginSettingEntity {Name = name, PluginGuid = PluginInfo.Guid, Value = JsonConvert.SerializeObject(defaultValue)};
settingEntity = new PluginSettingEntity {Name = name, PluginGuid = Plugin.Guid, Value = JsonConvert.SerializeObject(defaultValue)};
_pluginRepository.AddSetting(settingEntity);
}
PluginSetting<T> pluginSetting = new PluginSetting<T>(PluginInfo, _pluginRepository, settingEntity);
PluginSetting<T> pluginSetting = new PluginSetting<T>(Plugin, _pluginRepository, settingEntity);
// This overrides null with the default value, I'm not sure if that's desirable because you
// might expect something to go null and you might not

View File

@ -8,40 +8,41 @@ namespace Artemis.Core
/// <summary>
/// Represents a registration for a timed plugin update
/// </summary>
public class TimedUpdateRegistration
public class TimedUpdateRegistration : IDisposable
{
private DateTime _lastEvent;
private Timer _timer;
private bool _disposed;
internal TimedUpdateRegistration(PluginInfo pluginInfo, TimeSpan interval, Action<double> action)
internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action<double> action)
{
PluginInfo = pluginInfo;
Feature = feature;
Interval = interval;
Action = action;
PluginInfo.Plugin.Enabled += InstanceOnEnabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
if (PluginInfo.Plugin.IsEnabled)
Feature.Enabled += FeatureOnEnabled;
Feature.Disabled += FeatureOnDisabled;
if (Feature.IsEnabled)
Start();
}
internal TimedUpdateRegistration(PluginInfo pluginInfo, TimeSpan interval, Func<double, Task> asyncAction)
internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Func<double, Task> asyncAction)
{
PluginInfo = pluginInfo;
Feature = feature;
Interval = interval;
AsyncAction = asyncAction;
PluginInfo.Plugin.Enabled += InstanceOnEnabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
if (PluginInfo.Plugin.IsEnabled)
Feature.Enabled += FeatureOnEnabled;
Feature.Disabled += FeatureOnDisabled;
if (Feature.IsEnabled)
Start();
}
/// <summary>
/// Gets the plugin info of the plugin this registration is associated with
/// Gets the plugin feature this registration is associated with
/// </summary>
public PluginInfo PluginInfo { get; }
public PluginFeature Feature { get; }
/// <summary>
/// Gets the interval at which the update should occur
@ -64,10 +65,13 @@ namespace Artemis.Core
/// </summary>
public void Start()
{
if (_disposed)
throw new ObjectDisposedException("TimedUpdateRegistration");
lock (this)
{
if (!PluginInfo.Plugin.IsEnabled)
throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin");
if (!Feature.IsEnabled)
throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin feature");
if (_timer != null)
return;
@ -85,6 +89,9 @@ namespace Artemis.Core
/// </summary>
public void Stop()
{
if (_disposed)
throw new ObjectDisposedException("TimedUpdateRegistration");
lock (this)
{
if (_timer == null)
@ -99,7 +106,7 @@ namespace Artemis.Core
private void TimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (!PluginInfo.Plugin.IsEnabled)
if (!Feature.IsEnabled)
return;
lock (this)
@ -108,7 +115,7 @@ namespace Artemis.Core
_lastEvent = DateTime.Now;
// Modules don't always want to update, honor that
if (PluginInfo.Plugin is Module module && !module.IsUpdateAllowed)
if (Feature is Module module && !module.IsUpdateAllowed)
return;
if (Action != null)
@ -121,14 +128,25 @@ namespace Artemis.Core
}
}
private void InstanceOnEnabled(object sender, EventArgs e)
private void FeatureOnEnabled(object sender, EventArgs e)
{
Start();
}
private void InstanceOnDisabled(object sender, EventArgs e)
private void FeatureOnDisabled(object sender, EventArgs e)
{
Stop();
}
/// <inheritdoc />
public void Dispose()
{
Stop();
Feature.Enabled -= FeatureOnEnabled;
Feature.Disabled -= FeatureOnDisabled;
_disposed = true;
}
}
}

View File

@ -40,7 +40,7 @@ namespace Artemis.Core.Services
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
{
Kernel = kernel;
Constants.CorePluginInfo.Kernel = kernel;
Constants.CorePlugin.Kernel = kernel;
_logger = logger;
_pluginManagementService = pluginManagementService;
@ -133,8 +133,8 @@ namespace Artemis.Core.Services
private void UpdatePluginCache()
{
_modules = _pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled).ToList();
_dataModelExpansions = _pluginManagementService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled).ToList();
_modules = _pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled).ToList();
_dataModelExpansions = _pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled).ToList();
}
private void ConfigureJsonConvert()

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Artemis.Core.DeviceProviders;
using RGB.NET.Core;
namespace Artemis.Core.Services
@ -33,69 +34,77 @@ namespace Artemis.Core.Services
void UnloadPlugins();
/// <summary>
/// Loads the plugin defined in the provided <see cref="PluginInfo" />
/// Loads the plugin located in the provided <paramref name="directory" />
/// </summary>
/// <param name="pluginInfo">The plugin info defining the plugin to load</param>
void LoadPlugin(DirectoryInfo pluginInfo);
/// <param name="directory">The directory where the plugin is located</param>
Plugin LoadPlugin(DirectoryInfo directory);
/// <summary>
/// Unloads the plugin defined in the provided <see cref="PluginInfo" />
/// Enables the provided <paramref name="plugin" />
/// </summary>
/// <param name="pluginInfo">The plugin info defining the plugin to unload</param>
void UnloadPlugin(PluginInfo pluginInfo);
/// <param name="plugin">The plugin to enable</param>
void EnablePlugin(Plugin plugin, bool ignorePluginLock = false);
/// <summary>
/// Enables the provided plugin
/// Unloads the provided <paramref name="plugin" />
/// </summary>
/// <param name="pluginImplementation"></param>
/// <param name="plugin">The plugin to unload</param>
void UnloadPlugin(Plugin plugin);
/// <summary>
/// Disables the provided <paramref name="plugin" />
/// </summary>
/// <param name="plugin">The plugin to disable</param>
void DisablePlugin(Plugin plugin);
/// <summary>
/// Enables the provided plugin feature
/// </summary>
/// <param name="pluginFeature"></param>
/// <param name="isAutoEnable">If true, fails if there is a lock file present</param>
void EnablePluginImplementation(PluginImplementation pluginImplementation, bool isAutoEnable = false);
void EnablePluginFeature(PluginFeature pluginFeature, bool isAutoEnable = false);
/// <summary>
/// Disables the provided plugin
/// Disables the provided plugin feature
/// </summary>
/// <param name="pluginImplementation"></param>
void DisablePluginImplementation(PluginImplementation pluginImplementation);
/// <summary>
/// Finds the plugin info related to the plugin
/// </summary>
/// <param name="pluginImplementation">The plugin you want to find the plugin info for</param>
/// <returns>The plugins PluginInfo</returns>
PluginInfo GetPluginInfo(PluginImplementation pluginImplementation);
/// <param name="pluginFeature"></param>
void DisablePluginFeature(PluginFeature pluginFeature);
/// <summary>
/// Gets the plugin info of all loaded plugins
/// </summary>
/// <returns>A list containing all the plugin info</returns>
List<PluginInfo> GetAllPluginInfo();
List<Plugin> GetAllPlugins();
/// <summary>
/// Finds all enabled <see cref="PluginImplementation" /> instances of <typeparamref name="T" />
/// Finds all enabled <see cref="PluginFeature" /> instances of <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">Either <see cref="PluginImplementation" /> or a plugin type implementing <see cref="PluginImplementation" /></typeparam>
/// <returns>Returns a list of plugin instances of <typeparamref name="T" /></returns>
List<T> GetPluginsOfType<T>() where T : PluginImplementation;
/// <typeparam name="T">
/// Either <see cref="PluginFeature" /> or a plugin type implementing
/// <see cref="PluginFeature" />
/// </typeparam>
/// <returns>Returns a list of feature instances of <typeparamref name="T" /></returns>
List<T> GetFeaturesOfType<T>() where T : PluginFeature;
/// <summary>
/// Gets the plugin that provided the specified assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
PluginImplementation GetPluginByAssembly(Assembly assembly);
Plugin GetPluginByAssembly(Assembly assembly);
/// <summary>
/// Returns the plugin info of the current call stack
/// </summary>
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
Plugin? GetCallingPlugin();
/// <summary>
/// Gets the plugin that defined the specified device
/// </summary>
/// <param name="device"></param>
/// <returns></returns>
PluginImplementation GetPluginByDevice(IRGBDevice device);
/// <summary>
/// Returns the plugin info of the current call stack
/// </summary>
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
PluginImplementation GetCallingPlugin();
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
#region Events
@ -134,6 +143,26 @@ namespace Artemis.Core.Services
/// </summary>
event EventHandler<PluginEventArgs> PluginDisabled;
/// <summary>
/// Occurs when a plugin feature is being enabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabling;
/// <summary>
/// Occurs when a plugin feature has been enabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabled;
/// <summary>
/// Occurs when a plugin feature could not be enabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnableFailed;
/// <summary>
/// Occurs when a plugin feature has been disabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
#endregion
}
}

View File

@ -26,16 +26,129 @@ namespace Artemis.Core.Services
_moduleRepository = moduleRepository;
_pluginManagementService = pluginManagementService;
_profileService = profileService;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementEnabled;
_pluginManagementService.PluginFeatureEnabled += OnPluginFeatureEnabled;
Timer activationUpdateTimer = new Timer(2000);
activationUpdateTimer.Start();
activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
foreach (Module module in _pluginManagementService.GetPluginsOfType<Module>())
foreach (Module module in _pluginManagementService.GetFeaturesOfType<Module>())
InitialiseOrApplyPriority(module);
}
private async void ActivationUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
await UpdateModuleActivation();
}
private async Task ActivateModule(Module module)
{
try
{
module.Activate(false);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
await _profileService.ActivateLastProfileAnimated(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to activate module and last profile.", e), "Failed to activate module and last profile"
);
throw;
}
}
private async Task DeactivateModule(Module module)
{
try
{
// If this is a profile module, animate profile disable
// module.Deactivate would do the same but without animation
if (module.IsActivated && module is ProfileModule profileModule)
await profileModule.ChangeActiveProfileAnimated(null, null);
module.Deactivate(false);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile"
);
throw;
}
}
private void OverrideActivate(Module module)
{
try
{
if (module.IsActivated)
return;
// If activating while it should be deactivated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
module.Activate(!shouldBeActivated);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
_profileService.ActivateLastProfile(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to activate module and last profile.", e), "Failed to activate module and last profile"
);
throw;
}
}
private void OverrideDeactivate(Module module, bool clearingOverride)
{
try
{
if (!module.IsActivated)
return;
// If deactivating while it should be activated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
// No need to deactivate if it is not in an overridden state
if (shouldBeActivated && !module.IsActivatedOverride && !clearingOverride)
return;
module.Deactivate(true);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile"
);
throw;
}
}
private void OnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature is Module module)
InitialiseOrApplyPriority(module);
}
private void InitialiseOrApplyPriority(Module module)
{
ModulePriorityCategory category = module.DefaultPriorityCategory;
int priority = 1;
module.Entity = _moduleRepository.GetByModuleId(module.Id);
if (module.Entity != null)
{
category = (ModulePriorityCategory) module.Entity.PriorityCategory;
priority = module.Entity.Priority;
}
UpdateModulePriority(module, category, priority);
}
public Module ActiveModuleOverride { get; private set; }
public async Task SetActiveModuleOverride(Module overrideModule)
@ -46,17 +159,19 @@ namespace Artemis.Core.Services
if (ActiveModuleOverride == overrideModule)
return;
if (overrideModule != null)
{
OverrideActivate(overrideModule);
_logger.Information($"Setting active module override to {overrideModule.DisplayName}");
}
else
{
_logger.Information("Clearing active module override");
}
// Always deactivate all other modules whenever override is called
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList();
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
foreach (Module module in modules.Where(m => m != overrideModule))
OverrideDeactivate(module, overrideModule != null);
@ -83,13 +198,8 @@ namespace Artemis.Core.Services
// the principle is different for this service but not for the module
bool shouldBeActivated = ActiveModuleOverride.EvaluateActivationRequirements();
if (shouldBeActivated && ActiveModuleOverride.IsActivatedOverride)
{
ActiveModuleOverride.Reactivate(true, false);
}
else if (!shouldBeActivated && !ActiveModuleOverride.IsActivatedOverride)
{
ActiveModuleOverride.Reactivate(false, true);
}
else if (!shouldBeActivated && !ActiveModuleOverride.IsActivatedOverride) ActiveModuleOverride.Reactivate(false, true);
return;
}
@ -97,10 +207,9 @@ namespace Artemis.Core.Services
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList();
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
List<Task> tasks = new List<Task>();
foreach (Module module in modules)
{
lock (module)
{
bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled;
@ -109,7 +218,6 @@ namespace Artemis.Core.Services
else if (!shouldBeActivated && module.IsActivated)
tasks.Add(DeactivateModule(module));
}
}
await Task.WhenAll(tasks);
@ -128,7 +236,12 @@ namespace Artemis.Core.Services
if (module.PriorityCategory == category && module.Priority == priority)
return;
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().Where(m => m.PriorityCategory == category).OrderBy(m => m.Priority).ToList();
List<Module> modules = _pluginManagementService
.GetFeaturesOfType<Module>()
.Where(m => m.PriorityCategory == category)
.OrderBy(m => m.Priority)
.ToList();
if (modules.Contains(module))
modules.Remove(module);
@ -149,110 +262,5 @@ namespace Artemis.Core.Services
}
}
}
private async void ActivationUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
await UpdateModuleActivation();
}
private async Task ActivateModule(Module module)
{
try
{
module.Activate(false);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
await _profileService.ActivateLastProfileAnimated(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to activate module and last profile.", e), "Failed to activate module and last profile");
throw;
}
}
private async Task DeactivateModule(Module module)
{
try
{
// If this is a profile module, animate profile disable
// module.Deactivate would do the same but without animation
if (module.IsActivated && module is ProfileModule profileModule)
await profileModule.ChangeActiveProfileAnimated(null, null);
module.Deactivate(false);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile");
throw;
}
}
private void OverrideActivate(Module module)
{
try
{
if (module.IsActivated)
return;
// If activating while it should be deactivated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
module.Activate(!shouldBeActivated);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
_profileService.ActivateLastProfile(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to activate module and last profile.", e), "Failed to activate module and last profile");
throw;
}
}
private void OverrideDeactivate(Module module, bool clearingOverride)
{
try
{
if (!module.IsActivated)
return;
// If deactivating while it should be activated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
// No need to deactivate if it is not in an overridden state
if (shouldBeActivated && !module.IsActivatedOverride && !clearingOverride)
return;
module.Deactivate(true);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile");
throw;
}
}
private void PluginManagementServiceOnPluginManagementEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Plugin is Module module)
InitialiseOrApplyPriority(module);
}
private void InitialiseOrApplyPriority(Module module)
{
ModulePriorityCategory category = module.DefaultPriorityCategory;
int priority = 1;
module.Entity = _moduleRepository.GetByPluginGuid(module.PluginInfo.Guid);
if (module.Entity != null)
{
category = (ModulePriorityCategory) module.Entity.PriorityCategory;
priority = module.Entity.Priority;
}
UpdateModulePriority(module, category, priority);
}
}
}

View File

@ -41,6 +41,21 @@ namespace Artemis.Core.Services
Directory.CreateDirectory(Constants.DataFolder + "plugins");
}
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive)
{
DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins", Path.GetFileNameWithoutExtension(zipFileInfo.Name)));
bool createLockFile = File.Exists(Path.Combine(pluginDirectory.FullName, "artemis.lock"));
// Remove the old directory if it exists
if (Directory.Exists(pluginDirectory.FullName))
pluginDirectory.DeleteRecursively();
Directory.CreateDirectory(pluginDirectory.FullName);
zipArchive.ExtractToDirectory(pluginDirectory.FullName, true);
if (createLockFile)
File.Create(Path.Combine(pluginDirectory.FullName, "artemis.lock")).Close();
}
public bool LoadingPlugins { get; private set; }
#region Built in plugins
@ -72,7 +87,9 @@ namespace Artemis.Core.Services
// Find the matching plugin in the plugin folder
DirectoryInfo? match = pluginDirectory.EnumerateDirectories().FirstOrDefault(d => d.Name == Path.GetFileNameWithoutExtension(zipFile.Name));
if (match == null)
{
CopyBuiltInPlugin(zipFile, archive);
}
else
{
string metadataFile = Path.Combine(match.FullName, "plugin.json");
@ -105,6 +122,48 @@ namespace Artemis.Core.Services
#endregion
public List<Plugin> GetAllPlugins()
{
return new List<Plugin>(_plugins);
}
public List<T> GetFeaturesOfType<T>() where T : PluginFeature
{
return _plugins.Where(p => p.IsEnabled).SelectMany(p => p.Features.Where(i => i.IsEnabled && i is T)).Cast<T>().ToList();
}
public Plugin? GetPluginByAssembly(Assembly assembly)
{
return _plugins.FirstOrDefault(p => p.Assembly == assembly);
}
// TODO: move to a more appropriate service
public DeviceProvider GetDeviceProviderByDevice(IRGBDevice rgbDevice)
{
return GetFeaturesOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
}
public Plugin? GetCallingPlugin()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
foreach (StackFrame stackFrame in stackFrames)
{
Assembly assembly = stackFrame.GetMethod().DeclaringType.Assembly;
Plugin plugin = GetPluginByAssembly(assembly);
if (plugin != null)
return plugin;
}
return null;
}
public void Dispose()
{
UnloadPlugins();
}
#region Plugins
public void LoadPlugins(bool ignorePluginLock)
@ -125,7 +184,8 @@ namespace Artemis.Core.Services
{
try
{
LoadPlugin(subDirectory);
Plugin plugin = LoadPlugin(subDirectory);
EnablePlugin(plugin, ignorePluginLock);
}
catch (Exception e)
{
@ -133,22 +193,6 @@ namespace Artemis.Core.Services
}
}
// Activate plugins after they are all loaded
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
{
foreach (PluginImplementation pluginImplementation in plugin.Implementations.Where(i => i.Entity.IsEnabled))
{
try
{
EnablePluginImplementation(pluginImplementation, !ignorePluginLock);
}
catch (Exception)
{
// ignored, logged in EnablePlugin
}
}
}
LoadingPlugins = false;
}
}
@ -165,29 +209,32 @@ namespace Artemis.Core.Services
}
}
public void LoadPlugin(DirectoryInfo pluginDirectory)
public Plugin LoadPlugin(DirectoryInfo directory)
{
lock (_plugins)
{
_logger.Debug("Loading plugin from {directory}", pluginDirectory.FullName);
_logger.Debug("Loading plugin from {directory}", directory.FullName);
// Load the metadata
string metadataFile = Path.Combine(pluginDirectory.FullName, "plugin.json");
string metadataFile = Path.Combine(directory.FullName, "plugin.json");
if (!File.Exists(metadataFile))
_logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception");
// PluginInfo contains the ID which we need to move on
PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
if (pluginInfo.Guid == Constants.CorePluginInfo.Guid)
throw new ArtemisPluginException($"Plugin cannot use reserved GUID {pluginInfo.Guid}");
// Ensure the plugin is not already loaded
if (_plugins.Any(p => p.Guid == pluginInfo.Guid))
throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
Plugin plugin = new Plugin(pluginInfo, pluginDirectory);
Plugin plugin = new Plugin(pluginInfo, directory);
OnPluginLoading(new PluginEventArgs(plugin));
// Load the entity and fall back on creating a new one
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity { Id = plugin.Guid, IsEnabled = true };
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity {Id = plugin.Guid, IsEnabled = true};
// Locate the main assembly entry
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);
@ -210,55 +257,94 @@ namespace Artemis.Core.Services
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
}
// Get the Plugin implementation from the main assembly and if there is only one, instantiate it
List<Type> implementationTypes;
try
{
implementationTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginImplementation).IsAssignableFrom(t)).ToList();
}
catch (ReflectionTypeLoadException e)
{
throw new ArtemisPluginException(plugin, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
}
// Create the Ninject child kernel and load the module
plugin.Kernel = new ChildKernel(_kernel);
plugin.Kernel.Load(new PluginModule(pluginInfo));
if (!implementationTypes.Any())
_logger.Warning("Plugin {plugin} contains no implementations", plugin);
// Create instances of each implementation and add them to the plugin
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
foreach (Type implementationType in implementationTypes)
{
try
{
PluginImplementation instance = (PluginImplementation)plugin.Kernel.Get(implementationType);
plugin.AddImplementation(instance);
}
catch (Exception e)
{
throw new ArtemisPluginException(plugin, "Failed to load instantiate implementation", e);
}
}
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
if (bootstrappers.Count > 1)
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
if (bootstrappers.Any())
plugin.Bootstrapper = (IPluginBootstrapper?) Activator.CreateInstance(bootstrappers.First());
_plugins.Add(plugin);
OnPluginLoaded(new PluginEventArgs(plugin));
return plugin;
}
}
public void EnablePlugin(Plugin plugin, bool ignorePluginLock)
{
if (plugin.Assembly == null)
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
// Create the Ninject child kernel and load the module
plugin.Kernel = new ChildKernel(_kernel);
plugin.Kernel.Bind<Plugin>().ToConstant(plugin);
plugin.Kernel.Load(new PluginModule(plugin.Info));
plugin.SetEnabled(true);
// Get the Plugin feature from the main assembly and if there is only one, instantiate it
List<Type> featureTypes;
try
{
featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t)).ToList();
}
catch (ReflectionTypeLoadException e)
{
throw new ArtemisPluginException(plugin, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
}
if (!featureTypes.Any())
_logger.Warning("Plugin {plugin} contains no features", plugin);
// Create instances of each feature and add them to the plugin
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
foreach (Type featureType in featureTypes)
{
try
{
// Include Plugin as a parameter for the PluginSettingsProvider
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureType, parameters);
plugin.AddFeature(instance);
// Load the enabled state and if not found, default to true
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
new PluginFeatureEntity {IsEnabled = true, Type = featureType.FullName};
}
catch (Exception e)
{
throw new ArtemisPluginException(plugin, "Failed to load instantiate feature", e);
}
}
// Activate plugins after they are all loaded
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
{
try
{
EnablePluginFeature(pluginFeature, !ignorePluginLock);
}
catch (Exception)
{
// ignored, logged in EnablePluginFeature
}
}
OnPluginEnabled(new PluginEventArgs(plugin));
}
public void UnloadPlugin(Plugin plugin)
{
lock (_plugins)
{
try
{
plugin.SetEnabled(false);
DisablePlugin(plugin);
}
catch (Exception)
catch (Exception e)
{
// TODO: Log these
_logger.Warning(new ArtemisPluginException(plugin, "Exception during DisablePlugin call for UnloadPlugin", e), "Failed to unload plugin");
}
finally
{
@ -267,38 +353,59 @@ namespace Artemis.Core.Services
plugin.Dispose();
_plugins.Remove(plugin);
OnPluginUnloaded(new PluginEventArgs(plugin));
}
}
public void DisablePlugin(Plugin plugin)
{
if (!plugin.IsEnabled)
return;
while (plugin.Features.Any())
{
PluginFeature feature = plugin.Features[0];
plugin.RemoveFeature(feature);
OnPluginFeatureDisabled(new PluginFeatureEventArgs(feature));
}
plugin.SetEnabled(false);
plugin.Kernel.Dispose();
plugin.Kernel = null;
OnPluginDisabled(new PluginEventArgs(plugin));
}
#endregion
#region Features
#region Implementations
public void EnablePluginImplementation(PluginImplementation pluginImplementation, bool isAutoEnable = false)
public void EnablePluginFeature(PluginFeature pluginFeature, bool isAutoEnable = false)
{
_logger.Debug("Enabling plugin implementation {implementation} - {plugin}", pluginImplementation, pluginImplementation.Plugin);
_logger.Debug("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
lock (_plugins)
{
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
try
{
// A device provider may be queued for disable on next restart, this undoes that
if (pluginImplementation is DeviceProvider && pluginImplementation.IsEnabled && !pluginImplementation.PluginInfo.IsEnabled)
if (pluginFeature is DeviceProvider && pluginFeature.IsEnabled && !pluginFeature.Entity.IsEnabled)
{
pluginImplementation.PluginInfo.IsEnabled = true;
pluginImplementation.PluginInfo.ApplyToEntity();
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity);
pluginFeature.Entity.IsEnabled = true;
SavePlugin(pluginFeature.Plugin);
return;
}
pluginImplementation.SetEnabled(true, isAutoEnable);
pluginFeature.SetEnabled(true, isAutoEnable);
pluginFeature.Entity.IsEnabled = true;
}
catch (Exception e)
{
_logger.Warning(new ArtemisPluginException(pluginImplementation.PluginInfo, "Exception during SetEnabled(true)", e), "Failed to enable plugin");
_logger.Warning(
new ArtemisPluginException(pluginFeature.Plugin, $"Exception during SetEnabled(true) on {pluginFeature}", e),
"Failed to enable plugin"
);
throw;
}
finally
@ -306,108 +413,83 @@ namespace Artemis.Core.Services
// On an auto-enable, ensure PluginInfo.Enabled is true even if enable failed, that way a failure on auto-enable does
// not affect the user's settings
if (isAutoEnable)
pluginImplementation.PluginInfo.IsEnabled = true;
pluginFeature.Entity.IsEnabled = true;
pluginImplementation.PluginInfo.ApplyToEntity();
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity);
SavePlugin(pluginFeature.Plugin);
if (pluginImplementation.PluginInfo.IsEnabled)
_logger.Debug("Successfully enabled plugin {pluginInfo}", pluginImplementation.PluginInfo);
if (pluginFeature.IsEnabled)
{
_logger.Debug("Successfully enabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
OnPluginFeatureEnabled(new PluginFeatureEventArgs(pluginFeature));
}
else
{
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
}
}
}
OnPluginImplementationEnabled(new PluginImplementationEventArgs(pluginImplementation));
}
public void DisablePluginImplementation(PluginImplementation pluginImplementation)
public void DisablePluginFeature(PluginFeature pluginFeature)
{
lock (_plugins)
{
_logger.Debug("Disabling plugin {pluginInfo}", pluginImplementation.PluginInfo);
// Device providers cannot be disabled at runtime simply queue a disable for next restart
if (pluginImplementation is DeviceProvider)
try
{
// Don't call SetEnabled(false) but simply update enabled state and save it
pluginImplementation.PluginInfo.IsEnabled = false;
pluginImplementation.PluginInfo.ApplyToEntity();
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity);
return;
_logger.Debug("Disabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
// Device providers cannot be disabled at runtime simply queue a disable for next restart
if (pluginFeature is DeviceProvider)
{
// Don't call SetEnabled(false) but simply update enabled state and save it
pluginFeature.Entity.IsEnabled = false;
SavePlugin(pluginFeature.Plugin);
return;
}
pluginFeature.SetEnabled(false);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
pluginFeature.Entity.IsEnabled = false;
SavePlugin(pluginFeature.Plugin);
pluginImplementation.SetEnabled(false);
pluginImplementation.PluginInfo.ApplyToEntity();
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity);
_logger.Debug("Successfully disabled plugin {pluginInfo}", pluginImplementation.PluginInfo);
if (!pluginFeature.IsEnabled)
{
_logger.Debug("Successfully disabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
OnPluginFeatureDisabled(new PluginFeatureEventArgs(pluginFeature));
}
}
}
OnPluginDisabled(new PluginEventArgs(pluginImplementation.PluginInfo));
}
#endregion
#region Storage
public PluginInfo GetPluginInfo(PluginImplementation pluginImplementation)
private void SavePlugin(Plugin plugin)
{
return _plugins.FirstOrDefault(p => p.Plugin == pluginImplementation);
}
public List<PluginInfo> GetAllPluginInfo()
{
return new List<PluginInfo>(_plugins);
}
public List<T> GetPluginsOfType<T>() where T : PluginImplementation
{
return _plugins.Where(p => p.IsEnabled && p.Plugin is T).Select(p => (T) p.Plugin).ToList();
}
public PluginImplementation GetPluginByAssembly(Assembly assembly)
{
return _plugins.FirstOrDefault(p => p.Assembly == assembly)?.Plugin;
}
public PluginImplementation GetPluginByDevice(IRGBDevice rgbDevice)
{
return GetPluginsOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
}
public PluginImplementation GetCallingPlugin()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
foreach (StackFrame stackFrame in stackFrames)
foreach (PluginFeature pluginFeature in plugin.Features)
{
Assembly assembly = stackFrame.GetMethod().DeclaringType.Assembly;
PluginImplementation pluginImplementation = GetPluginByAssembly(assembly);
if (pluginImplementation != null)
return pluginImplementation;
if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
plugin.Entity.Features.Add(pluginFeature.Entity);
}
return null;
_pluginRepository.SavePlugin(plugin.Entity);
}
public void Dispose()
private PluginFeatureEntity GetOrCreateFeatureEntity(PluginFeature feature)
{
UnloadPlugins();
return feature.Plugin.Entity.Features.FirstOrDefault(i => i.Type == feature.GetType().FullName) ??
new PluginFeatureEntity {IsEnabled = true, Type = feature.GetType().FullName};
}
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive)
{
DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins", Path.GetFileNameWithoutExtension(zipFileInfo.Name)));
bool createLockFile = File.Exists(Path.Combine(pluginDirectory.FullName, "artemis.lock"));
// Remove the old directory if it exists
if (Directory.Exists(pluginDirectory.FullName))
pluginDirectory.DeleteRecursively();
Directory.CreateDirectory(pluginDirectory.FullName);
zipArchive.ExtractToDirectory(pluginDirectory.FullName, true);
if (createLockFile)
File.Create(Path.Combine(pluginDirectory.FullName, "artemis.lock")).Close();
}
#endregion
#region Events
@ -419,8 +501,10 @@ namespace Artemis.Core.Services
public event EventHandler<PluginEventArgs> PluginEnabled;
public event EventHandler<PluginEventArgs> PluginDisabled;
public event EventHandler<PluginImplementationEventArgs> PluginImplementationEnabled;
public event EventHandler<PluginImplementationEventArgs> PluginImplementationDisabled;
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabling;
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabled;
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnableFailed;
protected virtual void OnCopyingBuildInPlugins()
{
@ -457,14 +541,24 @@ namespace Artemis.Core.Services
PluginDisabled?.Invoke(this, e);
}
protected virtual void OnPluginImplementationDisabled(PluginImplementationEventArgs e)
protected virtual void OnPluginFeatureEnabling(PluginFeatureEventArgs e)
{
PluginImplementationDisabled?.Invoke(this, e);
PluginFeatureEnabling?.Invoke(this, e);
}
protected virtual void OnPluginImplementationEnabled(PluginImplementationEventArgs e)
protected virtual void OnPluginFeatureEnabled(PluginFeatureEventArgs e)
{
PluginImplementationEnabled?.Invoke(this, e);
PluginFeatureEnabled?.Invoke(this, e);
}
protected virtual void OnPluginFeatureDisabled(PluginFeatureEventArgs e)
{
PluginFeatureDisabled?.Invoke(this, e);
}
protected virtual void OnPluginFeatureEnableFailed(PluginFeatureEventArgs e)
{
PluginFeatureEnableFailed?.Invoke(this, e);
}
#endregion

View File

@ -12,14 +12,14 @@ namespace Artemis.Core.Services
RegisterBuiltInConditionOperators();
}
public ConditionOperatorRegistration RegisterConditionOperator(PluginInfo pluginInfo, BaseConditionOperator conditionOperator)
public ConditionOperatorRegistration RegisterConditionOperator(Plugin plugin, BaseConditionOperator conditionOperator)
{
if (pluginInfo == null)
throw new ArgumentNullException(nameof(pluginInfo));
if (plugin == null)
throw new ArgumentNullException(nameof(plugin));
if (conditionOperator == null)
throw new ArgumentNullException(nameof(conditionOperator));
conditionOperator.PluginInfo = pluginInfo;
conditionOperator.Plugin = plugin;
return ConditionOperatorStore.Add(conditionOperator);
}
@ -43,30 +43,30 @@ namespace Artemis.Core.Services
private void RegisterBuiltInConditionOperators()
{
// General usage for any type
RegisterConditionOperator(Constants.CorePluginInfo, new EqualsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new EqualsConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new NotEqualConditionOperator());
// Numeric operators
RegisterConditionOperator(Constants.CorePluginInfo, new NumberEqualsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new NumberNotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanOrEqualConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new NumberEqualsConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new NumberNotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new LessThanConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new GreaterThanConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new LessThanOrEqualConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new GreaterThanOrEqualConditionOperator());
// String operators
RegisterConditionOperator(Constants.CorePluginInfo, new StringEqualsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringContainsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotContainsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringStartsWithConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringEndsWithConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringMatchesRegexConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringEqualsConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringNotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringContainsConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringNotContainsConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringStartsWithConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringEndsWithConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringMatchesRegexConditionOperator());
// Null checks, at the bottom
// TODO: Implement a priority mechanism
RegisterConditionOperator(Constants.CorePluginInfo, new NullConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new NotNullConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new NullConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new NotNullConditionOperator());
}
}
}

View File

@ -12,14 +12,14 @@ namespace Artemis.Core.Services
RegisterBuiltInModifiers();
}
public DataBindingModifierTypeRegistration RegisterModifierType(PluginInfo pluginInfo, BaseDataBindingModifierType dataBindingModifierType)
public DataBindingModifierTypeRegistration RegisterModifierType(Plugin plugin, BaseDataBindingModifierType dataBindingModifierType)
{
if (pluginInfo == null)
throw new ArgumentNullException(nameof(pluginInfo));
if (plugin == null)
throw new ArgumentNullException(nameof(plugin));
if (dataBindingModifierType == null)
throw new ArgumentNullException(nameof(dataBindingModifierType));
dataBindingModifierType.PluginInfo = pluginInfo;
dataBindingModifierType.Plugin = plugin;
return DataBindingModifierTypeStore.Add(dataBindingModifierType);
}
@ -43,41 +43,41 @@ namespace Artemis.Core.Services
private void RegisterBuiltInModifiers()
{
// Numbers - General
RegisterModifierType(Constants.CorePluginInfo, new SumModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SubtractModifierType());
RegisterModifierType(Constants.CorePluginInfo, new MultiplicationModifierType());
RegisterModifierType(Constants.CorePluginInfo, new DivideModifierType());
RegisterModifierType(Constants.CorePluginInfo, new PercentageOfModifierType());
RegisterModifierType(Constants.CorePlugin, new SumModifierType());
RegisterModifierType(Constants.CorePlugin, new SubtractModifierType());
RegisterModifierType(Constants.CorePlugin, new MultiplicationModifierType());
RegisterModifierType(Constants.CorePlugin, new DivideModifierType());
RegisterModifierType(Constants.CorePlugin, new PercentageOfModifierType());
// Numbers - Advanced
RegisterModifierType(Constants.CorePluginInfo, new MaxModifierType());
RegisterModifierType(Constants.CorePluginInfo, new MinModifierType());
RegisterModifierType(Constants.CorePluginInfo, new ModuloModifierType());
RegisterModifierType(Constants.CorePluginInfo, new AbsoluteModifierType());
RegisterModifierType(Constants.CorePluginInfo, new PowerModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SquareRootModifierType());
RegisterModifierType(Constants.CorePlugin, new MaxModifierType());
RegisterModifierType(Constants.CorePlugin, new MinModifierType());
RegisterModifierType(Constants.CorePlugin, new ModuloModifierType());
RegisterModifierType(Constants.CorePlugin, new AbsoluteModifierType());
RegisterModifierType(Constants.CorePlugin, new PowerModifierType());
RegisterModifierType(Constants.CorePlugin, new SquareRootModifierType());
// Numbers - Rounding
RegisterModifierType(Constants.CorePluginInfo, new FloorModifierType());
RegisterModifierType(Constants.CorePluginInfo, new RoundModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CeilingModifierType());
RegisterModifierType(Constants.CorePlugin, new FloorModifierType());
RegisterModifierType(Constants.CorePlugin, new RoundModifierType());
RegisterModifierType(Constants.CorePlugin, new CeilingModifierType());
// Numbers - Trigonometric
RegisterModifierType(Constants.CorePluginInfo, new SineModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CosineModifierType());
RegisterModifierType(Constants.CorePluginInfo, new TangentModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CotangentModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SecantModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CosecantModifierType());
RegisterModifierType(Constants.CorePlugin, new SineModifierType());
RegisterModifierType(Constants.CorePlugin, new CosineModifierType());
RegisterModifierType(Constants.CorePlugin, new TangentModifierType());
RegisterModifierType(Constants.CorePlugin, new CotangentModifierType());
RegisterModifierType(Constants.CorePlugin, new SecantModifierType());
RegisterModifierType(Constants.CorePlugin, new CosecantModifierType());
// Colors
RegisterModifierType(Constants.CorePluginInfo, new SKColorSumModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorSaturateModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorDesaturateModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorBrightenModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorDarkenModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorRotateHueModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorInvertModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorSumModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorSaturateModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorDesaturateModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorBrightenModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorDarkenModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorRotateHueModifierType());
RegisterModifierType(Constants.CorePlugin, new SKColorInvertModifierType());
}
}
}

View File

@ -11,15 +11,16 @@ namespace Artemis.Core.Services
public DataModelService(IPluginManagementService pluginManagementService)
{
// Add data models of already loaded plugins
foreach (Module module in pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled))
foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled))
AddModuleDataModel(module);
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
AddDataModelExpansionDataModel(dataModelExpansion);
// Add data models of new plugins when they get enabled
pluginManagementService.PluginEnabled += PluginServiceOnPluginEnabled;
pluginManagementService.PluginFeatureEnabled += OnPluginFeatureEnabled;
}
public DataModelRegistration RegisterDataModel(DataModel dataModel)
{
if (dataModel == null)
@ -44,21 +45,16 @@ namespace Artemis.Core.Services
return (T) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel;
}
public DataModel GetPluginDataModel(PluginImplementation pluginImplementation)
public DataModel? GetPluginDataModel(PluginFeature pluginFeature)
{
return DataModelStore.Get(pluginImplementation.PluginInfo.Guid)?.DataModel;
return DataModelStore.Get(pluginFeature.Id)?.DataModel;
}
public DataModel GetPluginDataModel(Guid pluginGuid)
private void OnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
{
return DataModelStore.Get(pluginGuid)?.DataModel;
}
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Plugin is Module module)
if (e.PluginFeature is Module module)
AddModuleDataModel(module);
else if (e.PluginInfo.Plugin is BaseDataModelExpansion dataModelExpansion)
else if (e.PluginFeature is BaseDataModelExpansion dataModelExpansion)
AddDataModelExpansionDataModel(dataModelExpansion);
}
@ -68,7 +64,7 @@ namespace Artemis.Core.Services
return;
if (module.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginException(module.PluginInfo, "Module overrides GetDataModelDescription but returned null");
throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");
module.InternalDataModel.IsExpansion = module.InternalExpandsMainDataModel;
RegisterDataModel(module.InternalDataModel);
@ -77,7 +73,7 @@ namespace Artemis.Core.Services
private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion)
{
if (dataModelExpansion.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginException(dataModelExpansion.PluginInfo, "Data model expansion overrides GetDataModelDescription but returned null");
throw new ArtemisPluginFeatureException(dataModelExpansion, "Data model expansion overrides GetDataModelDescription but returned null");
dataModelExpansion.InternalDataModel.IsExpansion = true;
RegisterDataModel(dataModelExpansion.InternalDataModel);

View File

@ -12,9 +12,9 @@ namespace Artemis.Core.Services
/// <summary>
/// Registers a new condition operator for use in layer conditions
/// </summary>
/// <param name="pluginInfo">The PluginInfo of the plugin this condition operator belongs to</param>
/// <param name="plugin">The plugin this condition operator belongs to</param>
/// <param name="conditionOperator">The condition operator to register</param>
ConditionOperatorRegistration RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] BaseConditionOperator conditionOperator);
ConditionOperatorRegistration RegisterConditionOperator([NotNull] Plugin plugin, [NotNull] BaseConditionOperator conditionOperator);
/// <summary>
/// Removes a condition operator so it is no longer available for use in layer conditions

View File

@ -12,9 +12,9 @@ namespace Artemis.Core.Services
/// <summary>
/// Registers a new modifier type for use in data bindings
/// </summary>
/// <param name="pluginInfo">The PluginInfo of the plugin this modifier type belongs to</param>
/// <param name="plugin">The plugin this modifier type belongs to</param>
/// <param name="dataBindingModifierType">The modifier type to register</param>
DataBindingModifierTypeRegistration RegisterModifierType([NotNull] PluginInfo pluginInfo, [NotNull] BaseDataBindingModifierType dataBindingModifierType);
DataBindingModifierTypeRegistration RegisterModifierType([NotNull] Plugin plugin, [NotNull] BaseDataBindingModifierType dataBindingModifierType);
/// <summary>
/// Removes a modifier type so it is no longer available for use in data bindings

View File

@ -34,13 +34,7 @@ namespace Artemis.Core.Services
/// <summary>
/// If found, returns the data model of the provided plugin
/// </summary>
/// <param name="pluginImplementation">The plugin to find the data model of</param>
DataModel GetPluginDataModel(PluginImplementation pluginImplementation);
/// <summary>
/// If found, returns the data model of the provided plugin GUID
/// </summary>
/// <param name="pluginGuid">The GUID of the plugin to find the data model of</param>
DataModel GetPluginDataModel(Guid pluginGuid);
/// <param name="pluginFeature">The plugin to find the data model of</param>
DataModel? GetPluginDataModel(PluginFeature pluginFeature);
}
}

View File

@ -38,11 +38,11 @@ namespace Artemis.Core.Services
{
PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
{
BrushPluginGuid = Guid.Parse("92a9d6ba-6f7a-4937-94d5-c1d715b4141a"),
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
BrushType = "ColorBrush"
});
return LayerBrushStore.Get(defaultReference.Value.BrushPluginGuid, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
}
}
}

View File

@ -9,7 +9,7 @@ namespace Artemis.Core.Services
internal SettingsService(IPluginRepository pluginRepository)
{
_pluginSettings = new PluginSettings(Constants.CorePluginInfo, pluginRepository);
_pluginSettings = new PluginSettings(Constants.CorePlugin, pluginRepository);
}
public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default)

View File

@ -38,13 +38,13 @@ namespace Artemis.Core.Services
public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module)
{
List<ProfileEntity> profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList();
}
public ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name)
{
ProfileEntity profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, PluginGuid = module.PluginInfo.Guid};
ProfileEntity profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, ModuleId = module.Id};
_profileRepository.Add(profileEntity);
return new ProfileDescriptor(module, profileEntity);
@ -258,7 +258,7 @@ namespace Artemis.Core.Services
public ProfileDescriptor GetLastActiveProfile(ProfileModule module)
{
List<ProfileEntity> moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
List<ProfileEntity> moduleProfiles = _profileRepository.GetByModuleId(module.Id);
if (!moduleProfiles.Any())
return CreateProfileDescriptor(module, "Default");
@ -271,7 +271,7 @@ namespace Artemis.Core.Services
if (module.ActiveProfile == null)
return;
List<ProfileEntity> profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
foreach (ProfileEntity profileEntity in profileEntities)
{
profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id;
@ -285,7 +285,7 @@ namespace Artemis.Core.Services
/// <param name="surface"></param>
private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{
List<ProfileModule> profileModules = _pluginManagementService.GetPluginsOfType<ProfileModule>();
List<ProfileModule> profileModules = _pluginManagementService.GetFeaturesOfType<ProfileModule>();
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
profileModule.ActiveProfile.PopulateLeds(surface);
}

View File

@ -44,8 +44,8 @@ namespace Artemis.Core.Services
// Add all current devices
foreach (IRGBDevice rgbDevice in _rgbService.LoadedDevices)
{
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice);
configuration.Devices.Add(new ArtemisDevice(rgbDevice, pluginImplementation, configuration));
PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
configuration.Devices.Add(new ArtemisDevice(rgbDevice, pluginFeature, configuration));
}
lock (_surfaceConfigurations)
@ -136,8 +136,8 @@ namespace Artemis.Core.Services
IRGBDevice device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier);
if (device != null)
{
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(device);
surfaceConfiguration.Devices.Add(new ArtemisDevice(device, pluginImplementation, surfaceConfiguration, position));
PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(device);
surfaceConfiguration.Devices.Add(new ArtemisDevice(device, pluginFeature, surfaceConfiguration, position));
}
}
@ -178,8 +178,8 @@ namespace Artemis.Core.Services
DeviceEntity existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier);
if (existingDeviceConfig != null)
{
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginImplementation, surface, existingDeviceConfig);
PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginFeature, surface, existingDeviceConfig);
}
// Fall back on creating a new device
else
@ -189,8 +189,8 @@ namespace Artemis.Core.Services
rgbDevice.DeviceInfo,
deviceIdentifier
);
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginImplementation, surface);
PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginFeature, surface);
}
surface.Devices.Add(device);

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.ConditionOperator == conditionOperator))
throw new ArtemisCoreException($"Condition operator store store already contains operator '{conditionOperator.Description}'");
registration = new ConditionOperatorRegistration(conditionOperator, conditionOperator.PluginInfo.Plugin) {IsInStore = true};
registration = new ConditionOperatorRegistration(conditionOperator, conditionOperator.Plugin) {IsInStore = true};
Registrations.Add(registration);
}
@ -42,7 +42,7 @@ namespace Artemis.Core
{
lock (Registrations)
{
return Registrations.FirstOrDefault(r => r.PluginImplementation.PluginInfo.Guid == pluginGuid && r.ConditionOperator.GetType().Name == type);
return Registrations.FirstOrDefault(r => r.Plugin.Guid == pluginGuid && r.ConditionOperator.GetType().Name == type);
}
}

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.DataBindingModifierType == modifierType))
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{modifierType.Name}'");
typeRegistration = new DataBindingModifierTypeRegistration(modifierType, modifierType.PluginInfo.Plugin) { IsInStore = true };
typeRegistration = new DataBindingModifierTypeRegistration(modifierType, modifierType.Plugin) { IsInStore = true };
Registrations.Add(typeRegistration);
}
@ -42,7 +42,7 @@ namespace Artemis.Core
{
lock (Registrations)
{
return Registrations.FirstOrDefault(r => r.PluginImplementation.PluginInfo.Guid == pluginGuid && r.DataBindingModifierType.GetType().Name == type);
return Registrations.FirstOrDefault(r => r.Plugin.Guid == pluginGuid && r.DataBindingModifierType.GetType().Name == type);
}
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.DataModel == dataModel))
throw new ArtemisCoreException($"Data model store already contains data model '{dataModel.DataModelDescription}'");
registration = new DataModelRegistration(dataModel, dataModel.Implementation.Instance) {IsInStore = true};
registration = new DataModelRegistration(dataModel, dataModel.Feature) {IsInStore = true};
Registrations.Add(registration);
}
@ -47,11 +47,11 @@ namespace Artemis.Core
}
}
public static DataModelRegistration Get(Guid pluginGuid)
public static DataModelRegistration Get(string id)
{
lock (Registrations)
{
return Registrations.FirstOrDefault(d => d.PluginImplementation.PluginInfo.Guid == pluginGuid);
return Registrations.FirstOrDefault(d => d.PluginFeature.Id == id);
}
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.LayerBrushDescriptor == descriptor))
throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'");
registration = new LayerBrushRegistration(descriptor, descriptor.LayerBrushProvider.PluginInfo.Plugin) {IsInStore = true};
registration = new LayerBrushRegistration(descriptor, descriptor.Provider) {IsInStore = true};
Registrations.Add(registration);
}
@ -47,11 +47,11 @@ namespace Artemis.Core
}
}
public static LayerBrushRegistration Get(Guid pluginGuid, string typeName)
public static LayerBrushRegistration? Get(string id, string typeName)
{
lock (Registrations)
{
return Registrations.FirstOrDefault(d => d.PluginImplementation.PluginInfo.Guid == pluginGuid &&
return Registrations.FirstOrDefault(d => d.PluginFeature.Id == id &&
d.LayerBrushDescriptor.LayerBrushType.Name == typeName);
}
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.LayerEffectDescriptor == descriptor))
throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'");
registration = new LayerEffectRegistration(descriptor, descriptor.LayerEffectProvider.PluginInfo.Plugin) { IsInStore = true };
registration = new LayerEffectRegistration(descriptor, descriptor.Provider) { IsInStore = true };
Registrations.Add(registration);
}
@ -47,11 +47,11 @@ namespace Artemis.Core
}
}
public static LayerEffectRegistration Get(Guid pluginGuid, string typeName)
public static LayerEffectRegistration? Get(string providerId, string typeName)
{
lock (Registrations)
{
return Registrations.FirstOrDefault(d => d.PluginImplementation.PluginInfo.Guid == pluginGuid && d.LayerEffectDescriptor.LayerEffectType.Name == typeName);
return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType.Name == typeName);
}
}

View File

@ -7,12 +7,12 @@ namespace Artemis.Core
/// </summary>
public class ConditionOperatorRegistration
{
internal ConditionOperatorRegistration(BaseConditionOperator conditionOperator, PluginImplementation pluginImplementation)
internal ConditionOperatorRegistration(BaseConditionOperator conditionOperator, Plugin plugin)
{
ConditionOperator = conditionOperator;
PluginImplementation = pluginImplementation;
Plugin = plugin;
PluginImplementation.Disabled += OnDisabled;
Plugin.Disabled += OnDisabled;
}
/// <summary>
@ -23,7 +23,7 @@ namespace Artemis.Core
/// <summary>
/// Gets the plugin the condition operator is associated with
/// </summary>
public PluginImplementation PluginImplementation { get; }
public Plugin Plugin { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
@ -32,7 +32,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e)
{
PluginImplementation.Disabled -= OnDisabled;
Plugin.Disabled -= OnDisabled;
if (IsInStore)
ConditionOperatorStore.Remove(this);
}

View File

@ -7,12 +7,12 @@ namespace Artemis.Core
/// </summary>
public class DataBindingModifierTypeRegistration
{
internal DataBindingModifierTypeRegistration(BaseDataBindingModifierType dataBindingModifierType, PluginImplementation pluginImplementation)
internal DataBindingModifierTypeRegistration(BaseDataBindingModifierType dataBindingModifierType, Plugin plugin)
{
DataBindingModifierType = dataBindingModifierType;
PluginImplementation = pluginImplementation;
Plugin = plugin;
PluginImplementation.Disabled += OnDisabled;
Plugin.Disabled += OnDisabled;
}
/// <summary>
@ -23,7 +23,7 @@ namespace Artemis.Core
/// <summary>
/// Gets the plugin the data binding modifier is associated with
/// </summary>
public PluginImplementation PluginImplementation { get; }
public Plugin Plugin { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
@ -32,7 +32,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e)
{
PluginImplementation.Disabled -= OnDisabled;
Plugin.Disabled -= OnDisabled;
if (IsInStore)
DataBindingModifierTypeStore.Remove(this);
}

View File

@ -8,12 +8,12 @@ namespace Artemis.Core
/// </summary>
public class DataModelRegistration
{
internal DataModelRegistration(DataModel dataModel, PluginImplementation pluginImplementation)
internal DataModelRegistration(DataModel dataModel, PluginFeature pluginFeature)
{
DataModel = dataModel;
PluginImplementation = pluginImplementation;
PluginFeature = pluginFeature;
PluginImplementation.Disabled += OnDisabled;
PluginFeature.Disabled += OnDisabled;
}
/// <summary>
@ -24,7 +24,7 @@ namespace Artemis.Core
/// <summary>
/// Gets the plugin the data model is associated with
/// </summary>
public PluginImplementation PluginImplementation { get; }
public PluginFeature PluginFeature { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
@ -33,7 +33,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e)
{
PluginImplementation.Disabled -= OnDisabled;
PluginFeature.Disabled -= OnDisabled;
if (IsInStore)
DataModelStore.Remove(this);
}

View File

@ -8,12 +8,12 @@ namespace Artemis.Core
/// </summary>
public class LayerBrushRegistration
{
internal LayerBrushRegistration(LayerBrushDescriptor descriptor, PluginImplementation pluginImplementation)
internal LayerBrushRegistration(LayerBrushDescriptor descriptor, PluginFeature pluginFeature)
{
LayerBrushDescriptor = descriptor;
PluginImplementation = pluginImplementation;
PluginFeature = pluginFeature;
PluginImplementation.Disabled += OnDisabled;
PluginFeature.Disabled += OnDisabled;
}
/// <summary>
@ -24,7 +24,7 @@ namespace Artemis.Core
/// <summary>
/// Gets the plugin the layer brush is associated with
/// </summary>
public PluginImplementation PluginImplementation { get; }
public PluginFeature PluginFeature { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
@ -33,7 +33,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e)
{
PluginImplementation.Disabled -= OnDisabled;
PluginFeature.Disabled -= OnDisabled;
if (IsInStore)
LayerBrushStore.Remove(this);
}

View File

@ -8,12 +8,12 @@ namespace Artemis.Core
/// </summary>
public class LayerEffectRegistration
{
internal LayerEffectRegistration(LayerEffectDescriptor descriptor, PluginImplementation pluginImplementation)
internal LayerEffectRegistration(LayerEffectDescriptor descriptor, PluginFeature pluginFeature)
{
LayerEffectDescriptor = descriptor;
PluginImplementation = pluginImplementation;
PluginFeature = pluginFeature;
PluginImplementation.Disabled += OnDisabled;
PluginFeature.Disabled += OnDisabled;
}
/// <summary>
@ -24,7 +24,7 @@ namespace Artemis.Core
/// <summary>
/// Gets the plugin the layer effect is associated with
/// </summary>
public PluginImplementation PluginImplementation { get; }
public PluginFeature PluginFeature { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
@ -33,7 +33,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e)
{
PluginImplementation.Disabled -= OnDisabled;
PluginFeature.Disabled -= OnDisabled;
if (IsInStore)
LayerEffectStore.Remove(this);
}

View File

@ -3,13 +3,13 @@
namespace Artemis.Core
{
/// <summary>
/// An empty plugin used by <see cref="Constants.CorePluginInfo"/>
/// An empty data model plugin feature used by <see cref="Constants.CorePlugin" />
/// </summary>
internal class CorePluginImplementation : PluginImplementation
internal class CorePluginFeature : DataModelPluginFeature
{
public CorePluginImplementation()
public CorePluginFeature()
{
Constants.CorePluginInfo.Plugin = this;
Constants.CorePlugin.AddFeature(this);
IsEnabled = true;
}

View File

@ -1,6 +1,6 @@
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.Windows;
using Stylet;
namespace Artemis.Core
{
@ -37,8 +37,8 @@ namespace Artemis.Core
if (!Debugger.IsAttached)
Process.Start(info);
// Also attempt a graceful shutdown on the UI thread
Execute.OnUIThread(() => Application.Current.Shutdown());
// Request a graceful shutdown, whatever UI we're running can pick this up
OnShutdownRequested();
}
/// <summary>
@ -64,5 +64,18 @@ namespace Artemis.Core
{
return Process.GetCurrentProcess().MainModule.FileName;
}
#region Events
public static event EventHandler ShutdownRequested;
private static void OnShutdownRequested()
{
ShutdownRequested?.Invoke(null, EventArgs.Empty);
}
#endregion
}
}

View File

@ -121,15 +121,6 @@
"System.Memory": "4.5.3"
}
},
"Stylet": {
"type": "Direct",
"requested": "[1.3.4, )",
"resolved": "1.3.4",
"contentHash": "bCEdA+AIi+TM9SQQGLYMsFRIfzZcDUDg2Mznyr72kOkcC/cdBj01/jel4/v2aoKwbFcxVjiqmpgnbsFgMEZ4zQ==",
"dependencies": {
"System.Drawing.Common": "4.6.0"
}
},
"System.Buffers": {
"type": "Direct",
"requested": "[4.5.0, )",
@ -192,8 +183,8 @@
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "3.0.0",
"contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ=="
"resolved": "1.1.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
},
"Microsoft.NETCore.Targets": {
"type": "Transitive",
@ -210,14 +201,6 @@
"System.Runtime": "4.3.0"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "4.6.0",
"contentHash": "Edg+pFW5C8WJb680Za2kTV8TqUi6Ahl/WldRVoOVJ23UQLpDHFspa+umgFjkWZw24ETsU99Cg+ErZz683M4chg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "3.0.0"
}
},
"NETStandard.Library": {
"type": "Transitive",
"resolved": "1.6.1",
@ -557,15 +540,6 @@
"System.Runtime": "4.3.0"
}
},
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "4.6.0",
"contentHash": "2A3spjjoPZnvpVh/sDTzd+0H8ZqTdr+hH/6obB8MMfG81EJ85PmxCKDBxhBVQiA25PliKAZ1sKogDcq9mSnFEA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "3.0.0",
"Microsoft.Win32.SystemEvents": "4.6.0"
}
},
"System.Dynamic.Runtime": {
"type": "Transitive",
"resolved": "4.3.0",

View File

@ -10,7 +10,7 @@ namespace Artemis.Storage.Entities.Module
}
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
public string ModuleId { get; set; }
public int PriorityCategory { get; set; }
public int Priority { get; set; }
}

View File

@ -8,16 +8,21 @@ namespace Artemis.Storage.Entities.Plugins
/// </summary>
public class PluginEntity
{
public PluginEntity()
{
Features = new List<PluginFeatureEntity>();
}
public Guid Id { get; set; }
public bool IsEnabled { get; set; }
public List<PluginImplementationEntity> Implementations { get; set; }
public List<PluginFeatureEntity> Features { get; set; }
}
/// <summary>
/// Represents the configuration of a plugin implementation, each implementation has one configuration
/// Represents the configuration of a plugin feature, each feature has one configuration
/// </summary>
public class PluginImplementationEntity
public class PluginFeatureEntity
{
public string Type { get; set; }
public bool IsEnabled { get; set; }

View File

@ -5,7 +5,7 @@ namespace Artemis.Storage.Entities.Profile
public class DataModelPathEntity
{
public string Path { get; set; }
public Guid? DataModelGuid { get; set; }
public string DataModelId { get; set; }
public PathWrapperType WrapperType { get; set; }
}

View File

@ -5,7 +5,7 @@ namespace Artemis.Storage.Entities.Profile
public class LayerEffectEntity
{
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
public string ProviderId { get; set; }
public string EffectType { get; set; }
public string Name { get; set; }
public bool Enabled { get; set; }

View File

@ -13,7 +13,7 @@ namespace Artemis.Storage.Entities.Profile
}
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
public string ModuleId { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile.DataBindings;
namespace Artemis.Storage.Entities.Profile
@ -12,7 +11,7 @@ namespace Artemis.Storage.Entities.Profile
DataBindingEntities = new List<DataBindingEntity>();
}
public Guid PluginGuid { get; set; }
public string FeatureId { get; set; }
public string Path { get; set; }
public string Value { get; set; }

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Artemis.Storage.Entities.Module;
namespace Artemis.Storage.Repositories.Interfaces
@ -7,7 +6,7 @@ namespace Artemis.Storage.Repositories.Interfaces
public interface IModuleRepository : IRepository
{
void Add(ModuleSettingsEntity moduleSettingsEntity);
ModuleSettingsEntity GetByPluginGuid(Guid guid);
ModuleSettingsEntity GetByModuleId(string moduleId);
List<ModuleSettingsEntity> GetAll();
List<ModuleSettingsEntity> GetByCategory(int category);
void Save(ModuleSettingsEntity moduleSettingsEntity);

View File

@ -10,7 +10,7 @@ namespace Artemis.Storage.Repositories.Interfaces
void Remove(ProfileEntity profileEntity);
List<ProfileEntity> GetAll();
ProfileEntity Get(Guid id);
List<ProfileEntity> GetByPluginGuid(Guid pluginGuid);
List<ProfileEntity> GetByModuleId(string moduleId);
void Save(ProfileEntity profileEntity);
}
}

View File

@ -13,7 +13,7 @@ namespace Artemis.Storage.Repositories
internal ModuleRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<ModuleSettingsEntity>().EnsureIndex(s => s.PluginGuid, true);
_repository.Database.GetCollection<ModuleSettingsEntity>().EnsureIndex(s => s.ModuleId, true);
}
public void Add(ModuleSettingsEntity moduleSettingsEntity)
@ -21,9 +21,9 @@ namespace Artemis.Storage.Repositories
_repository.Insert(moduleSettingsEntity);
}
public ModuleSettingsEntity GetByPluginGuid(Guid guid)
public ModuleSettingsEntity GetByModuleId(string moduleId)
{
return _repository.FirstOrDefault<ModuleSettingsEntity>(s => s.PluginGuid == guid);
return _repository.FirstOrDefault<ModuleSettingsEntity>(s => s.ModuleId == moduleId);
}
public List<ModuleSettingsEntity> GetAll()

View File

@ -36,12 +36,12 @@ namespace Artemis.Storage.Repositories
return _repository.FirstOrDefault<ProfileEntity>(p => p.Id == id);
}
public List<ProfileEntity> GetByPluginGuid(Guid pluginGuid)
public List<ProfileEntity> GetByModuleId(string moduleId)
{
return _repository.Query<ProfileEntity>()
.Include(p => p.Folders)
.Include(p => p.Layers)
.Where(s => s.PluginGuid == pluginGuid)
.Where(s => s.ModuleId == moduleId)
.ToList();
}

View File

@ -8,6 +8,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=propertyinput/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdialog/@EntryIndexedValue">True</s:Boolean>

View File

@ -11,22 +11,22 @@ namespace Artemis.UI.Shared
public DataModelVisualizationRegistration(IDataModelUIService dataModelUIService,
RegistrationType registrationType,
PluginInfo pluginInfo,
Plugin plugin,
Type supportedType,
Type viewModelType)
{
_dataModelUIService = dataModelUIService;
RegistrationType = registrationType;
PluginInfo = pluginInfo;
Plugin = plugin;
SupportedType = supportedType;
ViewModelType = viewModelType;
if (PluginInfo != Constants.CorePluginInfo)
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
if (Plugin != Constants.CorePlugin)
Plugin.Disabled += InstanceOnDisabled;
}
public RegistrationType RegistrationType { get; }
public PluginInfo PluginInfo { get; }
public Plugin Plugin { get; }
public Type SupportedType { get; }
public Type ViewModelType { get; }
@ -34,8 +34,8 @@ namespace Artemis.UI.Shared
internal void Unsubscribe()
{
if (PluginInfo != Constants.CorePluginInfo)
PluginInfo.Plugin.Disabled -= InstanceOnDisabled;
if (Plugin != Constants.CorePlugin)
Plugin.Disabled -= InstanceOnDisabled;
}
private void InstanceOnDisabled(object sender, EventArgs e)

View File

@ -1,6 +1,7 @@
using Stylet;
using Artemis.Core.LayerBrushes;
using Stylet;
namespace Artemis.Core.LayerBrushes
namespace Artemis.UI.Shared.LayerBrushes
{
/// <summary>
/// Represents a view model for a brush configuration window

View File

@ -1,6 +1,7 @@
using System;
using Artemis.Core.LayerBrushes;
namespace Artemis.Core.LayerBrushes
namespace Artemis.UI.Shared.LayerBrushes
{
/// <inheritdoc />
public class LayerBrushConfigurationDialog<T> : LayerBrushConfigurationDialog where T : BrushConfigurationViewModel
@ -12,7 +13,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary>
/// Describes a UI tab for a layer brush
/// </summary>
public abstract class LayerBrushConfigurationDialog
public abstract class LayerBrushConfigurationDialog : ILayerBrushConfigurationDialog
{
/// <summary>
/// The layer brush this dialog belongs to

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