mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
Core - Refactored effects
Profile editor - Added effect creation
This commit is contained in:
1 parent
e5ba48c7f4
commit
2bf36fbf20
61 files changed
+1005
-763
No files matched your search
@@ -230,7 +230,7 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
if (genericType.IsInterface)
|
||||
foreach (var i in typeToCheck.GetInterfaces())
|
||||
foreach (Type i in typeToCheck.GetInterfaces())
|
||||
if (i.IsOfGenericType(genericType, out concreteGenericType))
|
||||
return true;
|
||||
|
||||
|
||||
@@ -227,19 +227,29 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override void Enable()
|
||||
{
|
||||
if (Enabled)
|
||||
return;
|
||||
|
||||
// No checks here, effects will do their own checks to ensure they never enable twice
|
||||
// Also not enabling children, they'll enable themselves during their own Update
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalEnable();
|
||||
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Disable()
|
||||
{
|
||||
// No checks here, effects will do their own checks to ensure they never disable twice
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalDisable();
|
||||
|
||||
// Disabling children since their Update won't get called with their parent disabled
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
{
|
||||
if (profileElement is RenderProfileElement renderProfileElement && renderProfileElement.ShouldBeEnabled)
|
||||
renderProfileElement.Enable();
|
||||
if (profileElement is RenderProfileElement renderProfileElement)
|
||||
renderProfileElement.Disable();
|
||||
}
|
||||
|
||||
Enabled = true;
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -250,24 +260,6 @@ namespace Artemis.Core
|
||||
baseLayerEffect.InternalUpdate(Timeline); ;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Disable()
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalDisable();
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
{
|
||||
if (profileElement is RenderProfileElement renderProfileElement)
|
||||
renderProfileElement.Disable();
|
||||
}
|
||||
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a property affecting the rendering properties of this folder has been updated
|
||||
/// </summary>
|
||||
@@ -308,7 +300,6 @@ namespace Artemis.Core
|
||||
|
||||
internal override void Load()
|
||||
{
|
||||
ExpandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups);
|
||||
Reset();
|
||||
|
||||
// Load child folders
|
||||
@@ -340,8 +331,6 @@ namespace Artemis.Core
|
||||
FolderEntity.Suspended = Suspended;
|
||||
|
||||
FolderEntity.ProfileId = Profile.EntityId;
|
||||
FolderEntity.ExpandedPropertyGroups.Clear();
|
||||
FolderEntity.ExpandedPropertyGroups.AddRange(ExpandedPropertyGroups);
|
||||
|
||||
SaveRenderElement();
|
||||
}
|
||||
|
||||
@@ -296,7 +296,6 @@ namespace Artemis.Core
|
||||
Suspended = LayerEntity.Suspended;
|
||||
Order = LayerEntity.Order;
|
||||
|
||||
ExpandedPropertyGroups.AddRange(LayerEntity.ExpandedPropertyGroups);
|
||||
LoadRenderElement();
|
||||
Adapter.Load();
|
||||
}
|
||||
@@ -313,8 +312,6 @@ namespace Artemis.Core
|
||||
LayerEntity.Suspended = Suspended;
|
||||
LayerEntity.Name = Name;
|
||||
LayerEntity.ProfileId = Profile.EntityId;
|
||||
LayerEntity.ExpandedPropertyGroups.Clear();
|
||||
LayerEntity.ExpandedPropertyGroups.AddRange(ExpandedPropertyGroups);
|
||||
|
||||
General.ApplyToEntity();
|
||||
Transform.ApplyToEntity();
|
||||
@@ -382,7 +379,7 @@ namespace Artemis.Core
|
||||
|
||||
UpdateDisplayCondition();
|
||||
UpdateTimeline(deltaTime);
|
||||
|
||||
|
||||
if (ShouldBeEnabled)
|
||||
Enable();
|
||||
else if (Timeline.IsFinished && !_renderCopies.Any())
|
||||
@@ -505,9 +502,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override void Enable()
|
||||
{
|
||||
if (Enabled)
|
||||
return;
|
||||
|
||||
// No checks here, the brush and effects will do their own checks to ensure they never enable twice
|
||||
bool tryOrBreak = TryOrBreak(() => LayerBrush?.InternalEnable(), "Failed to enable layer brush");
|
||||
if (!tryOrBreak)
|
||||
return;
|
||||
@@ -519,10 +514,21 @@ namespace Artemis.Core
|
||||
}, "Failed to enable one or more effects");
|
||||
if (!tryOrBreak)
|
||||
return;
|
||||
|
||||
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Disable()
|
||||
{
|
||||
// No checks here, the brush and effects will do their own checks to ensure they never disable twice
|
||||
LayerBrush?.InternalDisable();
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalDisable();
|
||||
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OverrideTimelineAndApply(TimeSpan position)
|
||||
{
|
||||
@@ -536,19 +542,6 @@ namespace Artemis.Core
|
||||
baseLayerEffect.InternalUpdate(Timeline);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Disable()
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
LayerBrush?.InternalDisable();
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalDisable();
|
||||
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Reset()
|
||||
{
|
||||
@@ -822,6 +815,7 @@ namespace Artemis.Core
|
||||
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||
General.BlendMode.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||
Transform.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||
LayerBrush?.Update(0);
|
||||
|
||||
OnLayerBrushUpdated();
|
||||
ClearBrokenState("Failed to initialize layer brush");
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property group on a layer
|
||||
/// <para>
|
||||
/// Note: You cannot initialize property groups yourself. If properly placed and annotated, the Artemis core will
|
||||
/// initialize these for you.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract class LayerEffectPropertyGroup : LayerPropertyGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not this layer effect is enabled
|
||||
/// </summary>
|
||||
[PropertyDescription(Name = "Enabled", Description = "Whether or not this layer effect is enabled")]
|
||||
public BoolLayerProperty IsEnabled { get; set; } = null!;
|
||||
|
||||
internal void InitializeIsEnabled()
|
||||
{
|
||||
IsEnabled.DefaultValue = true;
|
||||
if (!IsEnabled.IsLoadedFromStorage)
|
||||
IsEnabled.SetCurrentValue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ namespace Artemis.Core
|
||||
/// or existing keyframe.
|
||||
/// </param>
|
||||
/// <returns>The keyframe if one was created or updated.</returns>
|
||||
public LayerPropertyKeyframe<T>? SetCurrentValue(T value, TimeSpan? time)
|
||||
public LayerPropertyKeyframe<T>? SetCurrentValue(T value, TimeSpan? time = null)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
|
||||
@@ -249,6 +249,15 @@ namespace Artemis.Core
|
||||
layerPropertyGroup.Update(timeline);
|
||||
}
|
||||
|
||||
internal void MoveLayerProperty(ILayerProperty layerProperty, int index)
|
||||
{
|
||||
if (!_layerProperties.Contains(layerProperty))
|
||||
return;
|
||||
|
||||
_layerProperties.Remove(layerProperty);
|
||||
_layerProperties.Insert(index, layerProperty);
|
||||
}
|
||||
|
||||
internal virtual void OnVisibilityChanged()
|
||||
{
|
||||
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
||||
@@ -292,7 +301,7 @@ namespace Artemis.Core
|
||||
throw new ArtemisPluginException($"Property with PropertyGroupDescription attribute must be of type LayerPropertyGroup: {propertyGroupDescription.Identifier}");
|
||||
if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance))
|
||||
throw new ArtemisPluginException($"Failed to create instance of layer property group: {propertyGroupDescription.Identifier}");
|
||||
|
||||
|
||||
PropertyGroupEntity entity = GetPropertyGroupEntity(propertyGroupDescription.Identifier);
|
||||
instance.Initialize(ProfileElement, this, propertyGroupDescription, entity);
|
||||
|
||||
|
||||
@@ -4,393 +4,393 @@ using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.LayerEffects;
|
||||
using Artemis.Core.LayerEffects.Placeholder;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an element of a <see cref="Profile" /> that has advanced rendering capabilities
|
||||
/// </summary>
|
||||
public abstract class RenderProfileElement : ProfileElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an element of a <see cref="Profile" /> that has advanced rendering capabilities
|
||||
/// </summary>
|
||||
public abstract class RenderProfileElement : ProfileElement
|
||||
private SKRectI _bounds;
|
||||
private SKPath? _path;
|
||||
|
||||
internal RenderProfileElement(ProfileElement parent, Profile profile) : base(profile)
|
||||
{
|
||||
private SKRectI _bounds;
|
||||
private SKPath? _path;
|
||||
|
||||
internal RenderProfileElement(ProfileElement parent, Profile profile) : base(profile)
|
||||
{
|
||||
Timeline = new Timeline();
|
||||
ExpandedPropertyGroups = new List<string>();
|
||||
LayerEffectsList = new List<BaseLayerEffect>();
|
||||
LayerEffects = new ReadOnlyCollection<BaseLayerEffect>(LayerEffectsList);
|
||||
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
_displayCondition = new AlwaysOnCondition(this);
|
||||
|
||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this render element and its layers/brushes are enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this render element and its layers/brushes should be enabled
|
||||
/// </summary>
|
||||
public abstract bool ShouldBeEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list of all layer properties present on this render element
|
||||
/// </summary>
|
||||
/// <returns>A list of all layer properties present on this render element</returns>
|
||||
public abstract List<ILayerProperty> GetAllLayerProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a layer effect has been added or removed to this render element
|
||||
/// </summary>
|
||||
public event EventHandler? LayerEffectsUpdated;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.Dispose();
|
||||
|
||||
if (DisplayCondition is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
internal void LoadRenderElement()
|
||||
{
|
||||
Timeline = RenderElementEntity.Timeline != null
|
||||
? new Timeline(RenderElementEntity.Timeline)
|
||||
: new Timeline();
|
||||
|
||||
DisplayCondition = RenderElementEntity.DisplayCondition switch
|
||||
{
|
||||
AlwaysOnConditionEntity entity => new AlwaysOnCondition(entity, this),
|
||||
PlayOnceConditionEntity entity => new PlayOnceCondition(entity, this),
|
||||
StaticConditionEntity entity => new StaticCondition(entity, this),
|
||||
EventConditionEntity entity => new EventCondition(entity, this),
|
||||
_ => DisplayCondition
|
||||
};
|
||||
|
||||
ActivateEffects();
|
||||
}
|
||||
|
||||
internal void SaveRenderElement()
|
||||
{
|
||||
RenderElementEntity.LayerEffects.Clear();
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
{
|
||||
baseLayerEffect.Save();
|
||||
RenderElementEntity.LayerEffects.Add(baseLayerEffect.LayerEffectEntity);
|
||||
}
|
||||
|
||||
// Condition
|
||||
DisplayCondition?.Save();
|
||||
RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
||||
|
||||
// Timeline
|
||||
RenderElementEntity.Timeline = Timeline?.Entity;
|
||||
Timeline?.Save();
|
||||
}
|
||||
|
||||
internal void LoadNodeScript()
|
||||
{
|
||||
if (DisplayCondition is INodeScriptCondition scriptCondition)
|
||||
scriptCondition.LoadNodeScript();
|
||||
|
||||
foreach (ILayerProperty layerProperty in GetAllLayerProperties())
|
||||
layerProperty.BaseDataBinding.LoadNodeScript();
|
||||
}
|
||||
|
||||
internal void OnLayerEffectsUpdated()
|
||||
{
|
||||
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#region Timeline
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timeline associated with this render element
|
||||
/// </summary>
|
||||
public Timeline Timeline { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Timeline" /> according to the provided <paramref name="deltaTime" /> and current display condition
|
||||
/// </summary>
|
||||
protected void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
DisplayCondition.UpdateTimeline(deltaTime);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal abstract RenderElementEntity RenderElementEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of this element
|
||||
/// </summary>
|
||||
public new ProfileElement Parent
|
||||
{
|
||||
get => base.Parent!;
|
||||
internal set
|
||||
{
|
||||
base.Parent = value;
|
||||
OnPropertyChanged(nameof(Parent));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
|
||||
/// clipped.
|
||||
/// </summary>
|
||||
public SKPath? Path
|
||||
{
|
||||
get => _path;
|
||||
protected set
|
||||
{
|
||||
SetAndNotify(ref _path, value);
|
||||
// I can't really be sure about the performance impact of calling Bounds often but
|
||||
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
|
||||
Bounds = SKRectI.Round(value?.Bounds ?? SKRect.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of this entity
|
||||
/// </summary>
|
||||
public SKRectI Bounds
|
||||
{
|
||||
get => _bounds;
|
||||
private set => SetAndNotify(ref _bounds, value);
|
||||
}
|
||||
|
||||
|
||||
#region Property group expansion
|
||||
|
||||
internal List<string> ExpandedPropertyGroups;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided property group is expanded
|
||||
/// </summary>
|
||||
/// <param name="layerPropertyGroup">The property group to check</param>
|
||||
/// <returns>A boolean indicating whether the provided property group is expanded</returns>
|
||||
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
|
||||
{
|
||||
return ExpandedPropertyGroups.Contains(layerPropertyGroup.Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands or collapses the provided property group
|
||||
/// </summary>
|
||||
/// <param name="layerPropertyGroup">The group to expand or collapse</param>
|
||||
/// <param name="expanded">Whether to expand or collapse the property group</param>
|
||||
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
|
||||
{
|
||||
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
|
||||
ExpandedPropertyGroups.Remove(layerPropertyGroup.Path);
|
||||
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
|
||||
ExpandedPropertyGroups.Add(layerPropertyGroup.Path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
/// <summary>
|
||||
/// Enables the render element and its brushes and effects
|
||||
/// </summary>
|
||||
public abstract void Disable();
|
||||
|
||||
/// <summary>
|
||||
/// Disables the render element and its brushes and effects
|
||||
/// </summary>
|
||||
public abstract void Enable();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Effect management
|
||||
|
||||
internal readonly List<BaseLayerEffect> LayerEffectsList;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of the layer effects on this entity
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<BaseLayerEffect> LayerEffects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a the layer effect described inthe provided <paramref name="descriptor" />
|
||||
/// </summary>
|
||||
public void AddLayerEffect(LayerEffectDescriptor descriptor)
|
||||
{
|
||||
if (descriptor == null)
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
|
||||
LayerEffectEntity entity = new()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Suspended = false,
|
||||
Order = LayerEffects.Count + 1
|
||||
};
|
||||
descriptor.CreateInstance(this, entity);
|
||||
|
||||
OrderEffects();
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided layer
|
||||
/// </summary>
|
||||
/// <param name="effect"></param>
|
||||
public void RemoveLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
|
||||
// Remove the effect from the layer and dispose it
|
||||
LayerEffectsList.Remove(effect);
|
||||
effect.Dispose();
|
||||
|
||||
// Update the order on the remaining effects
|
||||
OrderEffects();
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
private void OrderEffects()
|
||||
{
|
||||
int index = 0;
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
|
||||
{
|
||||
baseLayerEffect.Order = Order = index + 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
LayerEffectsList.Sort((a, b) => a.Order.CompareTo(b.Order));
|
||||
}
|
||||
|
||||
internal void ActivateEffects()
|
||||
{
|
||||
foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects)
|
||||
{
|
||||
// If there is a non-placeholder existing effect, skip this entity
|
||||
BaseLayerEffect? existing = LayerEffectsList.FirstOrDefault(e => e.LayerEffectEntity.Id == layerEffectEntity.Id);
|
||||
if (existing != null && existing.Descriptor.PlaceholderFor == null)
|
||||
continue;
|
||||
|
||||
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
|
||||
if (existing != null)
|
||||
{
|
||||
LayerEffectsList.Remove(existing);
|
||||
existing.Dispose();
|
||||
}
|
||||
|
||||
// Create an instance with the descriptor
|
||||
descriptor.CreateInstance(this, layerEffectEntity);
|
||||
}
|
||||
else if (existing == null)
|
||||
{
|
||||
// If no descriptor was found and there was no existing placeholder, create a placeholder
|
||||
descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId);
|
||||
descriptor.CreateInstance(this, layerEffectEntity);
|
||||
}
|
||||
}
|
||||
|
||||
OrderEffects();
|
||||
}
|
||||
|
||||
|
||||
internal void ActivateLayerEffect(BaseLayerEffect layerEffect)
|
||||
{
|
||||
LayerEffectsList.Add(layerEffect);
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e)
|
||||
{
|
||||
// If effects provided by the plugin are on the element, replace them with placeholders
|
||||
List<BaseLayerEffect> pluginEffects = LayerEffectsList.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
|
||||
foreach (BaseLayerEffect pluginEffect in pluginEffects)
|
||||
{
|
||||
LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.LayerEffectEntity.Id);
|
||||
LayerEffectsList.Remove(pluginEffect);
|
||||
pluginEffect.Dispose();
|
||||
|
||||
LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.ProviderId);
|
||||
descriptor.CreateInstance(this, entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e)
|
||||
{
|
||||
if (RenderElementEntity.LayerEffects.Any(ef => ef.ProviderId == e.Registration.PluginFeature.Id))
|
||||
ActivateEffects();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conditions
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the display conditions applied to this layer where met or not during last update
|
||||
/// <para>Always true if the layer has no display conditions</para>
|
||||
/// </summary>
|
||||
public bool DisplayConditionMet
|
||||
{
|
||||
get => _displayConditionMet;
|
||||
protected set => SetAndNotify(ref _displayConditionMet, value);
|
||||
}
|
||||
|
||||
private bool _displayConditionMet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display condition used to determine whether this element is active or not
|
||||
/// </summary>
|
||||
public ICondition DisplayCondition
|
||||
{
|
||||
get => _displayCondition;
|
||||
set => SetAndNotify(ref _displayCondition, value);
|
||||
}
|
||||
|
||||
private ICondition _displayCondition;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the display conditions on this element and applies any required changes to the <see cref="Timeline" />
|
||||
/// </summary>
|
||||
public void UpdateDisplayCondition()
|
||||
{
|
||||
if (Suspended)
|
||||
{
|
||||
DisplayConditionMet = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayCondition.Update();
|
||||
DisplayConditionMet = DisplayCondition.IsMet;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the main timeline to the specified time and clears any extra time lines
|
||||
/// </summary>
|
||||
/// <param name="position">The position to set the timeline to</param>
|
||||
public abstract void OverrideTimelineAndApply(TimeSpan position);
|
||||
_layerEffects = new List<BaseLayerEffect>();
|
||||
_displayCondition = new AlwaysOnCondition(this);
|
||||
Timeline = new Timeline();
|
||||
LayerEffects = new ReadOnlyCollection<BaseLayerEffect>(_layerEffects);
|
||||
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
|
||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this render element and its layers/brushes are enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this render element and its layers/brushes should be enabled
|
||||
/// </summary>
|
||||
public abstract bool ShouldBeEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list of all layer properties present on this render element
|
||||
/// </summary>
|
||||
/// <returns>A list of all layer properties present on this render element</returns>
|
||||
public abstract List<ILayerProperty> GetAllLayerProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a layer effect has been added or removed to this render element
|
||||
/// </summary>
|
||||
public event EventHandler? LayerEffectsUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the main timeline to the specified time and clears any extra time lines
|
||||
/// </summary>
|
||||
/// <param name="position">The position to set the timeline to</param>
|
||||
public abstract void OverrideTimelineAndApply(TimeSpan position);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.Dispose();
|
||||
|
||||
if (DisplayCondition is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
internal void LoadRenderElement()
|
||||
{
|
||||
Timeline = RenderElementEntity.Timeline != null
|
||||
? new Timeline(RenderElementEntity.Timeline)
|
||||
: new Timeline();
|
||||
|
||||
DisplayCondition = RenderElementEntity.DisplayCondition switch
|
||||
{
|
||||
AlwaysOnConditionEntity entity => new AlwaysOnCondition(entity, this),
|
||||
PlayOnceConditionEntity entity => new PlayOnceCondition(entity, this),
|
||||
StaticConditionEntity entity => new StaticCondition(entity, this),
|
||||
EventConditionEntity entity => new EventCondition(entity, this),
|
||||
_ => DisplayCondition
|
||||
};
|
||||
|
||||
LoadLayerEffects();
|
||||
}
|
||||
|
||||
internal void SaveRenderElement()
|
||||
{
|
||||
RenderElementEntity.LayerEffects.Clear();
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
{
|
||||
baseLayerEffect.Save();
|
||||
RenderElementEntity.LayerEffects.Add(baseLayerEffect.LayerEffectEntity);
|
||||
}
|
||||
|
||||
// Condition
|
||||
DisplayCondition?.Save();
|
||||
RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
||||
|
||||
// Timeline
|
||||
RenderElementEntity.Timeline = Timeline?.Entity;
|
||||
Timeline?.Save();
|
||||
}
|
||||
|
||||
internal void LoadNodeScript()
|
||||
{
|
||||
if (DisplayCondition is INodeScriptCondition scriptCondition)
|
||||
scriptCondition.LoadNodeScript();
|
||||
|
||||
foreach (ILayerProperty layerProperty in GetAllLayerProperties())
|
||||
layerProperty.BaseDataBinding.LoadNodeScript();
|
||||
}
|
||||
|
||||
private void OnLayerEffectsUpdated()
|
||||
{
|
||||
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#region Timeline
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timeline associated with this render element
|
||||
/// </summary>
|
||||
public Timeline Timeline { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Timeline" /> according to the provided <paramref name="deltaTime" /> and current display
|
||||
/// condition
|
||||
/// </summary>
|
||||
protected void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
DisplayCondition.UpdateTimeline(deltaTime);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal abstract RenderElementEntity RenderElementEntity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of this element
|
||||
/// </summary>
|
||||
public new ProfileElement Parent
|
||||
{
|
||||
get => base.Parent!;
|
||||
internal set
|
||||
{
|
||||
base.Parent = value;
|
||||
OnPropertyChanged(nameof(Parent));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
|
||||
/// clipped.
|
||||
/// </summary>
|
||||
public SKPath? Path
|
||||
{
|
||||
get => _path;
|
||||
protected set
|
||||
{
|
||||
SetAndNotify(ref _path, value);
|
||||
// I can't really be sure about the performance impact of calling Bounds often but
|
||||
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
|
||||
Bounds = SKRectI.Round(value?.Bounds ?? SKRect.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of this entity
|
||||
/// </summary>
|
||||
public SKRectI Bounds
|
||||
{
|
||||
get => _bounds;
|
||||
private set => SetAndNotify(ref _bounds, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
/// <summary>
|
||||
/// Enables the render element and its brushes and effects
|
||||
/// </summary>
|
||||
public abstract void Disable();
|
||||
|
||||
/// <summary>
|
||||
/// Disables the render element and its brushes and effects
|
||||
/// </summary>
|
||||
public abstract void Enable();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Effect management
|
||||
|
||||
private readonly List<BaseLayerEffect> _layerEffects;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of the layer effects on this entity
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<BaseLayerEffect> LayerEffects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a the provided layer effect to the render profile element
|
||||
/// </summary>
|
||||
/// <param name="layerEffect">The effect to add.</param>
|
||||
public void AddLayerEffect(BaseLayerEffect layerEffect)
|
||||
{
|
||||
if (layerEffect == null)
|
||||
throw new ArgumentNullException(nameof(layerEffect));
|
||||
|
||||
// Ensure something needs to be done
|
||||
if (_layerEffects.Contains(layerEffect))
|
||||
return;
|
||||
|
||||
// Make sure the layer effect is tied to this element
|
||||
layerEffect.ProfileElement = this;
|
||||
_layerEffects.Add(layerEffect);
|
||||
|
||||
// Update the order on the effects
|
||||
OrderEffects();
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided layer effect.
|
||||
/// </summary>
|
||||
/// <param name="layerEffect">The effect to remove.</param>
|
||||
public void RemoveLayerEffect(BaseLayerEffect layerEffect)
|
||||
{
|
||||
if (layerEffect == null)
|
||||
throw new ArgumentNullException(nameof(layerEffect));
|
||||
|
||||
// Ensure something needs to be done
|
||||
if (!_layerEffects.Contains(layerEffect))
|
||||
return;
|
||||
|
||||
// Remove the effect from the layer
|
||||
_layerEffects.Remove(layerEffect);
|
||||
|
||||
// Update the order on the remaining effects
|
||||
OrderEffects();
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
private void LoadLayerEffects()
|
||||
{
|
||||
foreach (BaseLayerEffect baseLayerEffect in _layerEffects)
|
||||
baseLayerEffect.Dispose();
|
||||
_layerEffects.Clear();
|
||||
|
||||
foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects.OrderBy(e => e.Order))
|
||||
LoadLayerEffect(layerEffectEntity);
|
||||
}
|
||||
|
||||
private void LoadLayerEffect(LayerEffectEntity layerEffectEntity)
|
||||
{
|
||||
LayerEffectDescriptor? descriptor = LayerEffectStore.Get(layerEffectEntity.ProviderId, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
|
||||
BaseLayerEffect layerEffect;
|
||||
// Create an instance with the descriptor
|
||||
if (descriptor != null)
|
||||
{
|
||||
layerEffect = descriptor.CreateInstance(this, layerEffectEntity);
|
||||
}
|
||||
// If no descriptor was found and there was no existing placeholder, create a placeholder
|
||||
else
|
||||
{
|
||||
descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId);
|
||||
layerEffect = descriptor.CreateInstance(this, layerEffectEntity);
|
||||
}
|
||||
|
||||
_layerEffects.Add(layerEffect);
|
||||
}
|
||||
|
||||
private void ReplaceLayerEffectWithPlaceholder(BaseLayerEffect layerEffect)
|
||||
{
|
||||
int index = _layerEffects.IndexOf(layerEffect);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffect.ProviderId);
|
||||
BaseLayerEffect placeholder = descriptor.CreateInstance(this, layerEffect.LayerEffectEntity);
|
||||
_layerEffects[index] = placeholder;
|
||||
layerEffect.Dispose();
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
private void ReplacePlaceholderWithLayerEffect(PlaceholderLayerEffect placeholder)
|
||||
{
|
||||
int index = _layerEffects.IndexOf(placeholder);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
LayerEffectDescriptor? descriptor = LayerEffectStore.Get(placeholder.OriginalEntity.ProviderId, placeholder.PlaceholderFor)?.LayerEffectDescriptor;
|
||||
if (descriptor == null)
|
||||
throw new ArtemisCoreException("Can't replace a placeholder effect because the real effect isn't available.");
|
||||
|
||||
BaseLayerEffect layerEffect = descriptor.CreateInstance(this, placeholder.OriginalEntity);
|
||||
_layerEffects[index] = layerEffect;
|
||||
placeholder.Dispose();
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
private void OrderEffects()
|
||||
{
|
||||
int index = 0;
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
|
||||
{
|
||||
baseLayerEffect.Order = Order = index + 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
_layerEffects.Sort((a, b) => a.Order.CompareTo(b.Order));
|
||||
}
|
||||
|
||||
private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e)
|
||||
{
|
||||
// Find effects that just got disabled and replace them with placeholders
|
||||
List<BaseLayerEffect> affectedLayerEffects = _layerEffects.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
|
||||
|
||||
if (!affectedLayerEffects.Any())
|
||||
return;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in affectedLayerEffects)
|
||||
ReplaceLayerEffectWithPlaceholder(baseLayerEffect);
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e)
|
||||
{
|
||||
// Find placeholders that just got enabled and replace them with real effects
|
||||
List<PlaceholderLayerEffect> affectedPlaceholders = LayerEffects
|
||||
.Where(l => l is PlaceholderLayerEffect ph && ph.OriginalEntity.ProviderId == e.Registration.PluginFeature.Id)
|
||||
.Cast<PlaceholderLayerEffect>()
|
||||
.ToList();
|
||||
|
||||
if (!affectedPlaceholders.Any())
|
||||
return;
|
||||
|
||||
foreach (PlaceholderLayerEffect placeholderLayerEffect in affectedPlaceholders)
|
||||
ReplacePlaceholderWithLayerEffect(placeholderLayerEffect);
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conditions
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the display conditions applied to this layer where met or not during last update
|
||||
/// <para>Always true if the layer has no display conditions</para>
|
||||
/// </summary>
|
||||
public bool DisplayConditionMet
|
||||
{
|
||||
get => _displayConditionMet;
|
||||
private set => SetAndNotify(ref _displayConditionMet, value);
|
||||
}
|
||||
|
||||
private bool _displayConditionMet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display condition used to determine whether this element is active or not
|
||||
/// </summary>
|
||||
public ICondition DisplayCondition
|
||||
{
|
||||
get => _displayCondition;
|
||||
set => SetAndNotify(ref _displayCondition, value);
|
||||
}
|
||||
|
||||
private ICondition _displayCondition;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the display conditions on this element and applies any required changes to the <see cref="Timeline" />
|
||||
/// </summary>
|
||||
public void UpdateDisplayCondition()
|
||||
{
|
||||
if (Suspended)
|
||||
{
|
||||
DisplayConditionMet = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayCondition.Update();
|
||||
DisplayConditionMet = DisplayCondition.IsMet;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -451,7 +451,7 @@ namespace Artemis.Core
|
||||
});
|
||||
|
||||
DeviceEntity.InputMappings.Clear();
|
||||
foreach (var (original, mapped) in InputMappings)
|
||||
foreach ((ArtemisLed? original, ArtemisLed? mapped) in InputMappings)
|
||||
DeviceEntity.InputMappings.Add(new InputMappingEntity {OriginalLedId = (int) original.RgbLed.Id, MappedLedId = (int) mapped.RgbLed.Id});
|
||||
|
||||
DeviceEntity.Categories.Clear();
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerBrushes
|
||||
/// <summary>
|
||||
/// For internal use only, please use <see cref="LayerBrush{T}" /> or <see cref="PerLedLayerBrush{T}" /> or instead
|
||||
/// </summary>
|
||||
public abstract class PropertiesLayerBrush<T> : BaseLayerBrush where T : LayerPropertyGroup
|
||||
public abstract class PropertiesLayerBrush<T> : BaseLayerBrush where T : LayerPropertyGroup, new()
|
||||
{
|
||||
private T _properties = null!;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Artemis.Core.LayerBrushes
|
||||
|
||||
internal void InitializeProperties(PropertyGroupEntity? propertyGroupEntity)
|
||||
{
|
||||
Properties = Activator.CreateInstance<T>();
|
||||
Properties = new T();
|
||||
PropertyGroupDescriptionAttribute groupDescription = new() {Identifier = "Brush", Name = Descriptor.DisplayName, Description = Descriptor.Description};
|
||||
Properties.Initialize(Layer, null, groupDescription, propertyGroupEntity);
|
||||
PropertiesInitialized = true;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerBrushes
|
||||
/// Represents a brush that renders on a layer
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of brush properties</typeparam>
|
||||
public abstract class LayerBrush<T> : PropertiesLayerBrush<T> where T : LayerPropertyGroup
|
||||
public abstract class LayerBrush<T> : PropertiesLayerBrush<T> where T : LayerPropertyGroup, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="LayerBrush{T}" /> class
|
||||
|
||||
@@ -69,8 +69,6 @@ namespace Artemis.Core.LayerBrushes
|
||||
brush.LayerBrushEntity = entity ?? new LayerBrushEntity { ProviderId = Provider.Id, BrushType = LayerBrushType.FullName };
|
||||
|
||||
brush.Initialize();
|
||||
brush.Update(0);
|
||||
|
||||
return brush;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerBrushes
|
||||
/// Represents a brush that renders on a per-layer basis
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of brush properties</typeparam>
|
||||
public abstract class PerLedLayerBrush<T> : PropertiesLayerBrush<T> where T : LayerPropertyGroup
|
||||
public abstract class PerLedLayerBrush<T> : PropertiesLayerBrush<T> where T : LayerPropertyGroup, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="PerLedLayerBrush{T}" /> class
|
||||
|
||||
@@ -7,12 +7,11 @@ namespace Artemis.Core.LayerEffects
|
||||
/// <summary>
|
||||
/// For internal use only, please use <see cref="LayerEffect{T}" /> instead
|
||||
/// </summary>
|
||||
public abstract class BaseLayerEffect : BreakableModel, IDisposable
|
||||
public abstract class BaseLayerEffect : BreakableModel, IDisposable, IStorageModel
|
||||
{
|
||||
private ILayerEffectConfigurationDialog? _configurationDialog;
|
||||
private LayerEffectDescriptor _descriptor;
|
||||
private bool _suspended;
|
||||
private Guid _entityId;
|
||||
private bool _hasBeenRenamed;
|
||||
private string _name;
|
||||
private int _order;
|
||||
@@ -51,15 +50,6 @@ namespace Artemis.Core.LayerEffects
|
||||
set => SetAndNotify(ref _name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the suspended state, if suspended the effect is skipped in render and update
|
||||
/// </summary>
|
||||
public bool Suspended
|
||||
{
|
||||
get => _suspended;
|
||||
set => SetAndNotify(ref _suspended, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the effect has been renamed by the user, if true consider refraining from changing the name
|
||||
/// programatically
|
||||
@@ -105,13 +95,18 @@ namespace Artemis.Core.LayerEffects
|
||||
/// <summary>
|
||||
/// Gets a reference to the layer property group without knowing it's type
|
||||
/// </summary>
|
||||
public virtual LayerPropertyGroup? BaseProperties => null;
|
||||
public virtual LayerEffectPropertyGroup? BaseProperties => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the layer effect is enabled or not
|
||||
/// </summary>
|
||||
public bool Enabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the layer effect is suspended or not
|
||||
/// </summary>
|
||||
public bool Suspended => BaseProperties is not {PropertiesInitialized: true} || !BaseProperties.IsEnabled;
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
@@ -224,11 +219,20 @@ namespace Artemis.Core.LayerEffects
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
Name = LayerEffectEntity.Name;
|
||||
HasBeenRenamed = LayerEffectEntity.HasBeenRenamed;
|
||||
Order = LayerEffectEntity.Order;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
// No need to update the ID, type and provider ID. They're set once by the LayerBrushDescriptors CreateInstance and can't change
|
||||
LayerEffectEntity.ProviderId = Descriptor.Provider.Id;
|
||||
LayerEffectEntity.EffectType = GetType().FullName;
|
||||
LayerEffectEntity.Name = Name;
|
||||
LayerEffectEntity.Suspended = Suspended;
|
||||
LayerEffectEntity.HasBeenRenamed = HasBeenRenamed;
|
||||
LayerEffectEntity.Order = Order;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerEffects
|
||||
/// Represents an effect that applies preprocessing and/or postprocessing to a layer
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class LayerEffect<T> : BaseLayerEffect where T : LayerPropertyGroup
|
||||
public abstract class LayerEffect<T> : BaseLayerEffect where T : LayerEffectPropertyGroup, new()
|
||||
{
|
||||
private T _properties = null!;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Artemis.Core.LayerEffects
|
||||
public bool PropertiesInitialized { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override LayerPropertyGroup BaseProperties => Properties;
|
||||
public override LayerEffectPropertyGroup BaseProperties => Properties;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properties of this effect.
|
||||
@@ -32,19 +32,22 @@ namespace Artemis.Core.LayerEffects
|
||||
}
|
||||
internal set => _properties = value;
|
||||
}
|
||||
|
||||
internal void InitializeProperties()
|
||||
{
|
||||
Properties = Activator.CreateInstance<T>();
|
||||
Properties.Initialize(ProfileElement, null, new PropertyGroupDescriptionAttribute(){Identifier = "LayerEffect"}, LayerEffectEntity.PropertyGroup);
|
||||
PropertiesInitialized = true;
|
||||
|
||||
EnableLayerEffect();
|
||||
}
|
||||
|
||||
|
||||
internal override void Initialize()
|
||||
{
|
||||
InitializeProperties();
|
||||
}
|
||||
|
||||
private void InitializeProperties()
|
||||
{
|
||||
Properties = new T();
|
||||
Properties.Initialize(ProfileElement, null, new PropertyGroupDescriptionAttribute {Identifier = "LayerEffect"}, LayerEffectEntity.PropertyGroup);
|
||||
|
||||
// Initialize will call PopulateDefaults but that is for plugin developers so can't rely on that to default IsEnabled to true
|
||||
Properties.InitializeIsEnabled();
|
||||
PropertiesInitialized = true;
|
||||
|
||||
EnableLayerEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +1,111 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core.LayerEffects.Placeholder;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Ninject;
|
||||
|
||||
namespace Artemis.Core.LayerEffects
|
||||
namespace Artemis.Core.LayerEffects;
|
||||
|
||||
/// <summary>
|
||||
/// A class that describes a layer effect
|
||||
/// </summary>
|
||||
public class LayerEffectDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that describes a layer effect
|
||||
/// </summary>
|
||||
public class LayerEffectDescriptor
|
||||
internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider provider)
|
||||
{
|
||||
internal LayerEffectDescriptor(string displayName, string description, string icon, Type? layerEffectType, LayerEffectProvider provider)
|
||||
DisplayName = displayName;
|
||||
Description = description;
|
||||
Icon = icon;
|
||||
LayerEffectType = layerEffectType ?? throw new ArgumentNullException(nameof(layerEffectType));
|
||||
Provider = provider ?? throw new ArgumentNullException(nameof(provider));
|
||||
}
|
||||
|
||||
internal LayerEffectDescriptor(string placeholderFor, LayerEffectProvider provider)
|
||||
{
|
||||
PlaceholderFor = placeholderFor ?? throw new ArgumentNullException(nameof(placeholderFor));
|
||||
Provider = provider ?? throw new ArgumentNullException(nameof(provider));
|
||||
DisplayName = "Missing effect";
|
||||
Description = "This effect could not be loaded";
|
||||
Icon = "FileQuestion";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name that is displayed in the UI
|
||||
/// </summary>
|
||||
public string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The description that is displayed in the UI
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Material icon to display in the UI, a full reference can be found
|
||||
/// <see href="https://materialdesignicons.com">here</see>
|
||||
/// </summary>
|
||||
public string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the layer effect
|
||||
/// </summary>
|
||||
public Type? LayerEffectType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
|
||||
/// </summary>
|
||||
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 string? PlaceholderFor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the described effect and applies it to the render element
|
||||
/// </summary>
|
||||
public BaseLayerEffect CreateInstance(RenderProfileElement renderElement, LayerEffectEntity? entity)
|
||||
{
|
||||
if (PlaceholderFor != null)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
Description = description;
|
||||
Icon = icon;
|
||||
LayerEffectType = layerEffectType;
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name that is displayed in the UI
|
||||
/// </summary>
|
||||
public string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The description that is displayed in the UI
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Material icon to display in the UI, a full reference can be found
|
||||
/// <see href="https://materialdesignicons.com">here</see>
|
||||
/// </summary>
|
||||
public string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the layer effect
|
||||
/// </summary>
|
||||
public Type? LayerEffectType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
|
||||
/// </summary>
|
||||
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 string? PlaceholderFor { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the described effect and applies it to the render element
|
||||
/// </summary>
|
||||
internal void CreateInstance(RenderProfileElement renderElement, LayerEffectEntity? entity)
|
||||
{
|
||||
if (LayerEffectType == null)
|
||||
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType");
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
entity = new LayerEffectEntity
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Suspended = false,
|
||||
Order = renderElement.LayerEffects.Count + 1,
|
||||
ProviderId = Provider.Id,
|
||||
EffectType = LayerEffectType.FullName
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip effects already on the element
|
||||
if (renderElement.LayerEffects.Any(e => e.LayerEffectEntity.Id == entity.Id))
|
||||
return;
|
||||
}
|
||||
throw new ArtemisCoreException("Cannot create a placeholder for a layer effect that wasn't loaded from an entity");
|
||||
|
||||
if (PlaceholderFor != null)
|
||||
{
|
||||
CreatePlaceHolderInstance(renderElement, entity);
|
||||
return;
|
||||
}
|
||||
|
||||
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
|
||||
effect.ProfileElement = renderElement;
|
||||
effect.LayerEffectEntity = entity;
|
||||
effect.Order = entity.Order;
|
||||
effect.Name = entity.Name;
|
||||
effect.Suspended = entity.Suspended;
|
||||
effect.Descriptor = this;
|
||||
|
||||
effect.Initialize();
|
||||
effect.Update(0);
|
||||
|
||||
renderElement.ActivateLayerEffect(effect);
|
||||
return CreatePlaceHolderInstance(renderElement, entity);
|
||||
}
|
||||
|
||||
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
|
||||
if (LayerEffectType == null)
|
||||
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType");
|
||||
|
||||
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
|
||||
effect.ProfileElement = renderElement;
|
||||
effect.Descriptor = this;
|
||||
if (entity != null)
|
||||
{
|
||||
if (PlaceholderFor == null)
|
||||
throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything");
|
||||
PlaceholderLayerEffect effect = new(entity, PlaceholderFor)
|
||||
{
|
||||
ProfileElement = renderElement,
|
||||
Descriptor = this
|
||||
};
|
||||
effect.LayerEffectEntity = entity;
|
||||
effect.Load();
|
||||
effect.Initialize();
|
||||
renderElement.ActivateLayerEffect(effect);
|
||||
|
||||
if (renderElement.ShouldBeEnabled)
|
||||
effect.InternalEnable();
|
||||
}
|
||||
else
|
||||
{
|
||||
effect.LayerEffectEntity = new LayerEffectEntity();
|
||||
effect.Initialize();
|
||||
effect.Save();
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
private BaseLayerEffect CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
|
||||
{
|
||||
if (PlaceholderFor == null)
|
||||
throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything");
|
||||
|
||||
PlaceholderLayerEffect effect = new(entity, PlaceholderFor)
|
||||
{
|
||||
ProfileElement = renderElement,
|
||||
Descriptor = this
|
||||
};
|
||||
effect.Initialize();
|
||||
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ namespace Artemis.Core.LayerEffects.Placeholder
|
||||
LayerEffectEntity = originalEntity;
|
||||
Order = OriginalEntity.Order;
|
||||
Name = OriginalEntity.Name;
|
||||
Suspended = OriginalEntity.Suspended;
|
||||
HasBeenRenamed = OriginalEntity.HasBeenRenamed;
|
||||
}
|
||||
|
||||
@@ -58,7 +57,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
|
||||
/// <summary>
|
||||
/// This is in place so that the UI has something to show
|
||||
/// </summary>
|
||||
internal class PlaceholderProperties : LayerPropertyGroup
|
||||
internal class PlaceholderProperties : LayerEffectPropertyGroup
|
||||
{
|
||||
protected override void PopulateDefaults()
|
||||
{
|
||||
|
||||
+1
-5
@@ -4,11 +4,7 @@
|
||||
{
|
||||
public static LayerEffectDescriptor Create(string missingProviderId)
|
||||
{
|
||||
LayerEffectDescriptor descriptor = new("Missing effect", "This effect could not be loaded", "FileQuestion", null, Constants.EffectPlaceholderPlugin)
|
||||
{
|
||||
PlaceholderFor = missingProviderId
|
||||
};
|
||||
|
||||
LayerEffectDescriptor descriptor = new(missingProviderId, Constants.EffectPlaceholderPlugin);
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Artemis.Core.Modules
|
||||
/// <summary>
|
||||
/// Allows you to add new data to the Artemis data model
|
||||
/// </summary>
|
||||
public abstract class Module<T> : Module where T : DataModel
|
||||
public abstract class Module<T> : Module where T : DataModel, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// The data model driving this module
|
||||
@@ -79,7 +79,7 @@ namespace Artemis.Core.Modules
|
||||
|
||||
internal override void InternalEnable()
|
||||
{
|
||||
DataModel = Activator.CreateInstance<T>();
|
||||
DataModel = new T();
|
||||
DataModel.Module = this;
|
||||
DataModel.DataModelDescription = GetDataModelDescription();
|
||||
base.InternalEnable();
|
||||
@@ -310,7 +310,7 @@ namespace Artemis.Core.Modules
|
||||
/// <inheritdoc />
|
||||
internal override void InternalEnable()
|
||||
{
|
||||
foreach ((DefaultCategoryName categoryName, var path) in _pendingDefaultProfilePaths)
|
||||
foreach ((DefaultCategoryName categoryName, string? path) in _pendingDefaultProfilePaths)
|
||||
AddDefaultProfile(categoryName, path);
|
||||
_pendingDefaultProfilePaths.Clear();
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public void SaveAllSettings()
|
||||
{
|
||||
foreach (var (_, pluginSetting) in _settingEntities)
|
||||
foreach ((string _, IPluginSetting? pluginSetting) in _settingEntities)
|
||||
pluginSetting.Save();
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
public void ReleaseAll()
|
||||
{
|
||||
foreach (var (device, keys) in _pressedKeys.ToList())
|
||||
foreach ((ArtemisDevice? device, HashSet<KeyboardKey>? keys) in _pressedKeys.ToList())
|
||||
{
|
||||
foreach (KeyboardKey keyboardKey in keys)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Artemis.Core.Services
|
||||
/// <see cref="object" /> or <see langword="null" />.
|
||||
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
|
||||
/// </summary>
|
||||
public class DataModelJsonPluginEndPoint<T> : PluginEndPoint where T : DataModel
|
||||
public class DataModelJsonPluginEndPoint<T> : PluginEndPoint where T : DataModel, new()
|
||||
{
|
||||
private readonly Module<T> _module;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Artemis.Core.Services
|
||||
/// <param name="module">The module whose datamodel to apply the received JSON to</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel;
|
||||
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" />.
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Artemis.Core.Services
|
||||
.WithModule(PluginsModule);
|
||||
|
||||
// Add registered modules
|
||||
foreach (var webModule in _modules)
|
||||
foreach (WebModuleRegistration? webModule in _modules)
|
||||
server = server.WithModule(webModule.CreateInstance());
|
||||
|
||||
server = server
|
||||
@@ -132,7 +132,7 @@ namespace Artemis.Core.Services
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel
|
||||
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new()
|
||||
{
|
||||
if (module == null) throw new ArgumentNullException(nameof(module));
|
||||
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
|
||||
@@ -141,7 +141,7 @@ namespace Artemis.Core.Services
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
private void HandleDataModelRequest<T>(Module<T> module, T value) where T : DataModel
|
||||
private void HandleDataModelRequest<T>(Module<T> module, T value) where T : DataModel, new()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Artemis.Core.Internal
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
foreach (var (property, inputPin) in _propertyPins)
|
||||
foreach ((IDataBindingProperty? property, InputPin? inputPin) in _propertyPins)
|
||||
{
|
||||
if (inputPin.ConnectedTo.Any())
|
||||
_propertyValues[property] = inputPin.Value!;
|
||||
@@ -35,7 +35,7 @@ namespace Artemis.Core.Internal
|
||||
|
||||
public void ApplyToDataBinding()
|
||||
{
|
||||
foreach (var (property, pendingValue) in _propertyValues)
|
||||
foreach ((IDataBindingProperty? property, object? pendingValue) in _propertyValues)
|
||||
property.SetValue(pendingValue);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user