mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
UI VM factories - Shortened names for readibility
Layer properties - Ensure only LayerBrushes can add properties and make sure that method instantiates a keyframe engine Layer properties UI - Respond to properties being added/removed
This commit is contained in:
1 parent
4f66d09755
commit
51a21b7a8a
29 files changed
+417
-303
No files matched your search
@@ -428,7 +428,7 @@ namespace Artemis.Core.Models.Profile
|
||||
/// <typeparam name="T">The type of value of the layer property</typeparam>
|
||||
/// <param name="layerProperty">The property to apply to the layer</param>
|
||||
/// <returns>True if an existing value was found and applied, otherwise false.</returns>
|
||||
public bool RegisterLayerProperty<T>(LayerProperty<T> layerProperty)
|
||||
internal bool RegisterLayerProperty<T>(LayerProperty<T> layerProperty)
|
||||
{
|
||||
return RegisterLayerProperty((BaseLayerProperty) layerProperty);
|
||||
}
|
||||
@@ -439,7 +439,7 @@ namespace Artemis.Core.Models.Profile
|
||||
/// </summary>
|
||||
/// <param name="layerProperty">The property to apply to the layer</param>
|
||||
/// <returns>True if an existing value was found and applied, otherwise false.</returns>
|
||||
public bool RegisterLayerProperty(BaseLayerProperty layerProperty)
|
||||
internal bool RegisterLayerProperty(BaseLayerProperty layerProperty)
|
||||
{
|
||||
if (_properties.ContainsKey((layerProperty.PluginInfo.Guid, layerProperty.Id)))
|
||||
throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}.");
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
// This can only be null if accessed internally
|
||||
if (PluginInfo == null)
|
||||
PluginInfo = Constants.CorePluginInfo;
|
||||
|
||||
|
||||
Children = new List<BaseLayerProperty>();
|
||||
BaseKeyframes = new List<BaseKeyframe>();
|
||||
|
||||
@@ -108,9 +108,9 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
public IReadOnlyCollection<BaseKeyframe> UntypedKeyframes => BaseKeyframes.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the keyframe engine instance of this property
|
||||
/// Gets the keyframe engine instance of this property
|
||||
/// </summary>
|
||||
public KeyframeEngine KeyframeEngine { get; set; }
|
||||
public KeyframeEngine KeyframeEngine { get; internal set; }
|
||||
|
||||
protected List<BaseKeyframe> BaseKeyframes { get; set; }
|
||||
|
||||
@@ -129,50 +129,6 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
}
|
||||
}
|
||||
|
||||
internal void ApplyToEntity()
|
||||
{
|
||||
var propertyEntity = Layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == Id);
|
||||
if (propertyEntity == null)
|
||||
{
|
||||
propertyEntity = new PropertyEntity {Id = Id};
|
||||
Layer.LayerEntity.PropertyEntities.Add(propertyEntity);
|
||||
}
|
||||
|
||||
propertyEntity.ValueType = Type.Name;
|
||||
propertyEntity.Value = JsonConvert.SerializeObject(BaseValue);
|
||||
propertyEntity.IsUsingKeyframes = IsUsingKeyframes;
|
||||
|
||||
propertyEntity.KeyframeEntities.Clear();
|
||||
foreach (var baseKeyframe in BaseKeyframes)
|
||||
{
|
||||
propertyEntity.KeyframeEntities.Add(new KeyframeEntity
|
||||
{
|
||||
Position = baseKeyframe.Position,
|
||||
Value = JsonConvert.SerializeObject(baseKeyframe.BaseValue),
|
||||
EasingFunction = (int) baseKeyframe.EasingFunction
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal void ApplyToProperty(PropertyEntity propertyEntity)
|
||||
{
|
||||
BaseValue = DeserializePropertyValue(propertyEntity.Value);
|
||||
IsUsingKeyframes = propertyEntity.IsUsingKeyframes;
|
||||
|
||||
BaseKeyframes.Clear();
|
||||
foreach (var keyframeEntity in propertyEntity.KeyframeEntities.OrderBy(e => e.Position))
|
||||
{
|
||||
// Create a strongly typed keyframe or else it cannot be cast later on
|
||||
var keyframeType = typeof(Keyframe<>);
|
||||
var keyframe = (BaseKeyframe) Activator.CreateInstance(keyframeType.MakeGenericType(Type), Layer, this);
|
||||
keyframe.Position = keyframeEntity.Position;
|
||||
keyframe.BaseValue = DeserializePropertyValue(keyframeEntity.Value);
|
||||
keyframe.EasingFunction = (Easings.Functions) keyframeEntity.EasingFunction;
|
||||
|
||||
BaseKeyframes.Add(keyframe);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new keyframe for this base property without knowing the type
|
||||
/// </summary>
|
||||
@@ -261,9 +217,29 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
SortKeyframes();
|
||||
}
|
||||
|
||||
internal void SortKeyframes()
|
||||
/// <summary>
|
||||
/// Returns the flattened index of this property on the layer
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetFlattenedIndex()
|
||||
{
|
||||
BaseKeyframes = BaseKeyframes.OrderBy(k => k.Position).ToList();
|
||||
if (Parent == null)
|
||||
return Layer.Properties.IndexOf(this);
|
||||
|
||||
// Create a flattened list of all properties in their order as defined by the parent/child hierarchy
|
||||
var properties = new List<BaseLayerProperty>();
|
||||
// Iterate root properties (those with children)
|
||||
foreach (var baseLayerProperty in Layer.Properties)
|
||||
{
|
||||
// First add self, then add all children
|
||||
if (baseLayerProperty.Children.Any())
|
||||
{
|
||||
properties.Add(baseLayerProperty);
|
||||
properties.AddRange(baseLayerProperty.GetAllChildren());
|
||||
}
|
||||
}
|
||||
|
||||
return properties.IndexOf(this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@@ -271,6 +247,65 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
return $"{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(Description)}: {Description}";
|
||||
}
|
||||
|
||||
internal void ApplyToEntity()
|
||||
{
|
||||
var propertyEntity = Layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == Id);
|
||||
if (propertyEntity == null)
|
||||
{
|
||||
propertyEntity = new PropertyEntity {Id = Id};
|
||||
Layer.LayerEntity.PropertyEntities.Add(propertyEntity);
|
||||
}
|
||||
|
||||
propertyEntity.ValueType = Type.Name;
|
||||
propertyEntity.Value = JsonConvert.SerializeObject(BaseValue);
|
||||
propertyEntity.IsUsingKeyframes = IsUsingKeyframes;
|
||||
|
||||
propertyEntity.KeyframeEntities.Clear();
|
||||
foreach (var baseKeyframe in BaseKeyframes)
|
||||
{
|
||||
propertyEntity.KeyframeEntities.Add(new KeyframeEntity
|
||||
{
|
||||
Position = baseKeyframe.Position,
|
||||
Value = JsonConvert.SerializeObject(baseKeyframe.BaseValue),
|
||||
EasingFunction = (int) baseKeyframe.EasingFunction
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal void ApplyToProperty(PropertyEntity propertyEntity)
|
||||
{
|
||||
BaseValue = DeserializePropertyValue(propertyEntity.Value);
|
||||
IsUsingKeyframes = propertyEntity.IsUsingKeyframes;
|
||||
|
||||
BaseKeyframes.Clear();
|
||||
foreach (var keyframeEntity in propertyEntity.KeyframeEntities.OrderBy(e => e.Position))
|
||||
{
|
||||
// Create a strongly typed keyframe or else it cannot be cast later on
|
||||
var keyframeType = typeof(Keyframe<>);
|
||||
var keyframe = (BaseKeyframe) Activator.CreateInstance(keyframeType.MakeGenericType(Type), Layer, this);
|
||||
keyframe.Position = keyframeEntity.Position;
|
||||
keyframe.BaseValue = DeserializePropertyValue(keyframeEntity.Value);
|
||||
keyframe.EasingFunction = (Easings.Functions) keyframeEntity.EasingFunction;
|
||||
|
||||
BaseKeyframes.Add(keyframe);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SortKeyframes()
|
||||
{
|
||||
BaseKeyframes = BaseKeyframes.OrderBy(k => k.Position).ToList();
|
||||
}
|
||||
|
||||
internal IEnumerable<BaseLayerProperty> GetAllChildren()
|
||||
{
|
||||
var children = new List<BaseLayerProperty>();
|
||||
children.AddRange(Children);
|
||||
foreach (var layerPropertyViewModel in Children)
|
||||
children.AddRange(layerPropertyViewModel.GetAllChildren());
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
private object DeserializePropertyValue(string value)
|
||||
{
|
||||
if (value == "null")
|
||||
|
||||
@@ -1,71 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Plugins.LayerBrush;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
|
||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless
|
||||
/// opted out)
|
||||
/// opted out).
|
||||
/// <para>To create and register a new LayerProperty use <see cref="LayerBrush.RegisterLayerProperty" /></para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class LayerProperty<T> : BaseLayerProperty
|
||||
{
|
||||
internal LayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description) : base(layer, null, parent, id, name, description, typeof(T))
|
||||
internal LayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description)
|
||||
: base(layer, null, parent, id, name, description, typeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
internal LayerProperty(Layer layer, string id, string name, string description) : base(layer, null, null, id, name, description, typeof(T))
|
||||
internal LayerProperty(Layer layer, string id, string name, string description)
|
||||
: base(layer, null, null, id, name, description, typeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless
|
||||
/// opted out)
|
||||
/// <para>
|
||||
/// Note: The value and keyframes of the property are stored using the ID, after adding the property to the layer
|
||||
/// these are restored.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer the property is applied to</param>
|
||||
/// <param name="pluginInfo">The plugin to create this property for</param>
|
||||
/// <param name="parent">The parent of this property, use this to create a tree-hierarchy in the editor</param>
|
||||
/// <param name="id">A and ID identifying your property, must be unique within your plugin</param>
|
||||
/// <param name="name">A name for your property, this is visible in the editor</param>
|
||||
/// <param name="description">A description for your property, this is visible in the editor</param>
|
||||
public LayerProperty(Layer layer, PluginInfo pluginInfo, BaseLayerProperty parent, string id, string name, string description) : base(layer, pluginInfo, parent, id, name, description,
|
||||
typeof(T))
|
||||
internal LayerProperty(Layer layer, PluginInfo pluginInfo, BaseLayerProperty parent, string id, string name, string description)
|
||||
: base(layer, pluginInfo, parent, id, name, description, typeof(T))
|
||||
{
|
||||
if (layer == null)
|
||||
throw new ArgumentNullException(nameof(layer));
|
||||
if (pluginInfo == null)
|
||||
throw new ArgumentNullException(nameof(pluginInfo));
|
||||
if (id == null)
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless
|
||||
/// opted out)
|
||||
/// <para>
|
||||
/// Note: The value and keyframes of the property are stored using the ID, after adding the property to the layer
|
||||
/// these are restored.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer the property is applied to</param>
|
||||
/// <param name="pluginInfo">The plugin to create this property for</param>
|
||||
/// <param name="id">A and ID identifying your property, must be unique within your plugin</param>
|
||||
/// <param name="name">A name for your property, this is visible in the editor</param>
|
||||
/// <param name="description">A description for your property, this is visible in the editor</param>
|
||||
public LayerProperty(Layer layer, PluginInfo pluginInfo, string id, string name, string description) : base(layer, pluginInfo, null, id, name, description, typeof(T))
|
||||
{
|
||||
if (layer == null)
|
||||
throw new ArgumentNullException(nameof(layer));
|
||||
if (pluginInfo == null)
|
||||
throw new ArgumentNullException(nameof(pluginInfo));
|
||||
if (id == null)
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using System;
|
||||
using Artemis.Core.Models.Profile;
|
||||
using Artemis.Core.Models.Profile.LayerProperties;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Core.Services.Interfaces;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Plugins.LayerBrush
|
||||
{
|
||||
public abstract class LayerBrush : IDisposable
|
||||
{
|
||||
private ILayerService _layerService;
|
||||
|
||||
protected LayerBrush(Layer layer, LayerBrushDescriptor descriptor)
|
||||
{
|
||||
Layer = layer;
|
||||
@@ -54,6 +58,8 @@ namespace Artemis.Core.Plugins.LayerBrush
|
||||
{
|
||||
var property = new LayerProperty<T>(Layer, Descriptor.LayerBrushProvider.PluginInfo, parent, id, name, description);
|
||||
Layer.RegisterLayerProperty(property);
|
||||
// It's fine if this is null, it'll be picked up by SetLayerService later
|
||||
_layerService?.InstantiateKeyframeEngine(property);
|
||||
return property;
|
||||
}
|
||||
|
||||
@@ -68,9 +74,20 @@ namespace Artemis.Core.Plugins.LayerBrush
|
||||
/// <returns>The layer property</returns>
|
||||
protected LayerProperty<T> RegisterLayerProperty<T>(string id, string name, string description)
|
||||
{
|
||||
var property = new LayerProperty<T>(Layer, Descriptor.LayerBrushProvider.PluginInfo, Layer.BrushReferenceProperty.Parent, id, name, description);
|
||||
var property = new LayerProperty<T>(
|
||||
Layer, Descriptor.LayerBrushProvider.PluginInfo, Layer.BrushReferenceProperty.Parent, id, name, description
|
||||
);
|
||||
Layer.RegisterLayerProperty(property);
|
||||
// It's fine if this is null, it'll be picked up by SetLayerService later
|
||||
_layerService?.InstantiateKeyframeEngine(property);
|
||||
return property;
|
||||
}
|
||||
|
||||
internal void SetLayerService(ILayerService layerService)
|
||||
{
|
||||
_layerService = layerService;
|
||||
foreach (var baseLayerProperty in Layer.Properties)
|
||||
_layerService.InstantiateKeyframeEngine(baseLayerProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,16 @@ namespace Artemis.Core.Services.Interfaces
|
||||
public interface ILayerService : IArtemisService
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates and adds the <see cref="LayerBrush" /> described by the provided
|
||||
/// <see cref="LayerBrushDescriptor" /> to the provided <see cref="Layer" />.
|
||||
/// Instantiates and adds the <see cref="LayerBrush" /> described by the provided <see cref="LayerBrushDescriptor" />
|
||||
/// to the <see cref="Layer" />.
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer to add the new layer element to</param>
|
||||
/// <param name="layer">The layer to instantiate the brush for</param>
|
||||
/// <returns></returns>
|
||||
LayerBrush InstantiateLayerBrush(Layer layer);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates and adds a compatible <see cref="KeyframeEngine" /> to the provided <see cref="LayerProperty{T}" />
|
||||
/// Instantiates and adds a compatible <see cref="KeyframeEngine" /> to the provided <see cref="LayerProperty{T}" />.
|
||||
/// If the property already has a compatible keyframe engine, nothing happens.
|
||||
/// </summary>
|
||||
/// <param name="layerProperty">The layer property to apply the keyframe engine to.</param>
|
||||
/// <returns>The resulting keyframe engine, if a compatible engine was found.</returns>
|
||||
@@ -24,6 +25,7 @@ namespace Artemis.Core.Services.Interfaces
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates and adds a compatible <see cref="KeyframeEngine" /> to the provided <see cref="BaseLayerProperty" />.
|
||||
/// If the property already has a compatible keyframe engine, nothing happens.
|
||||
/// </summary>
|
||||
/// <param name="layerProperty">The layer property to apply the keyframe engine to.</param>
|
||||
/// <returns>The resulting keyframe engine, if a compatible engine was found.</returns>
|
||||
|
||||
@@ -46,10 +46,13 @@ namespace Artemis.Core.Services
|
||||
new ConstructorArgument("layer", layer),
|
||||
new ConstructorArgument("descriptor", descriptor)
|
||||
};
|
||||
var layerElement = (LayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments);
|
||||
layer.LayerBrush = layerElement;
|
||||
var layerBrush = (LayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments);
|
||||
// Set the layer service after the brush was created to avoid constructor clutter, SetLayerService will play catch-up for us.
|
||||
// If layer brush implementations need the LayerService they can inject it themselves, but don't require it by default
|
||||
layerBrush.SetLayerService(this);
|
||||
layer.LayerBrush = layerBrush;
|
||||
|
||||
return layerElement;
|
||||
return layerBrush;
|
||||
}
|
||||
|
||||
public KeyframeEngine InstantiateKeyframeEngine<T>(LayerProperty<T> layerProperty)
|
||||
@@ -59,6 +62,9 @@ namespace Artemis.Core.Services
|
||||
|
||||
public KeyframeEngine InstantiateKeyframeEngine(BaseLayerProperty layerProperty)
|
||||
{
|
||||
if (layerProperty.KeyframeEngine != null && layerProperty.KeyframeEngine.CompatibleTypes.Contains(layerProperty.Type))
|
||||
return layerProperty.KeyframeEngine;
|
||||
|
||||
// This creates an instance of each keyframe engine, which is pretty cheap since all the expensive stuff is done during
|
||||
// Initialize() call but it's not ideal
|
||||
var keyframeEngines = _kernel.Get<List<KeyframeEngine>>();
|
||||
|
||||
Reference in new issue
Block a user