mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-31 17:53:32 +00:00
Plugins - Load features by their info before enabling plugins
General - Added missing XML comments
This commit is contained in:
parent
fa26e6b7da
commit
d5d1211649
@ -17,4 +17,20 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public PluginFeature PluginFeature { get; }
|
public PluginFeature PluginFeature { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides data about plugin feature info related events
|
||||||
|
/// </summary>
|
||||||
|
public class PluginFeatureInfoEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
internal PluginFeatureInfoEventArgs(PluginFeatureInfo pluginFeatureInfo)
|
||||||
|
{
|
||||||
|
PluginFeatureInfo = pluginFeatureInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin feature this event is related to
|
||||||
|
/// </summary>
|
||||||
|
public PluginFeatureInfo PluginFeatureInfo { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -38,6 +38,9 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DataModelPath? EventPath { get; private set; }
|
public DataModelPath? EventPath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the last time the event this condition is applied to was triggered
|
||||||
|
/// </summary>
|
||||||
public DateTime LastTrigger { get; private set; }
|
public DateTime LastTrigger { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -53,7 +56,7 @@ namespace Artemis.Core
|
|||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("DataModelConditionEvent");
|
throw new ObjectDisposedException("DataModelConditionEvent");
|
||||||
|
|
||||||
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
|
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
|
||||||
return false;
|
return false;
|
||||||
// Only evaluate to true once every time the event has been triggered since the last evaluation
|
// Only evaluate to true once every time the event has been triggered since the last evaluation
|
||||||
if (dataModelEvent.LastTrigger <= LastTrigger)
|
if (dataModelEvent.LastTrigger <= LastTrigger)
|
||||||
@ -67,7 +70,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// If there are no children, we always evaluate to true whenever the event triggered
|
// If there are no children, we always evaluate to true whenever the event triggered
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -131,7 +133,7 @@ namespace Artemis.Core
|
|||||||
// Target list
|
// Target list
|
||||||
EventPath?.Save();
|
EventPath?.Save();
|
||||||
Entity.EventPath = EventPath?.Entity;
|
Entity.EventPath = EventPath?.Entity;
|
||||||
|
|
||||||
// Children
|
// Children
|
||||||
Entity.Children.Clear();
|
Entity.Children.Clear();
|
||||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||||
|
|||||||
@ -225,7 +225,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
LedEntity ledEntity = new()
|
LedEntity ledEntity = new()
|
||||||
{
|
{
|
||||||
DeviceIdentifier = artemisLed.Device.RgbDevice.GetDeviceIdentifier(),
|
DeviceIdentifier = artemisLed.Device.Identifier,
|
||||||
LedName = artemisLed.RgbLed.Id.ToString()
|
LedName = artemisLed.RgbLed.Id.ToString()
|
||||||
};
|
};
|
||||||
LayerEntity.Leds.Add(ledEntity);
|
LayerEntity.Leds.Add(ledEntity);
|
||||||
@ -578,7 +578,7 @@ namespace Artemis.Core
|
|||||||
List<ArtemisLed> availableLeds = devices.SelectMany(d => d.Leds).ToList();
|
List<ArtemisLed> availableLeds = devices.SelectMany(d => d.Leds).ToList();
|
||||||
foreach (LedEntity ledEntity in LayerEntity.Leds)
|
foreach (LedEntity ledEntity in LayerEntity.Leds)
|
||||||
{
|
{
|
||||||
ArtemisLed? match = availableLeds.FirstOrDefault(a => a.Device.RgbDevice.GetDeviceIdentifier() == ledEntity.DeviceIdentifier &&
|
ArtemisLed? match = availableLeds.FirstOrDefault(a => a.Device.Identifier == ledEntity.DeviceIdentifier &&
|
||||||
a.RgbLed.Id.ToString() == ledEntity.LedName);
|
a.RgbLed.Id.ToString() == ledEntity.LedName);
|
||||||
if (match != null)
|
if (match != null)
|
||||||
leds.Add(match);
|
leds.Add(match);
|
||||||
|
|||||||
@ -21,6 +21,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider)
|
||||||
{
|
{
|
||||||
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = new DeviceEntity();
|
DeviceEntity = new DeviceEntity();
|
||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
@ -45,6 +46,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity)
|
||||||
{
|
{
|
||||||
|
Identifier = rgbDevice.GetDeviceIdentifier();
|
||||||
DeviceEntity = deviceEntity;
|
DeviceEntity = deviceEntity;
|
||||||
RgbDevice = rgbDevice;
|
RgbDevice = rgbDevice;
|
||||||
DeviceProvider = deviceProvider;
|
DeviceProvider = deviceProvider;
|
||||||
@ -59,6 +61,11 @@ namespace Artemis.Core
|
|||||||
ApplyKeyboardLayout();
|
ApplyKeyboardLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the (hopefully unique and persistent) ID of this device
|
||||||
|
/// </summary>
|
||||||
|
public string Identifier { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the rectangle covering the device
|
/// Gets the rectangle covering the device
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -336,7 +343,7 @@ namespace Artemis.Core
|
|||||||
internal void ApplyToEntity()
|
internal void ApplyToEntity()
|
||||||
{
|
{
|
||||||
// Other properties are computed
|
// Other properties are computed
|
||||||
DeviceEntity.Id = RgbDevice.GetDeviceIdentifier();
|
DeviceEntity.Id = Identifier;
|
||||||
|
|
||||||
DeviceEntity.InputIdentifiers.Clear();
|
DeviceEntity.InputIdentifiers.Clear();
|
||||||
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
|
||||||
|
|||||||
@ -46,6 +46,9 @@ namespace Artemis.Core
|
|||||||
private set => SetAndNotify(ref _absoluteRectangle, value);
|
private set => SetAndNotify(ref _absoluteRectangle, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the layout applied to this LED
|
||||||
|
/// </summary>
|
||||||
public ArtemisLedLayout? Layout { get; internal set; }
|
public ArtemisLedLayout? Layout { get; internal set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -53,7 +56,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
return RgbLed.ToString();
|
return RgbLed.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CalculateRectangles()
|
internal void CalculateRectangles()
|
||||||
{
|
{
|
||||||
Rectangle = RgbLed.Boundary.ToSKRect();
|
Rectangle = RgbLed.Boundary.ToSKRect();
|
||||||
|
|||||||
@ -5,6 +5,9 @@ using RGB.NET.Layout;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a LED layout decorated with extra Artemis-specific data
|
||||||
|
/// </summary>
|
||||||
public class ArtemisLedLayout
|
public class ArtemisLedLayout
|
||||||
{
|
{
|
||||||
internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
|
internal ArtemisLedLayout(ArtemisLayout deviceLayout, ILedLayout led)
|
||||||
|
|||||||
@ -29,6 +29,9 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ListLedGroup? LedGroup { get; internal set; }
|
public ListLedGroup? LedGroup { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For internal use only, is public for dependency injection but ignore pl0x
|
||||||
|
/// </summary>
|
||||||
[Inject]
|
[Inject]
|
||||||
public IRgbService? RgbService { get; set; }
|
public IRgbService? RgbService { get; set; }
|
||||||
|
|
||||||
@ -38,35 +41,6 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// <returns>Your RGB.NET effect</returns>
|
/// <returns>Your RGB.NET effect</returns>
|
||||||
public abstract IBrush GetBrush();
|
public abstract IBrush GetBrush();
|
||||||
|
|
||||||
internal void UpdateLedGroup()
|
|
||||||
{
|
|
||||||
if (LedGroup == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Layer.Parent != null)
|
|
||||||
LedGroup.ZIndex = Layer.Parent.Children.Count - Layer.Parent.Children.IndexOf(Layer);
|
|
||||||
else
|
|
||||||
LedGroup.ZIndex = 1;
|
|
||||||
|
|
||||||
List<Led> missingLeds = Layer.Leds.Where(l => !LedGroup.ContainsLed(l.RgbLed)).Select(l => l.RgbLed).ToList();
|
|
||||||
List<Led> extraLeds = LedGroup.Where(l => Layer.Leds.All(layerLed => layerLed.RgbLed != l)).ToList();
|
|
||||||
LedGroup.AddLeds(missingLeds);
|
|
||||||
LedGroup.RemoveLeds(extraLeds);
|
|
||||||
LedGroup.Brush = GetBrush();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Initialize()
|
|
||||||
{
|
|
||||||
if (RgbService == null)
|
|
||||||
throw new ArtemisCoreException("Cannot initialize RGB.NET layer brush because RgbService is not set");
|
|
||||||
|
|
||||||
LedGroup = new ListLedGroup(RgbService.Surface);
|
|
||||||
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
|
|
||||||
|
|
||||||
InitializeProperties();
|
|
||||||
UpdateLedGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -87,6 +61,35 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
internal void UpdateLedGroup()
|
||||||
|
{
|
||||||
|
if (LedGroup == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Layer.Parent != null)
|
||||||
|
LedGroup.ZIndex = Layer.Parent.Children.Count - Layer.Parent.Children.IndexOf(Layer);
|
||||||
|
else
|
||||||
|
LedGroup.ZIndex = 1;
|
||||||
|
|
||||||
|
List<Led> missingLeds = Layer.Leds.Where(l => !LedGroup.ContainsLed(l.RgbLed)).Select(l => l.RgbLed).ToList();
|
||||||
|
List<Led> extraLeds = LedGroup.Where(l => Layer.Leds.All(layerLed => layerLed.RgbLed != l)).ToList();
|
||||||
|
LedGroup.AddLeds(missingLeds);
|
||||||
|
LedGroup.RemoveLeds(extraLeds);
|
||||||
|
LedGroup.Brush = GetBrush();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Initialize()
|
||||||
|
{
|
||||||
|
if (RgbService == null)
|
||||||
|
throw new ArtemisCoreException("Cannot initialize RGB.NET layer brush because RgbService is not set");
|
||||||
|
|
||||||
|
LedGroup = new ListLedGroup(RgbService.Surface);
|
||||||
|
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
|
||||||
|
|
||||||
|
InitializeProperties();
|
||||||
|
UpdateLedGroup();
|
||||||
|
}
|
||||||
|
|
||||||
// Not used in this effect type
|
// Not used in this effect type
|
||||||
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Plugin : CorePropertyChanged, IDisposable
|
public class Plugin : CorePropertyChanged, IDisposable
|
||||||
{
|
{
|
||||||
private readonly List<PluginFeature> _features;
|
private readonly List<PluginFeatureInfo> _features;
|
||||||
|
|
||||||
private bool _isEnabled;
|
private bool _isEnabled;
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ namespace Artemis.Core
|
|||||||
Directory = directory;
|
Directory = directory;
|
||||||
Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true};
|
Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true};
|
||||||
|
|
||||||
_features = new List<PluginFeature>();
|
_features = new List<PluginFeatureInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -61,7 +61,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only collection of all features this plugin provides
|
/// Gets a read-only collection of all features this plugin provides
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<PluginFeature> Features => _features.AsReadOnly();
|
public ReadOnlyCollection<PluginFeatureInfo> Features => _features.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The assembly the plugin code lives in
|
/// The assembly the plugin code lives in
|
||||||
@ -107,7 +107,7 @@ namespace Artemis.Core
|
|||||||
/// <returns>If found, the instance of the feature</returns>
|
/// <returns>If found, the instance of the feature</returns>
|
||||||
public T? GetFeature<T>() where T : PluginFeature
|
public T? GetFeature<T>() where T : PluginFeature
|
||||||
{
|
{
|
||||||
return _features.FirstOrDefault(i => i is T) as T;
|
return _features.FirstOrDefault(i => i.Instance is T) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -122,31 +122,32 @@ namespace Artemis.Core
|
|||||||
Entity.IsEnabled = IsEnabled;
|
Entity.IsEnabled = IsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddFeature(PluginFeature feature)
|
internal void AddFeature(PluginFeatureInfo featureInfo)
|
||||||
{
|
{
|
||||||
feature.Plugin = this;
|
if (featureInfo.Plugin != this)
|
||||||
_features.Add(feature);
|
throw new ArtemisCoreException("Feature is not associated with this plugin");
|
||||||
|
_features.Add(featureInfo);
|
||||||
|
|
||||||
OnFeatureAdded(new PluginFeatureEventArgs(feature));
|
OnFeatureAdded(new PluginFeatureInfoEventArgs(featureInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveFeature(PluginFeature feature)
|
internal void RemoveFeature(PluginFeatureInfo featureInfo)
|
||||||
{
|
{
|
||||||
if (feature.IsEnabled)
|
if (featureInfo.Instance != null && featureInfo.Instance.IsEnabled)
|
||||||
throw new ArtemisCoreException("Cannot remove an enabled feature from a plugin");
|
throw new ArtemisCoreException("Cannot remove an enabled feature from a plugin");
|
||||||
|
|
||||||
_features.Remove(feature);
|
|
||||||
feature.Dispose();
|
|
||||||
|
|
||||||
OnFeatureRemoved(new PluginFeatureEventArgs(feature));
|
_features.Remove(featureInfo);
|
||||||
|
featureInfo.Instance?.Dispose();
|
||||||
|
|
||||||
|
OnFeatureRemoved(new PluginFeatureInfoEventArgs(featureInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetEnabled(bool enable)
|
internal void SetEnabled(bool enable)
|
||||||
{
|
{
|
||||||
if (IsEnabled == enable)
|
if (IsEnabled == enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!enable && Features.Any(e => e.IsEnabled))
|
if (!enable && Features.Any(e => e.Instance != null && e.Instance.IsEnabled))
|
||||||
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features");
|
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features");
|
||||||
|
|
||||||
IsEnabled = enable;
|
IsEnabled = enable;
|
||||||
@ -176,8 +177,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
foreach (PluginFeature feature in Features)
|
foreach (PluginFeatureInfo feature in Features)
|
||||||
feature.Dispose();
|
feature.Instance?.Dispose();
|
||||||
SetEnabled(false);
|
SetEnabled(false);
|
||||||
|
|
||||||
Kernel?.Dispose();
|
Kernel?.Dispose();
|
||||||
@ -189,7 +190,7 @@ namespace Artemis.Core
|
|||||||
_features.Clear();
|
_features.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@ -198,7 +199,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -214,12 +215,12 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when an feature is loaded and added to the plugin
|
/// Occurs when an feature is loaded and added to the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<PluginFeatureEventArgs>? FeatureAdded;
|
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureAdded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when an feature is disabled and removed from the plugin
|
/// Occurs when an feature is disabled and removed from the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<PluginFeatureEventArgs>? FeatureRemoved;
|
public event EventHandler<PluginFeatureInfoEventArgs>? FeatureRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the Enabled event
|
/// Invokes the Enabled event
|
||||||
@ -240,7 +241,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the FeatureAdded event
|
/// Invokes the FeatureAdded event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnFeatureAdded(PluginFeatureEventArgs e)
|
protected virtual void OnFeatureAdded(PluginFeatureInfoEventArgs e)
|
||||||
{
|
{
|
||||||
FeatureAdded?.Invoke(this, e);
|
FeatureAdded?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
@ -248,7 +249,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the FeatureRemoved event
|
/// Invokes the FeatureRemoved event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnFeatureRemoved(PluginFeatureEventArgs e)
|
protected virtual void OnFeatureRemoved(PluginFeatureInfoEventArgs e)
|
||||||
{
|
{
|
||||||
FeatureRemoved?.Invoke(this, e);
|
FeatureRemoved?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Core.DataModelExpansions;
|
using System;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
@ -16,22 +17,48 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
private string? _description;
|
private string? _description;
|
||||||
private string? _icon;
|
private string? _icon;
|
||||||
|
private PluginFeature? _instance;
|
||||||
private string _name = null!;
|
private string _name = null!;
|
||||||
private PluginFeature _pluginFeature = null!;
|
|
||||||
|
|
||||||
internal PluginFeatureInfo()
|
internal PluginFeatureInfo(Plugin plugin, Type featureType, PluginFeatureAttribute? attribute)
|
||||||
{
|
{
|
||||||
|
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
|
||||||
|
FeatureType = featureType ?? throw new ArgumentNullException(nameof(featureType));
|
||||||
|
|
||||||
|
Name = attribute?.Name ?? featureType.Name.Humanize(LetterCasing.Title);
|
||||||
|
Description = attribute?.Description;
|
||||||
|
Icon = attribute?.Icon;
|
||||||
|
|
||||||
|
if (Icon != null) return;
|
||||||
|
if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType))
|
||||||
|
Icon = "TableAdd";
|
||||||
|
else if (typeof(DeviceProvider).IsAssignableFrom(featureType))
|
||||||
|
Icon = "Devices";
|
||||||
|
else if (typeof(ProfileModule).IsAssignableFrom(featureType))
|
||||||
|
Icon = "VectorRectangle";
|
||||||
|
else if (typeof(Module).IsAssignableFrom(featureType))
|
||||||
|
Icon = "GearBox";
|
||||||
|
else if (typeof(LayerBrushProvider).IsAssignableFrom(featureType))
|
||||||
|
Icon = "Brush";
|
||||||
|
else if (typeof(LayerEffectProvider).IsAssignableFrom(featureType))
|
||||||
|
Icon = "AutoAwesome";
|
||||||
|
else
|
||||||
|
Icon = "Plugin";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal PluginFeatureInfo(PluginFeature instance, PluginFeatureAttribute? attribute)
|
internal PluginFeatureInfo(Plugin plugin, PluginFeatureAttribute? attribute, PluginFeature instance)
|
||||||
{
|
{
|
||||||
|
if (instance == null) throw new ArgumentNullException(nameof(instance));
|
||||||
|
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
|
||||||
|
FeatureType = instance.GetType();
|
||||||
|
|
||||||
Name = attribute?.Name ?? instance.GetType().Name.Humanize(LetterCasing.Title);
|
Name = attribute?.Name ?? instance.GetType().Name.Humanize(LetterCasing.Title);
|
||||||
Description = attribute?.Description;
|
Description = attribute?.Description;
|
||||||
Icon = attribute?.Icon;
|
Icon = attribute?.Icon;
|
||||||
PluginFeature = instance;
|
Instance = instance;
|
||||||
|
|
||||||
if (Icon != null) return;
|
if (Icon != null) return;
|
||||||
Icon = PluginFeature switch
|
Icon = Instance switch
|
||||||
{
|
{
|
||||||
BaseDataModelExpansion => "TableAdd",
|
BaseDataModelExpansion => "TableAdd",
|
||||||
DeviceProvider => "Devices",
|
DeviceProvider => "Devices",
|
||||||
@ -43,6 +70,16 @@ namespace Artemis.Core
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin this feature info is associated with
|
||||||
|
/// </summary>
|
||||||
|
public Plugin Plugin { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the feature
|
||||||
|
/// </summary>
|
||||||
|
public Type FeatureType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the plugin
|
/// The name of the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -75,18 +112,18 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin this info is associated with
|
/// Gets the feature this info is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PluginFeature PluginFeature
|
public PluginFeature? Instance
|
||||||
{
|
{
|
||||||
get => _pluginFeature;
|
get => _instance;
|
||||||
internal set => SetAndNotify(ref _pluginFeature, value);
|
internal set => SetAndNotify(ref _instance, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return PluginFeature.Id;
|
return Instance?.Id ?? "Uninitialized feature";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ namespace Artemis.Core.Services
|
|||||||
bool IsRenderPaused { get; set; }
|
bool IsRenderPaused { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recreates the Texture to use the given <see cref="SKBitmap"/>
|
/// Recreates the Texture to use the given <see cref="SKBitmap" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bitmap"></param>
|
/// <param name="bitmap"></param>
|
||||||
void UpdateTexture(SKBitmap bitmap);
|
void UpdateTexture(SKBitmap bitmap);
|
||||||
@ -119,7 +119,16 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void SaveDevices();
|
void SaveDevices();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables the provided device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to enable</param>
|
||||||
void EnableDevice(ArtemisDevice device);
|
void EnableDevice(ArtemisDevice device);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the provided device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The device to disable</param>
|
||||||
void DisableDevice(ArtemisDevice device);
|
void DisableDevice(ArtemisDevice device);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -133,7 +142,7 @@ namespace Artemis.Core.Services
|
|||||||
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
event EventHandler<DeviceEventArgs> DeviceRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when the surface has had modifications to its LED collection
|
/// Occurs when the surface has had modifications to its LED collection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler LedsChanged;
|
event EventHandler LedsChanged;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ namespace Artemis.Core.Services
|
|||||||
// If this is a profile module, animate profile disable
|
// If this is a profile module, animate profile disable
|
||||||
// module.Deactivate would do the same but without animation
|
// module.Deactivate would do the same but without animation
|
||||||
if (module.IsActivated && module is ProfileModule profileModule)
|
if (module.IsActivated && module is ProfileModule profileModule)
|
||||||
await profileModule.ChangeActiveProfileAnimated(null, null);
|
await profileModule.ChangeActiveProfileAnimated(null, Enumerable.Empty<ArtemisDevice>());
|
||||||
|
|
||||||
module.Deactivate(false);
|
module.Deactivate(false);
|
||||||
}
|
}
|
||||||
@ -210,6 +210,7 @@ namespace Artemis.Core.Services
|
|||||||
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
|
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
|
||||||
List<Task> tasks = new();
|
List<Task> tasks = new();
|
||||||
foreach (Module module in modules)
|
foreach (Module module in modules)
|
||||||
|
{
|
||||||
lock (module)
|
lock (module)
|
||||||
{
|
{
|
||||||
bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled;
|
bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled;
|
||||||
@ -218,6 +219,7 @@ namespace Artemis.Core.Services
|
|||||||
else if (!shouldBeActivated && module.IsActivated)
|
else if (!shouldBeActivated && module.IsActivated)
|
||||||
tasks.Add(DeactivateModule(module));
|
tasks.Add(DeactivateModule(module));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ using System.IO;
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.Storage.Entities.Plugins;
|
using Artemis.Storage.Entities.Plugins;
|
||||||
@ -130,7 +129,11 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
lock (_plugins)
|
lock (_plugins)
|
||||||
{
|
{
|
||||||
return _plugins.Where(p => p.IsEnabled).SelectMany(p => p.Features.Where(i => i.IsEnabled && i is T)).Cast<T>().ToList();
|
return _plugins.Where(p => p.IsEnabled)
|
||||||
|
.SelectMany(p => p.Features.Where(f => f.Instance != null && f.Instance.IsEnabled && f.Instance is T))
|
||||||
|
.Select(f => f.Instance)
|
||||||
|
.Cast<T>()
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +189,7 @@ namespace Artemis.Core.Services
|
|||||||
// Load the plugin assemblies into the plugin context
|
// Load the plugin assemblies into the plugin context
|
||||||
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
|
DirectoryInfo pluginDirectory = new(Path.Combine(Constants.DataFolder, "plugins"));
|
||||||
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories())
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadPlugin(subDirectory);
|
LoadPlugin(subDirectory);
|
||||||
@ -194,6 +198,7 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
_logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
|
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
|
||||||
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
|
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
|
||||||
@ -218,7 +223,7 @@ namespace Artemis.Core.Services
|
|||||||
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
|
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
|
||||||
EnablePlugin(plugin, false, ignorePluginLock);
|
EnablePlugin(plugin, false, ignorePluginLock);
|
||||||
|
|
||||||
_logger.Debug("Enabled {count} plugin(s)", _plugins.Where(p => p.IsEnabled).Sum(p => p.Features.Count(f => f.IsEnabled)));
|
_logger.Debug("Enabled {count} plugin(s)", _plugins.Count(p => p.IsEnabled));
|
||||||
// ReSharper restore InconsistentlySynchronizedField
|
// ReSharper restore InconsistentlySynchronizedField
|
||||||
|
|
||||||
LoadingPlugins = false;
|
LoadingPlugins = false;
|
||||||
@ -289,6 +294,28 @@ namespace Artemis.Core.Services
|
|||||||
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
|
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
// ReSharper disable once RedundantEnumerableCastCall - Casting from nullable to non-nullable here
|
||||||
|
new AggregateException(e.LoaderExceptions.Where(le => le != null).Cast<Exception>().ToArray())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Type featureType in featureTypes)
|
||||||
|
plugin.AddFeature(new PluginFeatureInfo(plugin, featureType, (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute))));
|
||||||
|
|
||||||
|
if (!featureTypes.Any())
|
||||||
|
_logger.Warning("Plugin {plugin} contains no features", plugin);
|
||||||
|
|
||||||
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
||||||
if (bootstrappers.Count > 1)
|
if (bootstrappers.Count > 1)
|
||||||
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
||||||
@ -315,60 +342,45 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
plugin.SetEnabled(true);
|
plugin.SetEnabled(true);
|
||||||
|
|
||||||
// Get the Plugin feature from the main assembly and if there is only one, instantiate it
|
// Create instances of each feature
|
||||||
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",
|
|
||||||
// ReSharper disable once RedundantEnumerableCastCall - Casting from nullable to non-nullable here
|
|
||||||
new AggregateException(e.LoaderExceptions.Where(le => le != null).Cast<Exception>().ToArray())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
|
||||||
foreach (Type featureType in featureTypes)
|
foreach (PluginFeatureInfo featureInfo in plugin.Features)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope();
|
plugin.Kernel.Bind(featureInfo.FeatureType).ToSelf().InSingletonScope();
|
||||||
|
|
||||||
// Include Plugin as a parameter for the PluginSettingsProvider
|
// Include Plugin as a parameter for the PluginSettingsProvider
|
||||||
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
|
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
|
||||||
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureType, parameters);
|
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureInfo.FeatureType, parameters);
|
||||||
|
|
||||||
// Get the PluginFeature attribute which contains extra info on the feature
|
// Get the PluginFeature attribute which contains extra info on the feature
|
||||||
PluginFeatureAttribute? pluginFeatureAttribute = (PluginFeatureAttribute?) Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute));
|
featureInfo.Instance = instance;
|
||||||
instance.Info = new PluginFeatureInfo(instance, pluginFeatureAttribute);
|
instance.Info = featureInfo;
|
||||||
plugin.AddFeature(instance);
|
instance.Plugin = plugin;
|
||||||
|
|
||||||
// Load the enabled state and if not found, default to true
|
// Load the enabled state and if not found, default to true
|
||||||
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
|
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureInfo.FeatureType.FullName) ??
|
||||||
new PluginFeatureEntity {IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureType.FullName!};
|
new PluginFeatureEntity {IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureInfo.FeatureType.FullName!};
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin);
|
_logger.Warning(new ArtemisPluginException(plugin, "Failed to instantiate feature", e), "Failed to instantiate feature", plugin);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Activate plugins after they are all loaded
|
// Activate features after they are all loaded
|
||||||
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
|
foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && f.Instance.Entity.IsEnabled))
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EnablePluginFeature(pluginFeature, false, !ignorePluginLock);
|
EnablePluginFeature(pluginFeature.Instance!, false, !ignorePluginLock);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// ignored, logged in EnablePluginFeature
|
// ignored, logged in EnablePluginFeature
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (saveState)
|
if (saveState)
|
||||||
{
|
{
|
||||||
@ -406,12 +418,10 @@ namespace Artemis.Core.Services
|
|||||||
if (!plugin.IsEnabled)
|
if (!plugin.IsEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (plugin.Features.Any())
|
foreach (PluginFeatureInfo pluginFeatureInfo in plugin.Features)
|
||||||
{
|
{
|
||||||
PluginFeature feature = plugin.Features[0];
|
if (pluginFeatureInfo.Instance != null && pluginFeatureInfo.Instance.IsEnabled)
|
||||||
if (feature.IsEnabled)
|
DisablePluginFeature(pluginFeatureInfo.Instance, false);
|
||||||
DisablePluginFeature(feature, false);
|
|
||||||
plugin.RemoveFeature(feature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.SetEnabled(false);
|
plugin.SetEnabled(false);
|
||||||
@ -450,7 +460,6 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
Plugin? existing = _plugins.FirstOrDefault(p => p.Guid == pluginInfo.Guid);
|
Plugin? existing = _plugins.FirstOrDefault(p => p.Guid == pluginInfo.Guid);
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RemovePlugin(existing);
|
RemovePlugin(existing);
|
||||||
@ -459,7 +468,6 @@ namespace Artemis.Core.Services
|
|||||||
{
|
{
|
||||||
throw new ArtemisPluginException("A plugin with the same GUID is already loaded, failed to remove old version", e);
|
throw new ArtemisPluginException("A plugin with the same GUID is already loaded, failed to remove old version", e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
string targetDirectory = pluginInfo.Main.Split(".dll")[0].Replace("/", "").Replace("\\", "");
|
string targetDirectory = pluginInfo.Main.Split(".dll")[0].Replace("/", "").Replace("\\", "");
|
||||||
string uniqueTargetDirectory = targetDirectory;
|
string uniqueTargetDirectory = targetDirectory;
|
||||||
@ -582,9 +590,11 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
private void SavePlugin(Plugin plugin)
|
private void SavePlugin(Plugin plugin)
|
||||||
{
|
{
|
||||||
foreach (PluginFeature pluginFeature in plugin.Features)
|
foreach (PluginFeatureInfo featureInfo in plugin.Features.Where(i => i.Instance != null))
|
||||||
if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
|
{
|
||||||
plugin.Entity.Features.Add(pluginFeature.Entity);
|
if (plugin.Entity.Features.All(i => i.Type != featureInfo.FeatureType.FullName))
|
||||||
|
plugin.Entity.Features.Add(featureInfo.Instance!.Entity);
|
||||||
|
}
|
||||||
|
|
||||||
_pluginRepository.SavePlugin(plugin.Entity);
|
_pluginRepository.SavePlugin(plugin.Entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,7 +146,8 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
|
void ActivatingProfilePluginToggle(object? sender, PluginEventArgs e)
|
||||||
{
|
{
|
||||||
InstantiateProfile(profile);
|
if (!profile.Disposed)
|
||||||
|
InstantiateProfile(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This could happen during activation so subscribe to it
|
// This could happen during activation so subscribe to it
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
public CorePluginFeature()
|
public CorePluginFeature()
|
||||||
{
|
{
|
||||||
Constants.CorePlugin.AddFeature(this);
|
Constants.CorePlugin.AddFeature(new PluginFeatureInfo(Constants.CorePlugin, null, this));
|
||||||
IsEnabled = true;
|
IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ namespace Artemis.Core
|
|||||||
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
|
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
|
||||||
profileEntityLayer.Leds.AddRange(_devices.SelectMany(d => d.Leds).Select(l => new LedEntity
|
profileEntityLayer.Leds.AddRange(_devices.SelectMany(d => d.Leds).Select(l => new LedEntity
|
||||||
{
|
{
|
||||||
DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
|
DeviceIdentifier = l.Device.Identifier,
|
||||||
LedName = l.RgbLed.Id.ToString()
|
LedName = l.RgbLed.Id.ToString()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
public interface ISettingsVmFactory : IVmFactory
|
public interface ISettingsVmFactory : IVmFactory
|
||||||
{
|
{
|
||||||
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
|
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
|
||||||
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeature feature);
|
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo);
|
||||||
DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
|
DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
|
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
|
||||||
{
|
{
|
||||||
ActiveItem?.Evaluate();
|
ActiveItem?.Evaluate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
@ -14,7 +13,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
{
|
{
|
||||||
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private ILayerProperty? _selectedDataBinding;
|
private ILayerProperty _selectedDataBinding;
|
||||||
private int _selectedItemIndex;
|
private int _selectedItemIndex;
|
||||||
private bool _updating;
|
private bool _updating;
|
||||||
|
|
||||||
@ -95,7 +94,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
SubscribeToSelectedDataBinding();
|
SubscribeToSelectedDataBinding();
|
||||||
base.OnInitialActivate();
|
base.OnInitialActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnActivate()
|
protected override void OnActivate()
|
||||||
{
|
{
|
||||||
SelectedItemIndex = 0;
|
SelectedItemIndex = 0;
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<!-- Icon column -->
|
<!-- Icon column -->
|
||||||
<shared:ArtemisIcon Grid.Column="0"
|
<shared:ArtemisIcon Grid.Column="0"
|
||||||
Icon="{Binding Feature.Info.Icon}"
|
Icon="{Binding FeatureInfo.Icon}"
|
||||||
Width="20"
|
Width="20"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@ -42,12 +42,12 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<!-- Display name column -->
|
<!-- Display name column -->
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Feature.Info.Name}" Style="{StaticResource MaterialDesignTextBlock}" VerticalAlignment="Center" ToolTip="{Binding Feature.Info.Description}" />
|
<TextBlock Grid.Column="1" Text="{Binding FeatureInfo.Name}" Style="{StaticResource MaterialDesignTextBlock}" VerticalAlignment="Center" ToolTip="{Binding FeatureInfo.Description}" />
|
||||||
|
|
||||||
<!-- Enable toggle column -->
|
<!-- Enable toggle column -->
|
||||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="8"
|
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="8"
|
||||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}">
|
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}">
|
||||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}">
|
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}" IsEnabled="{Binding FeatureInfo.Plugin.IsEnabled}">
|
||||||
Feature enabled
|
Feature enabled
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
private bool _enabling;
|
private bool _enabling;
|
||||||
private readonly IMessageService _messageService;
|
private readonly IMessageService _messageService;
|
||||||
|
|
||||||
public PluginFeatureViewModel(PluginFeature feature,
|
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
|
||||||
IDialogService dialogService,
|
IDialogService dialogService,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IMessageService messageService)
|
IMessageService messageService)
|
||||||
@ -25,11 +25,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
_messageService = messageService;
|
_messageService = messageService;
|
||||||
|
|
||||||
Feature = feature;
|
FeatureInfo = pluginFeatureInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginFeature Feature { get; }
|
public PluginFeatureInfo FeatureInfo { get; }
|
||||||
public Exception LoadException => Feature.LoadException;
|
public Exception LoadException => FeatureInfo.Instance?.LoadException;
|
||||||
|
|
||||||
public bool Enabling
|
public bool Enabling
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get => Feature.IsEnabled;
|
get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
|
||||||
set => Task.Run(() => UpdateEnabled(value));
|
set => Task.Run(() => UpdateEnabled(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
if (LoadException == null)
|
if (LoadException == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_dialogService.ShowExceptionDialog("Feature failed to enable", Feature.LoadException);
|
_dialogService.ShowExceptionDialog("Feature failed to enable", LoadException);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
@ -83,7 +83,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
private async Task UpdateEnabled(bool enable)
|
private async Task UpdateEnabled(bool enable)
|
||||||
{
|
{
|
||||||
if (IsEnabled == enable)
|
if (IsEnabled == enable || FeatureInfo.Instance == null)
|
||||||
{
|
{
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
return;
|
return;
|
||||||
@ -95,11 +95,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Run(() => _pluginManagementService.EnablePluginFeature(Feature, true));
|
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance, true));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_messageService.ShowMessage($"Failed to enable {Feature.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
_messageService.ShowMessage($"Failed to enable {FeatureInfo.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -108,7 +108,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_pluginManagementService.DisablePluginFeature(Feature, true);
|
_pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true);
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,13 +117,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PluginFeature != Feature) return;
|
if (e.PluginFeature != FeatureInfo.Instance) return;
|
||||||
Enabling = true;
|
Enabling = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFeatureEnableStopped(object sender, PluginFeatureEventArgs e)
|
private void OnFeatureEnableStopped(object sender, PluginFeatureEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PluginFeature != Feature) return;
|
if (e.PluginFeature != FeatureInfo.Instance) return;
|
||||||
Enabling = false;
|
Enabling = false;
|
||||||
|
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
|
|||||||
@ -116,21 +116,11 @@
|
|||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock Margin="10 10 0 5" Style="{StaticResource MaterialDesignBody2TextBlock}">Plugin features</TextBlock>
|
<TextBlock Margin="10 10 0 5" Style="{StaticResource MaterialDesignBody2TextBlock}">Plugin features</TextBlock>
|
||||||
<TextBlock Grid.Row="1"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Margin="0 30 0 0"
|
|
||||||
Style="{StaticResource MaterialDesignTextBlock}"
|
|
||||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
|
||||||
Visibility="{Binding IsEnabled, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
|
||||||
Enable the plugin to view its features
|
|
||||||
</TextBlock>
|
|
||||||
|
|
||||||
<ListBox Grid.Row="1"
|
<ListBox Grid.Row="1"
|
||||||
MaxHeight="135"
|
MaxHeight="135"
|
||||||
ItemsSource="{Binding Items}"
|
ItemsSource="{Binding Items}"
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
VirtualizingPanel.ScrollUnit="Pixel"
|
VirtualizingPanel.ScrollUnit="Pixel">
|
||||||
Visibility="{Binding IsEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<shared:ScrollParentWhenAtMax />
|
<shared:ScrollParentWhenAtMax />
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -117,22 +117,12 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
{
|
{
|
||||||
Plugin.FeatureAdded += PluginOnFeatureAdded;
|
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||||
Plugin.FeatureRemoved += PluginOnFeatureRemoved;
|
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo));
|
||||||
foreach (PluginFeature pluginFeature in Plugin.Features)
|
|
||||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeature));
|
|
||||||
|
|
||||||
base.OnInitialActivate();
|
base.OnInitialActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClose()
|
|
||||||
{
|
|
||||||
Plugin.FeatureAdded -= PluginOnFeatureAdded;
|
|
||||||
Plugin.FeatureRemoved -= PluginOnFeatureRemoved;
|
|
||||||
|
|
||||||
base.OnClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateEnabled(bool enable)
|
private async Task UpdateEnabled(bool enable)
|
||||||
{
|
{
|
||||||
if (IsEnabled == enable)
|
if (IsEnabled == enable)
|
||||||
@ -178,17 +168,5 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PluginOnFeatureRemoved(object sender, PluginFeatureEventArgs e)
|
|
||||||
{
|
|
||||||
PluginFeatureViewModel viewModel = Items.FirstOrDefault(i => i.Feature == e.PluginFeature);
|
|
||||||
if (viewModel != null)
|
|
||||||
Items.Remove(viewModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PluginOnFeatureAdded(object sender, PluginFeatureEventArgs e)
|
|
||||||
{
|
|
||||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(e.PluginFeature));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
@ -27,9 +28,8 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
|
|||||||
Items.Clear();
|
Items.Clear();
|
||||||
|
|
||||||
// _pluginManagementService.GetFeaturesOfType<>() will only give us enabled features so lets get all of them this way
|
// _pluginManagementService.GetFeaturesOfType<>() will only give us enabled features so lets get all of them this way
|
||||||
IEnumerable<DeviceProvider> features = _pluginManagementService.GetAllPlugins()
|
IEnumerable<PluginFeatureInfo> features = _pluginManagementService.GetAllPlugins()
|
||||||
.SelectMany(p => p.Features.Where(f => f is DeviceProvider))
|
.SelectMany(p => p.Features.Where(f => typeof(DeviceProvider).IsAssignableFrom(f.FeatureType)))
|
||||||
.Cast<DeviceProvider>()
|
|
||||||
.OrderBy(d => d.GetType().Name);
|
.OrderBy(d => d.GetType().Name);
|
||||||
Items.AddRange(features.Select(d => _settingsVmFactory.CreatePluginFeatureViewModel(d)));
|
Items.AddRange(features.Select(d => _settingsVmFactory.CreatePluginFeatureViewModel(d)));
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
private async void WindowServiceOnMainWindowOpened(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
List<ArtemisDevice> devices = _rgbService.Devices.Where(device => DeviceNeedsLayout(device) && !_ignoredDevices.Contains(device)).ToList();
|
||||||
foreach (ArtemisDevice artemisDevice in devices)
|
foreach (ArtemisDevice artemisDevice in devices)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user