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:
Robert committed 2020-02-12 21:18:00 +01:00
1 parent 4f66d09755
commit 51a21b7a8a
29 files changed
+417 -303

No files matched your search

+2 -2
View File
@@ -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>
+9 -3
View File
@@ -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>>();