Added generic typing to layer properties and keyframes

Restructured the way property input VMs are created
This commit is contained in:
Robert committed 2020-01-08 19:54:27 +01:00
1 parent e1a4a155b6
commit 139e1879c1
21 files changed
+342 -155

No files matched your search

+4 -2
View File
@@ -149,7 +149,9 @@
<Compile Include="Extensions\TypeExtensions.cs" />
<Compile Include="JsonConverters\SKColorConverter.cs" />
<Compile Include="Models\DataModelDescription.cs" />
<Compile Include="Models\Profile\Keyframe.cs" />
<Compile Include="Models\Profile\LayerProperties\BaseKeyframe.cs" />
<Compile Include="Models\Profile\LayerProperties\Keyframe.cs" />
<Compile Include="Models\Profile\LayerProperties\BaseLayerProperty.cs" />
<Compile Include="Models\Profile\LayerShapes\Ellipse.cs" />
<Compile Include="Models\Profile\LayerShapes\Fill.cs" />
<Compile Include="Models\Profile\LayerShapes\Polygon.cs" />
@@ -161,7 +163,7 @@
<Compile Include="Ninject\LoggerProvider.cs" />
<Compile Include="Ninject\PluginSettingsProvider.cs" />
<Compile Include="Ninject\SettingsServiceProvider.cs" />
<Compile Include="Models\Profile\LayerProperty.cs" />
<Compile Include="Models\Profile\LayerProperties\LayerProperty.cs" />
<Compile Include="Plugins\Abstract\ModuleDataModel.cs" />
<Compile Include="Plugins\Abstract\ModuleViewModel.cs" />
<Compile Include="Plugins\Abstract\ProfileModule.cs" />
@@ -1,13 +0,0 @@
using System;
namespace Artemis.Core.Models.Profile
{
public class Keyframe
{
public Layer Layer { get; set; }
public LayerProperty Property { get; set; }
public TimeSpan Position { get; set; }
public object Value { get; set; }
}
}
+67 -26
View File
@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.Exceptions;
using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Models.Profile.LayerShapes;
using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.LayerBrush;
@@ -14,7 +16,7 @@ namespace Artemis.Core.Models.Profile
{
public sealed class Layer : ProfileElement
{
private readonly List<LayerProperty> _properties;
private readonly Dictionary<string, BaseLayerProperty> _properties;
private LayerShape _layerShape;
private List<ArtemisLed> _leds;
@@ -28,7 +30,7 @@ namespace Artemis.Core.Models.Profile
Name = name;
_leds = new List<ArtemisLed>();
_properties = new List<LayerProperty>();
_properties = new Dictionary<string, BaseLayerProperty>();
CreateDefaultProperties();
}
@@ -44,7 +46,7 @@ namespace Artemis.Core.Models.Profile
Order = layerEntity.Order;
_leds = new List<ArtemisLed>();
_properties = new List<LayerProperty>();
_properties = new Dictionary<string, BaseLayerProperty>();
// TODO: Load properties from entity instead of creating the defaults
CreateDefaultProperties();
@@ -113,32 +115,32 @@ namespace Artemis.Core.Models.Profile
/// <summary>
/// A collection of all the properties on this layer
/// </summary>
public ReadOnlyCollection<LayerProperty> Properties => _properties.AsReadOnly();
public ReadOnlyCollection<BaseLayerProperty> Properties => _properties.Values.ToList().AsReadOnly();
/// <summary>
/// The anchor point property of this layer, also found in <see cref="Properties" />
/// </summary>
public LayerProperty AnchorPointProperty { get; private set; }
public LayerProperty<SKPoint> AnchorPointProperty { get; private set; }
/// <summary>
/// The position of this layer, also found in <see cref="Properties" />
/// </summary>
public LayerProperty PositionProperty { get; private set; }
public LayerProperty<SKPoint> PositionProperty { get; private set; }
/// <summary>
/// The scale property of this layer, also found in <see cref="Properties" />
/// </summary>
public LayerProperty ScaleProperty { get; private set; }
public LayerProperty<SKSize> ScaleProperty { get; private set; }
/// <summary>
/// The rotation property of this layer, also found in <see cref="Properties" />
/// </summary>
public LayerProperty RotationProperty { get; private set; }
public LayerProperty<int> RotationProperty { get; private set; }
/// <summary>
/// The opacity property of this layer, also found in <see cref="Properties" />
/// </summary>
public LayerProperty OpacityProperty { get; private set; }
public LayerProperty<float> OpacityProperty { get; private set; }
/// <summary>
/// The brush that will fill the <see cref="LayerShape" />.
@@ -262,6 +264,62 @@ namespace Artemis.Core.Models.Profile
CalculateRenderProperties();
}
#region Properties
public void AddLayerProperty<T>(LayerProperty<T> layerProperty)
{
if (_properties.ContainsKey(layerProperty.Id))
throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}.");
_properties.Add(layerProperty.Id, layerProperty);
}
public void AddLayerProperty(BaseLayerProperty layerProperty)
{
if (_properties.ContainsKey(layerProperty.Id))
throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}.");
_properties.Add(layerProperty.Id, layerProperty);
}
/// <summary>
/// If found, returns the <see cref="LayerProperty{T}"/> matching the provided ID
/// </summary>
/// <typeparam name="T">The type of the layer property</typeparam>
/// <param name="id"></param>
/// <returns></returns>
public LayerProperty<T> GetLayerPropertyById<T>(string id)
{
if (!_properties.ContainsKey(id))
return null;
var property = _properties[id];
if (property.Type != typeof(T))
throw new ArtemisCoreException($"Property type mismatch. Expected property {property} to have type {typeof(T)} but it has {property.Type} instead.");
return (LayerProperty<T>) _properties[id];
}
private void CreateDefaultProperties()
{
var transformProperty = new LayerProperty<object>(this, null, "Core.Transform", "Transform", "The default properties collection every layer has, allows you to transform the shape.");
AnchorPointProperty = new LayerProperty<SKPoint>(this, transformProperty, "Core.AnchorPoint", "Anchor Point", "The point at which the shape is attached to its position.");
PositionProperty = new LayerProperty<SKPoint>(this, transformProperty, "Core.Position", "Position", "The position of the shape.");
ScaleProperty = new LayerProperty<SKSize>(this, transformProperty, "Core.Scale", "Scale", "The scale of the shape.") {InputAffix = "%"};
RotationProperty = new LayerProperty<int>(this, transformProperty, "Core.Rotation", "Rotation", "The rotation of the shape in degrees.") {InputAffix = "°"};
OpacityProperty = new LayerProperty<float>(this, transformProperty, "Core.Opacity", "Opacity", "The opacity of the shape.") {InputAffix = "%"};
transformProperty.Children.Add(AnchorPointProperty);
transformProperty.Children.Add(PositionProperty);
transformProperty.Children.Add(ScaleProperty);
transformProperty.Children.Add(RotationProperty);
transformProperty.Children.Add(OpacityProperty);
AddLayerProperty(transformProperty);
foreach (var transformPropertyChild in transformProperty.Children)
AddLayerProperty(transformPropertyChild);
}
#endregion
internal void CalculateRenderProperties()
{
if (!Leds.Any())
@@ -291,23 +349,6 @@ namespace Artemis.Core.Models.Profile
OnRenderPropertiesUpdated();
}
private void CreateDefaultProperties()
{
var transformProperty = new LayerProperty(this, null, "Transform", "The default properties collection every layer has, allows you to transform the shape.", null);
AnchorPointProperty = new LayerProperty(this, transformProperty, "Anchor Point", "The point at which the shape is attached to its position.", typeof(SKPoint));
PositionProperty = new LayerProperty(this, transformProperty, "Position", "The position of the shape.", typeof(SKPoint));
ScaleProperty = new LayerProperty(this, transformProperty, "Scale", "The scale of the shape.", typeof(SKSize)) { InputAffix = "%" };
RotationProperty = new LayerProperty(this, transformProperty, "Rotation", "The rotation of the shape in degrees.", typeof(int)) {InputAffix = "°" };
OpacityProperty = new LayerProperty(this, transformProperty, "Opacity", "The opacity of the shape.", typeof(float)) {InputAffix = "%"};
transformProperty.Children.Add(AnchorPointProperty);
transformProperty.Children.Add(PositionProperty);
transformProperty.Children.Add(ScaleProperty);
transformProperty.Children.Add(RotationProperty);
transformProperty.Children.Add(OpacityProperty);
_properties.Add(transformProperty);
_properties.AddRange(transformProperty.Children);
}
public override string ToString()
{
@@ -0,0 +1,19 @@
using System;
namespace Artemis.Core.Models.Profile.LayerProperties
{
public class BaseKeyframe
{
protected BaseKeyframe(Layer layer, BaseLayerProperty property)
{
Layer = layer;
BaseProperty = property;
}
public Layer Layer { get; set; }
public TimeSpan Position { get; set; }
protected BaseLayerProperty BaseProperty { get; }
protected object BaseValue { get; set; }
}
}
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using Artemis.Core.Exceptions;
namespace Artemis.Core.Models.Profile.LayerProperties
{
public abstract class BaseLayerProperty
{
private object _baseValue;
protected BaseLayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description, Type type)
{
Layer = layer;
Parent = parent;
Id = id;
Name = name;
Description = description;
Type = type;
Children = new List<BaseLayerProperty>();
BaseKeyframes = new List<BaseKeyframe>();
}
/// <summary>
/// The layer this property applies to
/// </summary>
public Layer Layer { get; }
/// <summary>
/// The parent property of this property.
/// </summary>
public BaseLayerProperty Parent { get; }
/// <summary>
/// The child properties of this property.
/// <remarks>If the layer has children it cannot contain a value or keyframes.</remarks>
/// </summary>
public List<BaseLayerProperty> Children { get; set; }
/// <summary>
/// A unique identifier for this property, a layer may not contain two properties with the same ID.
/// </summary>
public string Id { get; set; }
/// <summary>
/// The user-friendly name for this property, shown in the UI.
/// </summary>
public string Name { get; set; }
/// <summary>
/// The user-friendly description for this property, shown in the UI.
/// </summary>
public string Description { get; set; }
/// <summary>
/// An optional input prefix to show before input elements in the UI.
/// </summary>
public string InputPrefix { get; set; }
/// <summary>
/// An optional input affix to show behind input elements in the UI.
/// </summary>
public string InputAffix { get; set; }
/// <summary>
/// The type of value this layer property contains.
/// </summary>
public Type Type { get; set; }
protected List<BaseKeyframe> BaseKeyframes { get; set; }
/// <summary>
/// A list of keyframes defining different values of the property in time, this list contains the untyped <see cref="BaseKeyframe"/>.
/// </summary>
public IReadOnlyCollection<BaseKeyframe> UntypedKeyframes => BaseKeyframes.AsReadOnly();
protected object BaseValue
{
get => _baseValue;
set
{
if (value != null && value.GetType() != Type)
throw new ArtemisCoreException($"Cannot set value of type {value.GetType()} on property {this}, expected type is {Type}.");
_baseValue = value;
}
}
public void ApplyToEntity()
{
// Big o' TODO
}
public override string ToString()
{
return $"{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(Description)}: {Description}";
}
}
}
@@ -0,0 +1,18 @@
namespace Artemis.Core.Models.Profile.LayerProperties
{
/// <inheritdoc />
public class Keyframe<T> : BaseKeyframe
{
public Keyframe(Layer layer, LayerProperty<T> propertyBase) : base(layer, propertyBase)
{
}
public LayerProperty<T> Property => (LayerProperty<T>) BaseProperty;
public T Value
{
get => (T) BaseValue;
set => BaseValue = value;
}
}
}
@@ -0,0 +1,49 @@
using System.Collections.ObjectModel;
using System.Linq;
namespace Artemis.Core.Models.Profile.LayerProperties
{
public class LayerProperty<T> : BaseLayerProperty
{
public LayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description) : base(layer, parent, id, name, description, typeof(T))
{
}
public T Value
{
get => (T) BaseValue;
set => BaseValue = value;
}
/// <summary>
/// A list of keyframes defining different values of the property in time, this list contains the strongly typed <see cref="Keyframe{T}"/>
/// </summary>
public ReadOnlyCollection<Keyframe<T>> Keyframes => BaseKeyframes.Cast<Keyframe<T>>().ToList().AsReadOnly();
/// <summary>
/// Adds a keyframe to the property.
/// </summary>
/// <param name="keyframe">The keyframe to remove</param>
public void AddKeyframe(Keyframe<T> keyframe)
{
BaseKeyframes.Add(keyframe);
}
/// <summary>
/// Removes a keyframe from the property.
/// </summary>
/// <param name="keyframe">The keyframe to remove</param>
public void RemoveKeyframe(Keyframe<T> keyframe)
{
BaseKeyframes.Remove(keyframe);
}
/// <summary>
/// Removes all keyframes from the property.
/// </summary>
public void ClearKeyframes()
{
BaseKeyframes.Clear();
}
}
}
@@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using Artemis.Core.Exceptions;
namespace Artemis.Core.Models.Profile
{
public class LayerProperty
{
private object _baseValue;
internal LayerProperty(Layer layer, LayerProperty parent, string name, string description, Type type)
{
Layer = layer;
Parent = parent;
Name = name;
Description = description;
Type = type;
Children = new List<LayerProperty>();
Keyframes = new List<Keyframe>();
}
public Layer Layer { get; }
public LayerProperty Parent { get; }
public List<LayerProperty> Children { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string InputPrefix { get; set; }
public string InputAffix { get; set; }
public Type Type { get; set; }
public object BaseValue
{
get => _baseValue;
set
{
if (value != null && value.GetType() != Type)
throw new ArtemisCoreException($"Cannot set value of type {value.GetType()} on property {Name}, expected type is {Type}.");
_baseValue = value;
}
}
public List<Keyframe> Keyframes { get; set; }
public void ApplyToEntity()
{
// Big o' TODO
}
}
}